mirror of
https://invent.kde.org/network/falkon.git
synced 2024-11-11 01:22:10 +01:00
Add method to fetch sync Master Key and upload device name
This commit is contained in:
parent
1460611cce
commit
ccbbf2c0fc
|
@ -223,6 +223,7 @@ set(SRCS ${SRCS}
|
||||||
sync/synccrypto.cpp
|
sync/synccrypto.cpp
|
||||||
sync/hawk/hawk.cpp
|
sync/hawk/hawk.cpp
|
||||||
sync/syncmanager.cpp
|
sync/syncmanager.cpp
|
||||||
|
sync/syncutils.cpp
|
||||||
tabwidget/combotabbar.cpp
|
tabwidget/combotabbar.cpp
|
||||||
tabwidget/tabbar.cpp
|
tabwidget/tabbar.cpp
|
||||||
tabwidget/tabicon.cpp
|
tabwidget/tabicon.cpp
|
||||||
|
|
|
@ -34,7 +34,7 @@ SyncOptions::SyncOptions(QWidget* parent)
|
||||||
loginPage = new FxALoginPage(this);
|
loginPage = new FxALoginPage(this);
|
||||||
ui->fxaloginframe->addWidget(loginPage);
|
ui->fxaloginframe->addWidget(loginPage);
|
||||||
|
|
||||||
connect(ui->btnSyncNow, &QPushButton::clicked, mApp->syncManager(), &SyncManager::sync);
|
connect(ui->btnSyncNow, &QPushButton::clicked, mApp->syncManager(), &SyncManager::startSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncOptions::~SyncOptions()
|
SyncOptions::~SyncOptions()
|
||||||
|
|
|
@ -100,7 +100,6 @@ void FxALoginPage::parseMessage(QJsonObject *msg)
|
||||||
settings.setValue(QSL("KeyFetchToken"), key_fetch_token);
|
settings.setValue(QSL("KeyFetchToken"), key_fetch_token);
|
||||||
settings.setValue(QSL("UnwrapBKey"), unwrap_kb);
|
settings.setValue(QSL("UnwrapBKey"), unwrap_kb);
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
// derive the various tokens and save into settings right here and instead of synccreds, create a user_data object
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
#include "synccrypto.h"
|
#include "synccrypto.h"
|
||||||
|
#include "syncutils.h"
|
||||||
|
|
||||||
#include <nettle/sha2.h>
|
#include <nettle/sha2.h>
|
||||||
#include <nettle/hkdf.h>
|
#include <nettle/hkdf.h>
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
|
@ -153,3 +155,158 @@ RSAKeyPair *generateRSAKeyPair()
|
||||||
|
|
||||||
return keyPair;
|
return keyPair;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray *createBowserIdAssertion(QByteArray *certificate, QByteArray *audience, qint64 seconds, RSAKeyPair *keyPair)
|
||||||
|
{
|
||||||
|
QByteArray *assertion = new QByteArray();
|
||||||
|
mpz_t signature;
|
||||||
|
|
||||||
|
QString tempHeader("{\"alg\": \"RS256\"}");
|
||||||
|
QByteArray header = QByteArray(tempHeader.toUtf8());
|
||||||
|
header.toBase64();
|
||||||
|
|
||||||
|
qint64 expiry = QDateTime::currentMSecsSinceEpoch() + seconds * 1000;
|
||||||
|
QString tempBody = QString("{\"exp\": %1, \"aud\": \"%2\"}").arg(expiry).arg(audience->data());
|
||||||
|
QByteArray body = QByteArray(tempBody.toUtf8());
|
||||||
|
body.toBase64();
|
||||||
|
|
||||||
|
QByteArray toSign = QByteArray((QString("%1.%2").arg(header.data()).arg(header.data())).toUtf8());
|
||||||
|
u_char *digest = new u_char[SHA256_DIGEST_SIZE];
|
||||||
|
sha256_ctx *ctx = new sha256_ctx;
|
||||||
|
sha256_init(ctx);
|
||||||
|
sha256_update(ctx, toSign.size(), (u_char *)(toSign.data()));
|
||||||
|
sha256_digest(ctx, SHA256_DIGEST_SIZE, digest);
|
||||||
|
delete ctx;
|
||||||
|
|
||||||
|
mpz_init(signature);
|
||||||
|
int success = nettle_rsa_sha256_sign_digest_tr(&(keyPair->m_publicKey), &(keyPair->m_privateKey), nullptr, generateRandomBytes, digest, signature);
|
||||||
|
Q_ASSERT(success);
|
||||||
|
|
||||||
|
size_t expectedSize = (mpz_sizeinbase(signature, 2) + 7) / 8;
|
||||||
|
size_t count;
|
||||||
|
u_char *sig = new u_char[expectedSize];
|
||||||
|
mpz_export(sig, &count, 1, sizeof(quint8), 0, 0, signature);
|
||||||
|
Q_ASSERT(count == expectedSize);
|
||||||
|
|
||||||
|
QByteArray sigBase64 = QByteArray::fromRawData((const char *)sig, count);
|
||||||
|
sigBase64.toBase64();
|
||||||
|
|
||||||
|
assertion->append((QString("%1~%2.%3.%4").arg(certificate->data()).arg(header.data()).arg(body.data()).arg(sigBase64.data())).toUtf8());
|
||||||
|
|
||||||
|
mpz_clear(signature);
|
||||||
|
delete digest;
|
||||||
|
delete sig;
|
||||||
|
return assertion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QByteArray *xorQByteArray(QByteArray *a, QByteArray *b, size_t len)
|
||||||
|
{
|
||||||
|
QByteArray *xored = new QByteArray();
|
||||||
|
|
||||||
|
char *aData = a->data();
|
||||||
|
char *bData = b->data();
|
||||||
|
char *xorData = new char[len];
|
||||||
|
|
||||||
|
for(int i = 0; i < len; ++i) {
|
||||||
|
xorData[i] = aData[i] ^ bData[i];
|
||||||
|
}
|
||||||
|
xored->append(xorData, len);
|
||||||
|
|
||||||
|
return xored;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deriveMasterKey(QByteArray *bundleHex, QByteArray *respHMACKey, QByteArray *respXORKey, QByteArray *unwrapKb, QByteArray *ka, QByteArray *kb)
|
||||||
|
{
|
||||||
|
bool returnVal = true;
|
||||||
|
QByteArray *bundle = new QByteArray(QByteArray::fromHex(bundleHex->data()));
|
||||||
|
size_t masterKeyLen = 32;
|
||||||
|
QByteArray *cipherText = new QByteArray(bundle->data());
|
||||||
|
cipherText->chop(masterKeyLen);
|
||||||
|
QByteArray *respHMAC = new QByteArray(bundle->right(masterKeyLen).data());
|
||||||
|
|
||||||
|
hmac_sha256_ctx *ctx = new hmac_sha256_ctx;
|
||||||
|
hmac_sha256_set_key(ctx, respHMACKey->size(), (u_char *)respHMACKey->data());
|
||||||
|
hmac_sha256_update(ctx, cipherText->length(), (u_char *)cipherText->data());
|
||||||
|
u_char *out = new u_char[SHA256_DIGEST_SIZE];
|
||||||
|
hmac_sha256_digest(ctx, SHA256_DIGEST_SIZE, out);
|
||||||
|
QByteArray *respHMAC2 = new QByteArray(QByteArray::fromRawData((const char *)out, SHA256_DIGEST_SIZE));
|
||||||
|
|
||||||
|
|
||||||
|
QByteArray mac(respHMAC->data());
|
||||||
|
QByteArray mac2(respHMAC2->data());
|
||||||
|
qDebug() << "mac: " << mac.toHex().data() << "\ndigest:" << mac2.toHex().data();
|
||||||
|
|
||||||
|
if (*respHMAC == *respHMAC2) {
|
||||||
|
QByteArray *xored = xorQByteArray(cipherText, respXORKey, 2 * masterKeyLen);
|
||||||
|
ka->append(xored->data());
|
||||||
|
ka->chop(masterKeyLen);
|
||||||
|
QByteArray *wrapKb = new QByteArray(xored->right(masterKeyLen));
|
||||||
|
QByteArray *xored2 = xorQByteArray(unwrapKb, wrapKb, masterKeyLen);
|
||||||
|
kb->append(xored2->data());
|
||||||
|
|
||||||
|
delete xored;
|
||||||
|
delete wrapKb;
|
||||||
|
delete xored2;
|
||||||
|
} else {
|
||||||
|
returnVal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete bundle;
|
||||||
|
delete cipherText;
|
||||||
|
delete respHMAC;
|
||||||
|
delete ctx;
|
||||||
|
|
||||||
|
return returnVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void testDeriveMasterKey()
|
||||||
|
{
|
||||||
|
bool returnVal = true;
|
||||||
|
QByteArray bundle(QByteArray::fromHex("ee5c58845c7c9412b11bbd20920c2fddd83c33c9cd2c2de2d66b222613364636fc7e59d854d599f10e212801de3a47c34333f3b838ee3471e0f285649c332bbb4c17f42a0b319bbba327d2b326ad23e937219b4de32e3ec7b3e3f740522ad6ef"));
|
||||||
|
size_t masterKeyLen = 32;
|
||||||
|
QByteArray cipherText(bundle.data());
|
||||||
|
cipherText.chop(masterKeyLen);
|
||||||
|
QByteArray respHMAC(bundle.right(masterKeyLen).data());
|
||||||
|
|
||||||
|
QByteArray respHMACKey(QByteArray::fromHex("f824d2953aab9faf51a1cb65ba9e7f9e5bf91c8d8fd1ac1c8c2d31853a8a1210"));
|
||||||
|
QByteArray respXORKey(QByteArray::fromHex("ce7d7aa77859b2359932970bbe2101f2e80d01faf9191bd5ee52181d2f0b78098281ba8cff3925433a89f7c3095e0c89900a469d60790c833281c4df1a11c763"));
|
||||||
|
|
||||||
|
QByteArray unwrapKb(QByteArray::fromHex("de6a2648b78284fcb9ffa81ba95803309cfba7af583c01a8a1a63e567234dd28"));
|
||||||
|
|
||||||
|
hmac_sha256_ctx *ctx = new hmac_sha256_ctx;
|
||||||
|
hmac_sha256_set_key(ctx, respHMACKey.size(), (u_char *)respHMACKey.data());
|
||||||
|
hmac_sha256_update(ctx, cipherText.length(), (u_char *)cipherText.data());
|
||||||
|
u_char *out = new u_char[SHA256_DIGEST_SIZE];
|
||||||
|
hmac_sha256_digest(ctx, SHA256_DIGEST_SIZE, out);
|
||||||
|
QByteArray respHMAC2(QByteArray::fromRawData((const char *)out, SHA256_DIGEST_SIZE));
|
||||||
|
|
||||||
|
|
||||||
|
QByteArray mac(respHMAC.data());
|
||||||
|
QByteArray mac2(respHMAC2.data());
|
||||||
|
qDebug() << "mac: " << mac.toHex().data() << "\ndigest:" << mac2.toHex().data() << "\nequal? " << (respHMAC == respHMAC2);
|
||||||
|
|
||||||
|
if (respHMAC == respHMAC2) {
|
||||||
|
QByteArray *xored = xorQByteArray(&cipherText, &respXORKey, 2 * masterKeyLen);
|
||||||
|
QByteArray ka(xored->data());
|
||||||
|
ka.chop(masterKeyLen);
|
||||||
|
QByteArray *wrapKb = new QByteArray(xored->right(masterKeyLen));
|
||||||
|
QByteArray *xored2 = xorQByteArray(&unwrapKb, wrapKb, masterKeyLen);
|
||||||
|
QByteArray kb(xored2->data());
|
||||||
|
|
||||||
|
QByteArray expectedKa(QByteArray::fromHex("202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"));
|
||||||
|
QByteArray expectedKb(("a095c51c1c6e384e8d5777d97e3c487a4fc2128a00ab395a73d57fedf41631f0"));
|
||||||
|
|
||||||
|
qDebug() << "ka? " << (ka == expectedKa) << "\nkb? " << (kb == expectedKb);
|
||||||
|
|
||||||
|
delete xored;
|
||||||
|
delete wrapKb;
|
||||||
|
delete xored2;
|
||||||
|
} else {
|
||||||
|
returnVal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete ctx;
|
||||||
|
delete out;
|
||||||
|
}
|
||||||
|
|
|
@ -29,8 +29,17 @@ struct RSAKeyPair {
|
||||||
};
|
};
|
||||||
|
|
||||||
void generateRandomBytes(void *randomCtx, size_t numBytes, u_char *out);
|
void generateRandomBytes(void *randomCtx, size_t numBytes, u_char *out);
|
||||||
|
|
||||||
u_char *syncCryptoHkdf(QByteArray *in, QByteArray *info, size_t out_len);
|
u_char *syncCryptoHkdf(QByteArray *in, QByteArray *info, size_t out_len);
|
||||||
void syncCryptoKW(QByteArray *kw, QString name);
|
void syncCryptoKW(QByteArray *kw, QString name);
|
||||||
|
|
||||||
void deriveSessionToken(QByteArray *sessionToken, QByteArray *tokenId, QByteArray *reqHMACKey, QByteArray *reqKey);
|
void deriveSessionToken(QByteArray *sessionToken, QByteArray *tokenId, QByteArray *reqHMACKey, QByteArray *reqKey);
|
||||||
void deriveKeyFetchToken(QByteArray *keyFetchToken, QByteArray *tokenId, QByteArray *reqHMACKey, QByteArray *respHMACKey, QByteArray *respXORKey);
|
void deriveKeyFetchToken(QByteArray *keyFetchToken, QByteArray *tokenId, QByteArray *reqHMACKey, QByteArray *respHMACKey, QByteArray *respXORKey);
|
||||||
|
bool deriveMasterKey(QByteArray *bundleHex, QByteArray *respHMACKey, QByteArray *respXORKey, QByteArray *unwrapKb, QByteArray *ka, QByteArray *kb);
|
||||||
|
|
||||||
RSAKeyPair *generateRSAKeyPair();
|
RSAKeyPair *generateRSAKeyPair();
|
||||||
|
|
||||||
|
QByteArray *createBowserIdAssertion(QByteArray *certificate, QByteArray *audience, qint64 seconds, RSAKeyPair *keyPair);
|
||||||
|
|
||||||
|
QByteArray *xorQByteArray(QByteArray *a, QByteArray *b, size_t len);
|
||||||
|
void testDeriveMasterKey();
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
#include "syncmanager.h"
|
#include "syncmanager.h"
|
||||||
#include "syncrequest.h"
|
|
||||||
#include "synccrypto.h"
|
#include "synccrypto.h"
|
||||||
|
#include "syncutils.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "hawk.h"
|
#include "hawk.h"
|
||||||
#include "mainapplication.h"
|
#include "mainapplication.h"
|
||||||
|
@ -79,7 +79,7 @@ SyncState::~SyncState()
|
||||||
void SyncState::saveSyncState(bool syncSuccess)
|
void SyncState::saveSyncState(bool syncSuccess)
|
||||||
{
|
{
|
||||||
if (syncSuccess) {
|
if (syncSuccess) {
|
||||||
*m_lastSyncTime = QDateTime::currentDateTimeUtc();
|
*m_lastSyncTime = QDateTime::currentDateTime();
|
||||||
m_isInitialSync = false;
|
m_isInitialSync = false;
|
||||||
Settings settings;
|
Settings settings;
|
||||||
settings.beginGroup(QSL("SyncState"));
|
settings.beginGroup(QSL("SyncState"));
|
||||||
|
@ -107,14 +107,19 @@ SyncManager::SyncManager(QObject *parent)
|
||||||
m_syncState = new SyncState();
|
m_syncState = new SyncState();
|
||||||
m_networkManager = new QNetworkAccessManager(this);
|
m_networkManager = new QNetworkAccessManager(this);
|
||||||
m_keyPair = generateRSAKeyPair();
|
m_keyPair = generateRSAKeyPair();
|
||||||
|
m_browserCertificate = new QByteArray();
|
||||||
|
|
||||||
|
testDeriveMasterKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncManager::~SyncManager()
|
SyncManager::~SyncManager()
|
||||||
{
|
{
|
||||||
saveSyncState();
|
saveSyncState();
|
||||||
|
m_networkManager->deleteLater();
|
||||||
delete m_syncCreds;
|
delete m_syncCreds;
|
||||||
delete m_syncState;
|
delete m_syncState;
|
||||||
delete m_keyPair;
|
delete m_keyPair;
|
||||||
|
delete m_browserCertificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncManager::saveSyncState()
|
void SyncManager::saveSyncState()
|
||||||
|
@ -124,35 +129,49 @@ void SyncManager::saveSyncState()
|
||||||
|
|
||||||
void SyncManager::sync()
|
void SyncManager::sync()
|
||||||
{
|
{
|
||||||
bool error = false;
|
switch(m_syncStep) {
|
||||||
if (m_storageCredentialsExpired) {
|
case enum_ERROR:
|
||||||
error = getBrowserSignedCertificate();
|
qWarning() << "Error occured in Sync Process.";
|
||||||
if (!error) {
|
|
||||||
m_storageCredentialsExpired = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << "Getting Crypto Keys";
|
|
||||||
getCryptoKeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
m_syncSuccess = false;
|
m_syncSuccess = false;
|
||||||
qDebug() << "Sync Failed";
|
|
||||||
} else {
|
|
||||||
m_syncSuccess = true;
|
|
||||||
}
|
|
||||||
saveSyncState();
|
saveSyncState();
|
||||||
|
break;
|
||||||
|
case enum_FetchBrowserId:
|
||||||
|
getBrowserSignedCertificate();
|
||||||
|
break;
|
||||||
|
case enum_TradeBrowserIdAssertion:
|
||||||
|
tradeBrowserIdAssertion();
|
||||||
|
break;
|
||||||
|
case enum_FetchAccountKeys:
|
||||||
|
getCryptoKeys();
|
||||||
|
break;
|
||||||
|
case enum_UploadDevice:
|
||||||
|
uploadDevice();
|
||||||
|
break;
|
||||||
|
case enum_NoRequestPending:
|
||||||
|
qDebug() << "No sync requests pending.";
|
||||||
|
m_syncSuccess = true;
|
||||||
|
saveSyncState();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
saveSyncState();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncManager::startSync()
|
void SyncManager::startSync()
|
||||||
{
|
{
|
||||||
|
if(m_syncState->isInitialSync()) {
|
||||||
|
qDebug() << "is initial sync, going to uploadDevice";
|
||||||
|
m_syncStep = enum_UploadDevice;
|
||||||
|
} else {
|
||||||
|
qDebug() << "not initial sync, going to fetchBrowserId";
|
||||||
|
m_syncStep = enum_FetchBrowserId;
|
||||||
|
}
|
||||||
sync();
|
sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SyncManager::getBrowserSignedCertificate()
|
void SyncManager::getBrowserSignedCertificate()
|
||||||
{
|
{
|
||||||
bool error = false;
|
|
||||||
|
|
||||||
QString endpoint("certificate/sign");
|
QString endpoint("certificate/sign");
|
||||||
|
|
||||||
QByteArray *sessionToken = new QByteArray(QByteArray::fromHex(m_syncCreds->getValue("SessionToken").toUtf8().data()));
|
QByteArray *sessionToken = new QByteArray(QByteArray::fromHex(m_syncCreds->getValue("SessionToken").toUtf8().data()));
|
||||||
|
@ -182,7 +201,7 @@ bool SyncManager::getBrowserSignedCertificate()
|
||||||
QNetworkRequest request = createHawkPostReqeuest(endpoint, tokenIdHex, requestHMACKey, 32, data);
|
QNetworkRequest request = createHawkPostReqeuest(endpoint, tokenIdHex, requestHMACKey, 32, data);
|
||||||
|
|
||||||
QNetworkReply *reply = m_networkManager->post(request, *data);
|
QNetworkReply *reply = m_networkManager->post(request, *data);
|
||||||
connect(reply, &QNetworkReply::finished, this, &SyncManager::recievedBrowserSignedCertificate);
|
connect(reply, &QNetworkReply::finished, this, &SyncManager::callback_getBrowserSignedCertificate);
|
||||||
|
|
||||||
qDebug() << "Hawk POST Request sent to /certificate/sign endpoint.";
|
qDebug() << "Hawk POST Request sent to /certificate/sign endpoint.";
|
||||||
|
|
||||||
|
@ -194,25 +213,27 @@ bool SyncManager::getBrowserSignedCertificate()
|
||||||
delete n;
|
delete n;
|
||||||
delete e;
|
delete e;
|
||||||
delete data;
|
delete data;
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncManager::recievedBrowserSignedCertificate()
|
void SyncManager::callback_getBrowserSignedCertificate()
|
||||||
{
|
{
|
||||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||||
if (!reply) {
|
if (!reply) {
|
||||||
|
m_syncStep = enum_ERROR;
|
||||||
|
sync();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
qWarning() << "Error in recieving Browser Signed Certificate for Firefox Sync.";
|
qWarning() << "Error in receiving Browser Signed Certificate for Firefox Sync.";
|
||||||
reply->close();
|
reply->close();
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
m_syncStep = enum_ERROR;
|
||||||
|
sync();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray response = reply->readAll();
|
QByteArray response(reply->readAll().data());
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(response);
|
QJsonDocument doc = QJsonDocument::fromJson(response);
|
||||||
QJsonObject obj = doc.object();
|
QJsonObject obj = doc.object();
|
||||||
|
|
||||||
|
@ -221,15 +242,24 @@ void SyncManager::recievedBrowserSignedCertificate()
|
||||||
if (verifyBrowserSignedCertificate(&certificate)) {
|
if (verifyBrowserSignedCertificate(&certificate)) {
|
||||||
qDebug() << "Valid Browser Signed Certificate Response.";
|
qDebug() << "Valid Browser Signed Certificate Response.";
|
||||||
m_storageCredentialsExpired = false;
|
m_storageCredentialsExpired = false;
|
||||||
sync();
|
m_browserCertificate->clear();
|
||||||
|
m_browserCertificate->append(certificate.data());
|
||||||
|
if (m_syncState->isInitialSync()) {
|
||||||
|
m_syncStep = enum_FetchAccountKeys;
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Invalid Browser ID Assertion Response Recieved.";
|
m_syncStep = enum_TradeBrowserIdAssertion;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Invalid Browser ID Assertion Response Recieved.";
|
qWarning() << "Invalid Browser ID Assertion Response Received.";
|
||||||
|
m_syncStep = enum_ERROR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qWarning() << "Invalid Browser ID Assertion Response Received.";
|
||||||
|
m_syncStep = enum_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SyncManager::verifyBrowserSignedCertificate(QByteArray* certificate)
|
bool SyncManager::verifyBrowserSignedCertificate(QByteArray* certificate)
|
||||||
|
@ -268,9 +298,16 @@ bool SyncManager::verifyBrowserSignedCertificate(QByteArray* certificate)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SyncManager::tradeBrowserIdAssertion()
|
||||||
bool SyncManager::getCryptoKeys()
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
m_syncStep = enum_NoRequestPending;
|
||||||
|
sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncManager::getCryptoKeys()
|
||||||
|
{
|
||||||
|
qDebug() << "getCryptoKeys";
|
||||||
QString endpoint("account/keys");
|
QString endpoint("account/keys");
|
||||||
QByteArray *keyFetchToken = new QByteArray(QByteArray::fromHex(m_syncCreds->getValue("KeyFetchToken").toUtf8().data()));
|
QByteArray *keyFetchToken = new QByteArray(QByteArray::fromHex(m_syncCreds->getValue("KeyFetchToken").toUtf8().data()));
|
||||||
QByteArray *tokenId = new QByteArray();
|
QByteArray *tokenId = new QByteArray();
|
||||||
|
@ -279,21 +316,22 @@ bool SyncManager::getCryptoKeys()
|
||||||
QByteArray *respXorKey = new QByteArray();
|
QByteArray *respXorKey = new QByteArray();
|
||||||
deriveKeyFetchToken(keyFetchToken, tokenId, reqHMACKey, respHMACKey, respXorKey);
|
deriveKeyFetchToken(keyFetchToken, tokenId, reqHMACKey, respHMACKey, respXorKey);
|
||||||
|
|
||||||
|
QByteArray respHMACKeyHex(respHMACKey->toHex().data());
|
||||||
|
QByteArray reqHMACKeyHex(reqHMACKey->toHex().data());
|
||||||
|
QByteArray respXorKeyHex(respXorKey->toHex().data());
|
||||||
|
|
||||||
|
m_syncCreds->addSyncCredentials(QString("RespHMACKey"), QString(respHMACKeyHex.data()));
|
||||||
|
m_syncCreds->addSyncCredentials(QString("ReqHMACKey"), QString(reqHMACKeyHex.data()));
|
||||||
|
m_syncCreds->addSyncCredentials(QString("RespXORKey"), QString(respXorKeyHex.data()));
|
||||||
|
qDebug() << "derived keyFetchToken";
|
||||||
QByteArray *tokenIdHex = new QByteArray(tokenId->toHex().data());
|
QByteArray *tokenIdHex = new QByteArray(tokenId->toHex().data());
|
||||||
|
|
||||||
QNetworkRequest request = createHawkGetRequest(endpoint, tokenIdHex, reqHMACKey, 32);
|
QNetworkRequest request = createHawkGetRequest(endpoint, tokenIdHex, reqHMACKey, 32);
|
||||||
|
qDebug() << "Request created";
|
||||||
QNetworkReply *reply = m_networkManager->get(request);
|
QNetworkReply *reply = m_networkManager->get(request);
|
||||||
connect(reply, &QNetworkReply::finished, this, [ = ]() {
|
connect(reply, &QNetworkReply::finished, this, &SyncManager::callback_getCryptoKeys);
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
|
||||||
qWarning() << "Reply Error" << reply->error() << reply->errorString();
|
qDebug() << "Hawk POST Request sent to /account/keys endpoint.";
|
||||||
qDebug() << "Reply: " << reply->readAll();
|
|
||||||
reply->deleteLater();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
qDebug() << "Reply: " << reply->readAll();
|
|
||||||
reply->deleteLater();
|
|
||||||
});
|
|
||||||
|
|
||||||
delete keyFetchToken;
|
delete keyFetchToken;
|
||||||
delete tokenId;
|
delete tokenId;
|
||||||
|
@ -301,10 +339,139 @@ bool SyncManager::getCryptoKeys()
|
||||||
delete respHMACKey;
|
delete respHMACKey;
|
||||||
delete respXorKey;
|
delete respXorKey;
|
||||||
delete tokenIdHex;
|
delete tokenIdHex;
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SyncManager::callback_getCryptoKeys()
|
||||||
|
{
|
||||||
|
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||||
|
qDebug() << "Received reply";
|
||||||
|
if (!reply) {
|
||||||
|
qDebug()<< "No reply";
|
||||||
|
m_syncStep = enum_ERROR;
|
||||||
|
sync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
qWarning() << "Error in receiving Crypto Keys for Firefox Sync.";
|
||||||
|
qDebug() << reply->readAll();
|
||||||
|
reply->close();
|
||||||
|
reply->deleteLater();
|
||||||
|
m_syncStep = enum_ERROR;
|
||||||
|
sync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "received bundle";
|
||||||
|
QByteArray response(reply->readAll().data());
|
||||||
|
qDebug() << "account/keys response:\n" << response;
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(response);
|
||||||
|
QJsonObject obj = doc.object();
|
||||||
|
|
||||||
|
if (obj.contains(QString("bundle"))) {
|
||||||
|
QByteArray *bundle = new QByteArray(obj.value(QString("bundle")).toString().toUtf8());
|
||||||
|
qDebug() << "bundle: " << bundle->data();
|
||||||
|
m_syncCreds->addSyncCredentials(QString("Bundle"), QString(bundle->data()));
|
||||||
|
QByteArray *ka = new QByteArray();
|
||||||
|
QByteArray *kb = new QByteArray();
|
||||||
|
QByteArray *unwrapKb = new QByteArray(QByteArray::fromHex(m_syncCreds->getValue(QString("UnwrapBKey")).toUtf8()));
|
||||||
|
QByteArray *respHMACKey = new QByteArray(QByteArray::fromHex(m_syncCreds->getValue(QString("RespHMACKey")).toUtf8()));
|
||||||
|
QByteArray *respXorKey = new QByteArray(QByteArray::fromHex(m_syncCreds->getValue(QString("RespXORKey")).toUtf8()));
|
||||||
|
if(!deriveMasterKey(bundle, respHMACKey, respXorKey, unwrapKb, ka, kb)) {
|
||||||
|
qWarning() << "Failed to retrieve Sync Key.";
|
||||||
|
m_syncStep = enum_ERROR;
|
||||||
|
} else {
|
||||||
|
m_syncCreds->addSyncCredentials(QString("MasterKey"), QString(kb->toHex().data()));
|
||||||
|
m_syncStep = enum_TradeBrowserIdAssertion;
|
||||||
|
}
|
||||||
|
delete bundle;
|
||||||
|
delete ka;
|
||||||
|
delete kb;
|
||||||
|
delete unwrapKb;
|
||||||
|
delete respHMACKey;
|
||||||
|
delete respXorKey;
|
||||||
|
} else {
|
||||||
|
qWarning() << "Invalid Crypto Keys Response Recieved.";
|
||||||
|
m_syncStep = enum_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SyncManager::uploadDevice()
|
||||||
|
{
|
||||||
|
QString endpoint("account/device");
|
||||||
|
|
||||||
|
QJsonObject obj;
|
||||||
|
obj.insert(QString("name"), QString("Falkon"));
|
||||||
|
obj.insert(QString("type"), QString("desktop"));
|
||||||
|
QJsonDocument doc(obj);
|
||||||
|
QByteArray *data = new QByteArray(doc.toJson(QJsonDocument::Compact));
|
||||||
|
|
||||||
|
QByteArray *sessionToken = new QByteArray(QByteArray::fromHex(m_syncCreds->getValue("SessionToken").toUtf8().data()));
|
||||||
|
QByteArray *tokenId = new QByteArray();
|
||||||
|
QByteArray *requestHMACKey = new QByteArray();
|
||||||
|
QByteArray *requestKey = new QByteArray();
|
||||||
|
deriveSessionToken(sessionToken, tokenId, requestHMACKey, requestKey);
|
||||||
|
|
||||||
|
QByteArray *tokenIdHex = new QByteArray(tokenId->toHex().data());
|
||||||
|
|
||||||
|
QNetworkRequest request = createHawkPostReqeuest(endpoint, tokenIdHex, requestHMACKey, 32, data);
|
||||||
|
QNetworkReply *reply = m_networkManager->post(request, *data);
|
||||||
|
connect(reply, &QNetworkReply::finished, this, &SyncManager::callback_uploadDevice);
|
||||||
|
|
||||||
|
qDebug() << "Hawk POST Request sent to /account/device endpoint.";
|
||||||
|
|
||||||
|
|
||||||
|
delete sessionToken;
|
||||||
|
delete tokenId;
|
||||||
|
delete requestHMACKey;
|
||||||
|
delete requestKey;
|
||||||
|
delete tokenIdHex;
|
||||||
|
delete data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncManager::callback_uploadDevice()
|
||||||
|
{
|
||||||
|
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||||
|
qDebug() << "Received reply";
|
||||||
|
if (!reply) {
|
||||||
|
qDebug()<< "No reply";
|
||||||
|
m_syncStep = enum_ERROR;
|
||||||
|
sync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
qWarning() << "Error in uploading Device Name to Firefox Sync.";
|
||||||
|
qDebug() << reply->readAll();
|
||||||
|
reply->close();
|
||||||
|
reply->deleteLater();
|
||||||
|
m_syncStep = enum_ERROR;
|
||||||
|
sync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray response(reply->readAll().data());
|
||||||
|
qDebug() << "account/device response:\n" << response;
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(response);
|
||||||
|
QJsonObject obj = doc.object();
|
||||||
|
|
||||||
|
if (obj.contains(QString("id"))) {
|
||||||
|
QString id = obj.value(QString("id")).toString();
|
||||||
|
qDebug() << "id of device:" << id;
|
||||||
|
m_syncCreds->addSyncCredentials(QString("DeviceId"), id);
|
||||||
|
m_syncStep = enum_FetchBrowserId;
|
||||||
|
} else {
|
||||||
|
qWarning() << "Invalid Upload Device Response Recieved.";
|
||||||
|
m_syncStep = enum_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
sync();
|
||||||
|
}
|
||||||
|
|
||||||
QNetworkRequest SyncManager::createHawkGetRequest(QString endpoint, QByteArray* id, QByteArray* key, size_t keyLen)
|
QNetworkRequest SyncManager::createHawkGetRequest(QString endpoint, QByteArray* id, QByteArray* key, size_t keyLen)
|
||||||
{
|
{
|
||||||
|
|
|
@ -58,6 +58,15 @@ class SyncManager : public QObject
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum SyncStep {
|
||||||
|
enum_ERROR = 0,
|
||||||
|
enum_NoRequestPending = 1,
|
||||||
|
enum_FetchBrowserId = 2,
|
||||||
|
enum_UploadDevice = 3,
|
||||||
|
enum_TradeBrowserIdAssertion = 4,
|
||||||
|
enum_FetchAccountKeys = 5
|
||||||
|
};
|
||||||
|
|
||||||
explicit SyncManager(QObject *parent = nullptr);
|
explicit SyncManager(QObject *parent = nullptr);
|
||||||
~SyncManager();
|
~SyncManager();
|
||||||
void sync();
|
void sync();
|
||||||
|
@ -67,11 +76,15 @@ public Q_SLOTS:
|
||||||
void startSync();
|
void startSync();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void recievedBrowserSignedCertificate();
|
void callback_getBrowserSignedCertificate();
|
||||||
|
void callback_getCryptoKeys();
|
||||||
|
void callback_uploadDevice();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool getBrowserSignedCertificate();
|
void getBrowserSignedCertificate();
|
||||||
bool getCryptoKeys();
|
void tradeBrowserIdAssertion();
|
||||||
|
void getCryptoKeys();
|
||||||
|
void uploadDevice();
|
||||||
|
|
||||||
bool verifyBrowserSignedCertificate(QByteArray *certificate);
|
bool verifyBrowserSignedCertificate(QByteArray *certificate);
|
||||||
|
|
||||||
|
@ -88,4 +101,8 @@ private:
|
||||||
SyncCredentials *m_syncCreds;
|
SyncCredentials *m_syncCreds;
|
||||||
SyncState *m_syncState;
|
SyncState *m_syncState;
|
||||||
RSAKeyPair *m_keyPair;
|
RSAKeyPair *m_keyPair;
|
||||||
|
|
||||||
|
QByteArray *m_browserCertificate;
|
||||||
|
|
||||||
|
uint m_syncStep = enum_FetchBrowserId;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
/* ============================================================
|
|
||||||
* Falkon - Qt web browser
|
|
||||||
* Copyright (C) 2019 Prasenjit Kumar Shaw <shawprasenjit07@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
* ============================================================ */
|
|
||||||
|
|
||||||
#include "syncrequest.h"
|
|
||||||
|
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QNetworkRequest>
|
|
||||||
#include <QQueue>
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
SyncRequestManager::SyncRequestManager(QObject* parent)
|
|
||||||
: QObject(parent)
|
|
||||||
{
|
|
||||||
m_requestManager = new QNetworkAccessManager(this);
|
|
||||||
m_requestQueue = new QQueue<RequestPair>();
|
|
||||||
startRequests = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SyncRequestManager::addRequest(QNetworkRequest *request, bool post)
|
|
||||||
{
|
|
||||||
RequestPair temp = {*request, post};
|
|
||||||
m_requestQueue->enqueue(temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SyncRequestManager::startSync(bool start)
|
|
||||||
{
|
|
||||||
startRequests = start;
|
|
||||||
if (startRequests) {
|
|
||||||
if (!m_requestQueue->isEmpty()) {
|
|
||||||
RequestPair req = m_requestQueue->dequeue();
|
|
||||||
QNetworkRequest request = req.request;
|
|
||||||
QNetworkReply *reply = nullptr;
|
|
||||||
if (req.post) {
|
|
||||||
reply = m_requestManager->get(request);
|
|
||||||
} else {
|
|
||||||
reply = m_requestManager->get(request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
40
src/lib/sync/syncutils.cpp
Normal file
40
src/lib/sync/syncutils.cpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/* ============================================================
|
||||||
|
* Falkon - Qt web browser
|
||||||
|
* Copyright (C) 2019 Prasenjit Kumar Shaw <shawprasenjit07@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
#include "syncutils.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <qzcommon.h>
|
||||||
|
|
||||||
|
QString getAudience(QUrl url)
|
||||||
|
{
|
||||||
|
QString host = url.host();
|
||||||
|
QString scheme = url.scheme();
|
||||||
|
|
||||||
|
qint64 port = url.port(0);
|
||||||
|
|
||||||
|
QString audience;
|
||||||
|
if(port == 0) {
|
||||||
|
audience = QSL("%s://%s").arg(scheme).arg(host);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
audience = QSL("%s://%s:%s").arg(scheme).arg(host).arg(port);
|
||||||
|
}
|
||||||
|
return audience;
|
||||||
|
}
|
|
@ -15,31 +15,9 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QNetworkAccessManager>
|
#include <QString>
|
||||||
#include <QNetworkReply>
|
#include <QUrl>
|
||||||
#include <QNetworkRequest>
|
|
||||||
#include <QQueue>
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
class SyncRequestManager : public QObject
|
QString getAudience(QUrl url);
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit SyncRequestManager(QObject *parent = nullptr);
|
|
||||||
void addRequest(QNetworkRequest *request, bool post);
|
|
||||||
void startSync(bool start);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct RequestPair {
|
|
||||||
QNetworkRequest request;
|
|
||||||
bool post;
|
|
||||||
};
|
|
||||||
|
|
||||||
QNetworkAccessManager *m_requestManager;
|
|
||||||
QQueue<RequestPair> *m_requestQueue;
|
|
||||||
bool startRequests;
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user