From d1596cff9ce170f64bef307418b95314e4a6089e Mon Sep 17 00:00:00 2001 From: Prasenjit Kumar Shaw Date: Wed, 26 Jun 2019 01:55:57 +0530 Subject: [PATCH] Add cryptographic functions for sync Add function for HKDF using Nettle. Add function to process sessionToken. Add function to process keyFetchToken. --- src/lib/sync/fxalogin.cpp | 40 ++++++------- src/lib/sync/synccrypto.cpp | 114 +++++++++++++++++++++++------------- src/lib/sync/synccrypto.h | 20 ++----- 3 files changed, 99 insertions(+), 75 deletions(-) diff --git a/src/lib/sync/fxalogin.cpp b/src/lib/sync/fxalogin.cpp index fd199639c..4562bef50 100644 --- a/src/lib/sync/fxalogin.cpp +++ b/src/lib/sync/fxalogin.cpp @@ -28,42 +28,41 @@ FxALoginPage::FxALoginPage(QWidget* parent) : QWebEngineView(parent) { - m_page = new QWebEnginePage(); + m_page = new QWebEnginePage(this); m_channel = new QWebChannel(m_page); m_page->setWebChannel(m_channel); m_page->load(FxALoginUrl); - this->setPage(m_page); - connect(m_page, SIGNAL(loadFinished(bool)), this, SLOT(pageLoadFinished(bool))); + setPage(m_page); + connect(m_page, &QWebEnginePage::loadFinished, this, &FxALoginPage::pageLoadFinished); } FxALoginPage::~FxALoginPage() { delete m_communicator; delete m_channel; - delete m_page; } void FxALoginPage::pageLoadFinished(bool pageLoaded) { - if(pageLoaded) { + if (pageLoaded) { QFile apiFile(":/qtwebchannel/qwebchannel.js"); - if(!apiFile.open(QIODevice::ReadOnly)) { - qDebug() << "Couldn't load Qt's Webchannel API!"; + if (!apiFile.open(QIODevice::ReadOnly)) { + qWarning() << "Couldn't load Qt's Webchannel API!"; } - QString apiScript = QString::fromLatin1(apiFile.readAll()); + QString apiScript = QString::fromUtf8(apiFile.readAll()); apiFile.close(); m_page->runJavaScript(apiScript); m_communicator = new MessageReceiver(this); - connect(m_communicator, SIGNAL(signalMessageReceived()), - this, SLOT(slotMessageReceived())); + connect(m_communicator, &MessageReceiver::signalMessageReceived, + this, &FxALoginPage::slotMessageReceived); m_channel->registerObject(QString("communicator"), m_communicator); QFile scriptFile(":/data/inject.js"); - if(!scriptFile.open(QIODevice::ReadOnly)) { - qDebug() << "Couldn't load JavaScript file to inject."; + if (!scriptFile.open(QIODevice::ReadOnly)) { + qWarning() << "Couldn't load JavaScript file to inject."; } - QString injectScript = QString::fromLatin1(scriptFile.readAll()); + QString injectScript = QString::fromUtf8(scriptFile.readAll()); scriptFile.close(); m_page->runJavaScript(injectScript); } @@ -79,21 +78,20 @@ void FxALoginPage::slotMessageReceived() void FxALoginPage::parseMessage(QJsonObject *msg) { - QJsonValue command = (*msg).value("detail").toObject().value("message").toObject().value("command"); - if(command.toString() == QString("fxaccounts:can_link_account")) { + QJsonValue command = msg->value("detail").toObject().value("message").toObject().value("command"); + if (command.toString() == QLatin1String("fxaccounts:can_link_account")) { QJsonObject responseData; - responseData.insert("ok", true); + responseData.insert(QString("ok"), true); QJsonObject message; message.insert("command", command); message.insert("data", responseData); - message.insert("messageId", (*msg).value("detail").toObject().value("message").toObject().value("messageId")); + message.insert("messageId", msg->value("detail").toObject().value("message").toObject().value("messageId")); QJsonObject response; - response.insert("id", (*msg).value("detail").toObject().value("id")); + response.insert("id", msg->value("detail").toObject().value("id")); response.insert("message", message); sendMessage(response); - } - else if(command.toString() == QString("fxaccounts:login")) { - QJsonObject data = (*msg).value("detail").toObject().value("message").toObject().value("data").toObject(); + } else if (command.toString() == QLatin1String("fxaccounts:login")) { + QJsonObject data = msg->value("detail").toObject().value("message").toObject().value("data").toObject(); QString email = data.value("email").toString(); QString uid = data.value("uid").toString(); QString session_token = data.value("sessionToken").toString(); diff --git a/src/lib/sync/synccrypto.cpp b/src/lib/sync/synccrypto.cpp index f8f510eb7..d3f45b0d1 100644 --- a/src/lib/sync/synccrypto.cpp +++ b/src/lib/sync/synccrypto.cpp @@ -15,57 +15,91 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ - #include "synccrypto.h" -#include -#include +#include +#include +#include #include +#include -HKDF::HKDF(const QByteArray key, const QByteArray salt, const QByteArray info) +u_char *syncCryptoHkdf(QByteArray *in, QByteArray *info, size_t out_len) { - pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF,NULL); + hmac_sha256_ctx *ctx = new hmac_sha256_ctx; - init(key, salt,info); + size_t in_len = in->size(); + size_t info_len = info->size(); + u_char *salt = new u_char[SHA256_DIGEST_SIZE] {0}; + u_char *prk = new u_char[SHA256_DIGEST_SIZE]; + u_char *output = new u_char[out_len]; + + nettle_hmac_sha256_set_key(ctx, SHA256_DIGEST_SIZE, salt); + nettle_hkdf_extract(ctx, + (nettle_hash_update_func *)hmac_sha256_update, + (nettle_hash_digest_func *)hmac_sha256_digest, + SHA256_DIGEST_SIZE, + in_len, (u_char *)in->data(), prk); + nettle_hmac_sha256_set_key(ctx, SHA256_DIGEST_SIZE, prk); + nettle_hkdf_expand(ctx, + (nettle_hash_update_func *)hmac_sha256_update, + (nettle_hash_digest_func *)hmac_sha256_digest, + SHA256_DIGEST_SIZE, + info_len, (u_char *)info->data(), out_len, output); + + delete[] prk; + return output; } -HKDF::~HKDF() { -} - -void HKDF::init(const QByteArray key, const QByteArray salt, const QByteArray info) +void syncCryptoKW(QByteArray *kw, QString name) { - size_t keylen = key.size(); - size_t saltlen = salt.size(); - size_t infolen = info.size(); - - if (EVP_PKEY_derive_init(pctx) <= 0) { - qWarning("Unable to initialize public key algorithm context for HKDF."); - return; - } - if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) { - qWarning("Unable to set HKDF message digest."); - return; - } - if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (uchar *)salt.data(), saltlen) <= 0) { - qWarning("Unable to set salt."); - return; - } - if (EVP_PKEY_CTX_set1_hkdf_key(pctx, (uchar *)key.data(), keylen) <= 0) { - qWarning("Unable to set key."); - return; - } - if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (uchar *)info.data(), infolen) <= 0) { - qWarning("Uable to set info for HKDF."); - return; - } + // Concatenate "name" to Mozilla prefix to get the required KW. + // See https://raw.githubusercontent.com/wiki/mozilla/fxa-auth-server/images/onepw-create.png for details. + QString info = QString("identity.mozilla.com/picl/v1/").append(name); + kw->append(info.toUtf8()); } -QByteArray HKDF::getKey(size_t outlen) { - QByteArray out; - if (EVP_PKEY_derive(pctx, (uchar *)out.data(), &outlen) <= 0) { - qWarning("Unable to derive HKDF key."); - } - return out; +void deriveSessionToken(QByteArray *sessionToken, QByteArray *tokenId, QByteArray *reqHMACKey, QByteArray *reqKey) +{ + QByteArray *kw = new QByteArray(); + syncCryptoKW(kw, QString("sessionToken")); + size_t len = 32; + + u_char *out = syncCryptoHkdf(sessionToken, kw, 3 * len); + QByteArray *temp = new QByteArray(QByteArray::fromRawData((const char *)out, 3 * len)); + QByteArray tId(*temp); + tId.remove(len, 2 * len); + tokenId->append(tId); + reqKey->append(temp->right(len)); + temp->remove(0, len); + temp->remove(len, len); + reqHMACKey->append(*temp); } +void deriveKeyFetchToken(QByteArray *keyFetchToken, QByteArray *tokenId, QByteArray *reqHMACKey, QByteArray *respHMACKey, QByteArray *respXORKey) +{ + QByteArray *infoKft = new QByteArray(); + syncCryptoKW(infoKft, "keyFetchToken"); + QByteArray *infoKeys = new QByteArray(); + syncCryptoKW(infoKeys, "account/keys"); + size_t len = 32; + + u_char *out1 = syncCryptoHkdf(keyFetchToken, infoKft, 3 * len); + QByteArray *temp = new QByteArray(QByteArray::fromRawData((const char *)out1, 3 * len)); + QByteArray tId(*temp); + tId.remove(len, 2 * len); + tokenId->append(tId); + QByteArray *reqKey = new QByteArray(); + reqKey->append(temp->right(len)); + temp->remove(0, len); + temp->remove(len, len); + reqHMACKey->append(*temp); + + u_char *out2 = syncCryptoHkdf(reqKey, infoKeys, 3 * len); + QByteArray *temp2 = new QByteArray(QByteArray::fromRawData((const char *)out2, 3 * len)); + QByteArray respHKey(*temp2); + respHKey.remove(len, 2 * len); + respHMACKey->append(respHKey); + temp2->remove(0, len); + respXORKey->append(*temp2); +} diff --git a/src/lib/sync/synccrypto.h b/src/lib/sync/synccrypto.h index f57e5cde4..ce5f78a22 100644 --- a/src/lib/sync/synccrypto.h +++ b/src/lib/sync/synccrypto.h @@ -17,20 +17,12 @@ * ============================================================ */ #pragma once -#include #include +#include -class HKDF -{ -public: - explicit HKDF(const QByteArray key, const QByteArray salt, const QByteArray info); - ~HKDF(); - - QByteArray getKey(size_t outlen); - -private: - void init(const QByteArray key, const QByteArray salt, const QByteArray info); - - EVP_PKEY_CTX *pctx; -}; +#define SHA256_DIGEST_SIZE 32 +u_char *syncCryptoHkdf(QByteArray *in, QByteArray *info, size_t out_len); +void syncCryptoKW(QByteArray *kw, QString name); +void deriveSessionToken(QByteArray *sessionToken, QByteArray *tokenId, QByteArray *reqHMACKey, QByteArray *reqKey); +void deriveKeyFetchToken(QByteArray *keyFetchToken, QByteArray *tokenId, QByteArray *reqHMACKey, QByteArray *respHMACKey, QByteArray *respXORKey);