Unverified Commit 1587796b authored by Yang Liu's avatar Yang Liu Committed by GitHub
Browse files

Auth refactor, fix, update (#1)

* . since the current implementation of CryptoKey dealing with V2 and V3 are very confusing, many errors and bugs are caused by it, I did a semi-overhaul of CryptoKey and modified related classes.
. fixed the bug where service ID and private key are read as strings storing decoded bytes
. added v3 support in passing private keys to Tor in UserIdentity and HiddenService. Now Ricochet will show 'online' for both v2 and v3 reliably with the new changes in CryptoKey
. now can add v3 friends without crash (on the requesting side), still needs to test on the receiving side.

* . updated according to comment
parent a307038e
......@@ -84,12 +84,10 @@ void UserIdentity::setupService()
QString legacyDir = m_settings->read("dataDirectory").toString();
if (!keyData.isEmpty()) {
// TODO: add v3 support: by checking keyData length we know it's v3 or v2
// FIXME: need to add loadfromfile() inside loadfromdatav3() or not ?
if (keyData.size() == CryptoKey::V3PrivateKeyLength) {
// for V3 key and serviceID
CryptoKey v3privateKey;
CryptoKey v3serviceId;
CryptoKey v3privateKey(CryptoKey::V3PrivateKey);
CryptoKey v3serviceId(CryptoKey::V3ServiceID);
bool privateKeyLoaded = v3privateKey.loadFromDataV3(
keyData.toLatin1().toStdString(), CryptoKey::V3PrivateKey);
bool serviceIdLoaded = v3serviceId.loadFromDataV3(
......@@ -101,8 +99,9 @@ void UserIdentity::setupService()
m_hiddenService = new Tor::HiddenService(v3privateKey, v3serviceId, legacyDir, this);
} else {
// for V2 keys
CryptoKey key;
if (!key.loadFromData(QByteArray::fromBase64(keyData.toLatin1()), CryptoKey::PrivateKey, CryptoKey::DER)) {
CryptoKey key(CryptoKey::V2PrivateKey);
if (!key.loadFromData(QByteArray::fromBase64(keyData.toLatin1()), CryptoKey::V2PrivateKey,
CryptoKey::DER)) {
qWarning() << "Cannot load v2 service key from configuration";
return;
}
......@@ -111,13 +110,18 @@ void UserIdentity::setupService()
} else if (!legacyDir.isEmpty() && QFile::exists(
legacyDir + QLatin1String("/private_key"))) {
qDebug() << "Attempting to load key from legacy filesystem format in" << legacyDir;
// todo seems to be deprecated
CryptoKey key;
if (!key.loadFromFile(legacyDir + QLatin1String("/private_key"), CryptoKey::PrivateKey)) {
if (!key.loadFromFile(legacyDir + QLatin1String("/private_key"), CryptoKey::V2PrivateKey)) {
qWarning() << "Cannot load legacy format key from" << legacyDir << "for conversion";
return;
} else {
keyData = QString::fromLatin1(key.encodedPrivateKey(CryptoKey::DER).toBase64());
if (key.getVersion() == CryptoKey::V2) {
keyData = QString::fromLatin1(key.encodedPrivateKey(CryptoKey::DER).toBase64());
} else if (key.getVersion() == CryptoKey::V3) {
keyData = QString::fromStdString(key.getV3privateKey());
}
m_settings->write("serviceKey", keyData);
m_hiddenService = new Tor::HiddenService(key, legacyDir, this);
}
......@@ -131,16 +135,18 @@ void UserIdentity::setupService()
[&]() {
if (m_hiddenService->V3serviceId().isLoaded()) {
m_settings->write("V3serviceId", QString::fromStdString(
m_hiddenService->V3serviceId().getV3ServiceId()));
m_hiddenService->V3serviceId().getV3serviceID()));
}
if (m_hiddenService->privateKey().isLoaded()) {
if (m_hiddenService->privateKey().getVersion() == CryptoKey::Version::V2) {
QString key = QString::fromLatin1(m_hiddenService->
if (m_hiddenService->privateKey().getVersion() == CryptoKey::V2) {
QString key;
key = QString::fromLatin1(m_hiddenService->
privateKey().encodedPrivateKey(CryptoKey::DER).toBase64());
m_settings->write("serviceKey", key);
} else if (m_hiddenService->privateKey().getVersion() == CryptoKey::Version::V3) {
m_settings->write("serviceKey", QString::fromLatin1(m_hiddenService->
privateKey().encodedPrivateKey(CryptoKey::V3ENCODED).toBase64()));
QString key;
key = QString::fromStdString(m_hiddenService->privateKey().getV3privateKey());
m_settings->write("serviceKey", key);
}
}
}
......
......@@ -82,6 +82,7 @@ AuthHiddenServiceChannel::AuthHiddenServiceChannel(Direction dir, Connection *co
);
}
//todo change function name after v3 upgrade
void AuthHiddenServiceChannel::setPrivateKey(const CryptoKey &key, const CryptoKey &v3serviceID)
{
Q_D(AuthHiddenServiceChannel);
......@@ -155,7 +156,7 @@ bool AuthHiddenServiceChannel::allowOutboundChannelRequest(Data::Control::OpenCh
// IT IS BAD
// DO NOT USE THIS IN PRODUCTION
// PLEASE
d->privateKey.v3privateKey = d->privateKey.v3serviceID;
// d->privateKey.v3privateKey = d->privateKey.v3serviceID;
if (!d->privateKey.isLoaded()) {
BUG() << "AuthHiddenServiceChannel can't be opened without a private key";
return false;
......@@ -340,11 +341,11 @@ void AuthHiddenServiceChannel::handleProof(const Data::AuthHiddenService::Proof
bool decoded = false; // public key decoding status
if (signature.size() == 128) {
// v2
decoded= publicKey.loadFromData(publicKeyData, CryptoKey::PublicKey, CryptoKey::DER);
decoded = publicKey.loadFromData(publicKeyData, CryptoKey::V2PublicKey, CryptoKey::DER);
}
else if (signature.size() == 64) {
// v3
decoded = publicKey.loadFromDataV3(publicKeyData.constData(), CryptoKey::PublicKey);
decoded = publicKey.loadFromDataV3(publicKeyData.constData(), CryptoKey::V2PublicKey);
}
else {
// Invalid
......
......@@ -53,9 +53,13 @@ QByteArray AddOnionCommand::build()
QByteArray out("ADD_ONION");
if (m_service->privateKey().isLoaded()) {
// out += " RSA1024:";
out += " ED25519-V3:";
out += m_service->privateKey().encodedPrivateKey(CryptoKey::DER).toBase64();
if (m_service->privateKey().getVersion() == CryptoKey::V3) {
out += " ED25519-V3:";
out += QByteArray::fromStdString(m_service->privateKey().getV3privateKey());
} else if (m_service->privateKey().getVersion() == CryptoKey::V2) {
out += " RSA1024:";
out += m_service->privateKey().encodedPrivateKey(CryptoKey::DER).toBase64();
}
} else {
// out += " NEW:RSA1024";
out += " NEW:ED25519-V3";
......@@ -90,16 +94,15 @@ void AddOnionCommand::onReply(int statusCode, const QByteArray &data)
if (data.startsWith(keyPrefix)) {
QByteArray keyData(QByteArray::fromBase64(data.mid(keyPrefix.size())));
CryptoKey key;
if (!key.loadFromData(keyData, CryptoKey::PrivateKey, CryptoKey::DER)) {
if (!key.loadFromData(keyData, CryptoKey::V2PrivateKey, CryptoKey::DER)) {
m_errorMessage = QStringLiteral("Key decoding failed");
return;
}
m_service->setPrivateKey(key);
}
// returned data is v3 private key
//FIXME: v3 key should be a global instance
else if (data.startsWith(keyPrefixV3)) {
CryptoKey key(CryptoKey::V3);
CryptoKey key(CryptoKey::V3PrivateKey);
std::string keyData = data.toStdString()
.substr(keyPrefixV3.size());
if(!key.loadFromDataV3(keyData, CryptoKey::V3PrivateKey)) {
......@@ -111,7 +114,7 @@ void AddOnionCommand::onReply(int statusCode, const QByteArray &data)
// returned data is v3 serviced ID
else if ((data.size() == CryptoKey::V3ServiceIDLength + serviceIDPrefix.size())
&& data.startsWith((serviceIDPrefix))) {
CryptoKey key(CryptoKey::V3);
CryptoKey key(CryptoKey::V3ServiceID);
std::string keyData = data.toStdString()
.substr(serviceIDPrefix.size());
if (!key.loadFromDataV3(keyData, CryptoKey::V3ServiceID)) {
......
......@@ -47,8 +47,9 @@ HiddenService::HiddenService(QObject *parent)
{
}
// todo seems to be deprecated
HiddenService::HiddenService(const QString &path, QObject *parent)
: QObject(parent), m_dataPath(path), m_status(NotCreated)
: QObject(parent), m_dataPath(path), m_status(NotCreated)
{
/* Set the initial status and, if possible, load the hostname */
if (QDir(m_dataPath).exists(QLatin1String("private_key"))) {
......@@ -115,7 +116,7 @@ void HiddenService::setPrivateKey(const CryptoKey &key)
void HiddenService::setV3serviceID(const CryptoKey &serviceID)
{
if (!serviceID.isV3serviceID()) {
if (serviceID.getKeyType() != CryptoKey::V3ServiceID) {
BUG() << "Cannot set up hidden service with non-v3 service ID";
return;
}
......@@ -125,12 +126,13 @@ void HiddenService::setV3serviceID(const CryptoKey &serviceID)
emit privateKeyChanged();
}
// todo seems to be deprecated
void HiddenService::loadPrivateKey()
{
if (m_privateKey.isLoaded() || m_dataPath.isEmpty())
return;
bool ok = m_privateKey.loadFromFile(m_dataPath + QLatin1String("/private_key"), CryptoKey::PrivateKey);
bool ok = m_privateKey.loadFromFile(m_dataPath + QLatin1String("/private_key"), CryptoKey::V2PrivateKey);
if (!ok) {
qWarning() << "Failed to load hidden service key";
return;
......
......@@ -4,7 +4,7 @@ import QtQuick.Layouts 1.0
ApplicationWindow {
id: addContactWindow
width: 620
width: 720
height: 300
minimumWidth: width
maximumWidth: width
......
This diff is collapsed.
......@@ -55,10 +55,11 @@ public:
enum KeyType {
PrivateKey,
PublicKey,
V2PrivateKey,
V2PublicKey,
V3PrivateKey,
V3ServiceID,
Empty,
};
// V3 keys can be encoded in either base32 (public key/service id) or base64 (private key hash)
......@@ -76,64 +77,88 @@ public:
INVALID,
};
CryptoKey() {version = INVALID;};
CryptoKey() : CryptoKey(keyType = Empty) {}
explicit CryptoKey(const CryptoKey::Version version) : version(version) {
v3privateKey = "";
v3serviceID = "";
explicit CryptoKey(const KeyType keyType) :
keyType(keyType) {
this->v3EncodedKeyString = "";
};
CryptoKey(const CryptoKey &other) : d(other.d), version(other.version),
v3privateKey(other.v3privateKey), v3serviceID(other.v3serviceID){ };
CryptoKey(const CryptoKey &other) = default;
~CryptoKey();
bool loadFromData(const QByteArray &data, KeyType type, KeyFormat format = PEM);
bool loadFromData(const std::string &data, KeyType type);
bool loadFromDataV2(const QByteArray &data, KeyType type, KeyFormat format);
bool loadFromDataV3(const std::string &data, KeyType type);
bool loadFromFile(const QString &path, KeyType type, KeyFormat format = PEM);
void clear();
bool isLoaded() const;
bool isPrivate() const;
bool isV3serviceID() const;
std::string getV3PublicKey() const;
std::string getV3ServiceId() const { return this->v3serviceID; }
Version getVersion() const { return this->version; }
QByteArray getDecodedV3PublicKey() const;
QByteArray getDecodedV3PrivateKey() const;
QByteArray getDecodedHexV3PrivateKey() const;
QByteArray publicKeyDigest() const;
QByteArray v2PublicKeyDigest() const;
QByteArray encodedPublicKey(KeyFormat format) const;
QByteArray encodedPrivateKey(KeyFormat format) const;
QString torServiceID() const;
int bits() const;
// Calculate and sign SHA-256 digest of data using this key and PKCS #1 v2.0 padding
QByteArray signData(const QByteArray &data) const;
// Verify a signature as per signData
bool verifyData(const QByteArray &data, QByteArray signature) const;
// the sing function for v2
// Sign the input SHA-256 digest using this key and PKCS #1 v2.0 padding
QByteArray signSHA256(const QByteArray &digest) const;
// the sign function for v3
QByteArray signSHA256(const QByteArray &digest, const CryptoKey &v3serviceID) const;
// Verify a signature as per signSHA256
bool verifySHA256(const QByteArray &digest, QByteArray signature) const;
// getters
KeyType getKeyType() const { return this->keyType; }
Version getVersion() const;
std::string getV3privateKey() { return this->keyType == V3PrivateKey ? this->v3EncodedKeyString : ""; }
std::string getV3serviceID() { return this->keyType == V3ServiceID ? this->v3EncodedKeyString : ""; }
std::string getEncodedV3PublicKey() const;
QByteArray getDecodedV3PublicKey() const;
QByteArray getDecodedV3PrivateKey() const;
private:
struct Data : public QSharedData
{
struct Data : public QSharedData {
typedef struct rsa_st RSA;
RSA *key;
Data(RSA *k = 0) : key(k) { }
Data(RSA *k = 0) : key(k) {}
~Data();
};
QExplicitlySharedDataPointer<Data> d;
public:
Version version;
std::string v3privateKey;
std::string v3publicKey;
std::string v3serviceID;
KeyType keyType;
std::string v3EncodedKeyString;
};
QByteArray torControlHashedPassword(const QByteArray &password);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment