Make RSA key-pair class a Runnable that's owned by a threadpool

This commit is contained in:
Simon Walton 2018-05-30 13:41:08 -07:00
parent 66d39667e1
commit 8fb5d1b92d
4 changed files with 61 additions and 101 deletions

View file

@ -22,6 +22,7 @@
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <QtCore/QStandardPaths> #include <QtCore/QStandardPaths>
#include <QtCore/QUrlQuery> #include <QtCore/QUrlQuery>
#include <QtCore/QThreadPool>
#include <QtNetwork/QHttpMultiPart> #include <QtNetwork/QHttpMultiPart>
#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkRequest>
#include <qthread.h> #include <qthread.h>
@ -94,10 +95,6 @@ const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash";
const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner"; const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner";
AccountManager::~AccountManager() { AccountManager::~AccountManager() {
QMutexLocker lock(&_rsaKeygenLock);
while (_rsaKeygenThread) {
_rsaKeygenWait.wait(&_rsaKeygenLock);
}
} }
void AccountManager::logout() { void AccountManager::logout() {
@ -747,96 +744,75 @@ void AccountManager::generateNewKeypair(bool isUserKeypair, const QUuid& domainI
qCDebug(networking) << "Clearing current private key in DataServerAccountInfo"; qCDebug(networking) << "Clearing current private key in DataServerAccountInfo";
_accountInfo.setPrivateKey(QByteArray()); _accountInfo.setPrivateKey(QByteArray());
// setup a new QThread to generate the keypair on, in case it takes a while // Create a runnable keypair generated to create an RSA pair and exit.
QThread* generateThread = new QThread(this);
generateThread->setObjectName("Account Manager Generator Thread");
// setup a keypair generator
RSAKeypairGenerator* keypairGenerator = new RSAKeypairGenerator; RSAKeypairGenerator* keypairGenerator = new RSAKeypairGenerator;
if (!isUserKeypair) { if (!isUserKeypair) {
keypairGenerator->setDomainID(domainID);
_accountInfo.setDomainID(domainID); _accountInfo.setDomainID(domainID);
} }
// start keypair generation when the thread starts
connect(generateThread, &QThread::started, keypairGenerator, &RSAKeypairGenerator::generateKeypair);
// handle success or failure of keypair generation // handle success or failure of keypair generation
connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this, &AccountManager::processGeneratedKeypair); connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this,
connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair, &AccountManager::processGeneratedKeypair);
this, &AccountManager::handleKeypairGenerationError); connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair, this,
&AccountManager::handleKeypairGenerationError);
connect(keypairGenerator, &QObject::destroyed, generateThread, &QThread::quit);
connect(generateThread, &QThread::finished, this, &AccountManager::rsaKeygenThreadFinished);
connect(generateThread, &QThread::finished, generateThread, &QThread::deleteLater);
_rsaKeygenThread = generateThread;
keypairGenerator->moveToThread(generateThread);
qCDebug(networking) << "Starting worker thread to generate 2048-bit RSA keypair."; qCDebug(networking) << "Starting worker thread to generate 2048-bit RSA keypair.";
generateThread->start(); // Start on Qt's global thread pool.
QThreadPool::globalInstance()->start(keypairGenerator);
} }
} }
void AccountManager::processGeneratedKeypair() { void AccountManager::processGeneratedKeypair(QByteArray publicKey, QByteArray privateKey) {
qCDebug(networking) << "Generated 2048-bit RSA keypair. Uploading public key now."; qCDebug(networking) << "Generated 2048-bit RSA keypair. Uploading public key now.";
RSAKeypairGenerator* keypairGenerator = qobject_cast<RSAKeypairGenerator*>(sender()); // hold the private key to later set our metaverse API account info if upload succeeds
_pendingPrivateKey = privateKey;
if (keypairGenerator) { // upload the public key so data-web has an up-to-date key
// hold the private key to later set our metaverse API account info if upload succeeds const QString USER_PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key";
_pendingPrivateKey = keypairGenerator->getPrivateKey(); const QString DOMAIN_PUBLIC_KEY_UPDATE_PATH = "api/v1/domains/%1/public_key";
// upload the public key so data-web has an up-to-date key QString uploadPath;
const QString USER_PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key"; const auto& domainID = _accountInfo.getDomainID();
const QString DOMAIN_PUBLIC_KEY_UPDATE_PATH = "api/v1/domains/%1/public_key"; if (domainID.isNull()) {
uploadPath = USER_PUBLIC_KEY_UPDATE_PATH;
QString uploadPath;
const auto& domainID = keypairGenerator->getDomainID();
if (domainID.isNull()) {
uploadPath = USER_PUBLIC_KEY_UPDATE_PATH;
} else {
uploadPath = DOMAIN_PUBLIC_KEY_UPDATE_PATH.arg(uuidStringWithoutCurlyBraces(domainID));
}
// setup a multipart upload to send up the public key
QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart publicKeyPart;
publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
publicKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"public_key\"; filename=\"public_key\""));
publicKeyPart.setBody(keypairGenerator->getPublicKey());
requestMultiPart->append(publicKeyPart);
if (!domainID.isNull()) {
const auto& key = getTemporaryDomainKey(domainID);
QHttpPart apiKeyPart;
publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"api_key\""));
apiKeyPart.setBody(key.toUtf8());
requestMultiPart->append(apiKeyPart);
}
// setup callback parameters so we know once the keypair upload has succeeded or failed
JSONCallbackParameters callbackParameters;
callbackParameters.jsonCallbackReceiver = this;
callbackParameters.jsonCallbackMethod = "publicKeyUploadSucceeded";
callbackParameters.errorCallbackReceiver = this;
callbackParameters.errorCallbackMethod = "publicKeyUploadFailed";
sendRequest(uploadPath, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation,
callbackParameters, QByteArray(), requestMultiPart);
keypairGenerator->deleteLater();
} else { } else {
qCWarning(networking) << "Expected processGeneratedKeypair to be called by a live RSAKeypairGenerator" uploadPath = DOMAIN_PUBLIC_KEY_UPDATE_PATH.arg(uuidStringWithoutCurlyBraces(domainID));
<< "but the casted sender is NULL. Will not process generated keypair.";
} }
// setup a multipart upload to send up the public key
QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart publicKeyPart;
publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
publicKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"public_key\"; filename=\"public_key\""));
publicKeyPart.setBody(publicKey);
requestMultiPart->append(publicKeyPart);
// Currently broken? We don't have the temporary domain key.
if (!domainID.isNull()) {
const auto& key = getTemporaryDomainKey(domainID);
QHttpPart apiKeyPart;
publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"api_key\""));
apiKeyPart.setBody(key.toUtf8());
requestMultiPart->append(apiKeyPart);
}
// setup callback parameters so we know once the keypair upload has succeeded or failed
JSONCallbackParameters callbackParameters;
callbackParameters.jsonCallbackReceiver = this;
callbackParameters.jsonCallbackMethod = "publicKeyUploadSucceeded";
callbackParameters.errorCallbackReceiver = this;
callbackParameters.errorCallbackMethod = "publicKeyUploadFailed";
sendRequest(uploadPath, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation,
callbackParameters, QByteArray(), requestMultiPart);
} }
void AccountManager::publicKeyUploadSucceeded(QNetworkReply& reply) { void AccountManager::publicKeyUploadSucceeded(QNetworkReply& reply) {
@ -875,12 +851,4 @@ void AccountManager::handleKeypairGenerationError() {
// reset our waiting state for keypair response // reset our waiting state for keypair response
_isWaitingForKeypairResponse = false; _isWaitingForKeypairResponse = false;
sender()->deleteLater();
}
void AccountManager::rsaKeygenThreadFinished() {
QMutexLocker lock(&_rsaKeygenLock);
_rsaKeygenThread = nullptr;
_rsaKeygenWait.wakeAll();
} }

View file

@ -15,8 +15,6 @@
#include <QtCore/QByteArray> #include <QtCore/QByteArray>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QUrl> #include <QtCore/QUrl>
#include <QtCore/QMutex>
#include <QtCore/QWaitCondition>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <QUrlQuery> #include <QUrlQuery>
@ -130,11 +128,10 @@ signals:
private slots: private slots:
void processReply(); void processReply();
void handleKeypairGenerationError(); void handleKeypairGenerationError();
void processGeneratedKeypair(); void processGeneratedKeypair(QByteArray publicKey, QByteArray privateKey);
void publicKeyUploadSucceeded(QNetworkReply& reply); void publicKeyUploadSucceeded(QNetworkReply& reply);
void publicKeyUploadFailed(QNetworkReply& reply); void publicKeyUploadFailed(QNetworkReply& reply);
void generateNewKeypair(bool isUserKeypair = true, const QUuid& domainID = QUuid()); void generateNewKeypair(bool isUserKeypair = true, const QUuid& domainID = QUuid());
void rsaKeygenThreadFinished();
private: private:
AccountManager(AccountManager const& other) = delete; AccountManager(AccountManager const& other) = delete;
@ -160,9 +157,6 @@ private:
QByteArray _pendingPrivateKey; QByteArray _pendingPrivateKey;
QUuid _sessionID { QUuid::createUuid() }; QUuid _sessionID { QUuid::createUuid() };
QMutex _rsaKeygenLock;
QWaitCondition _rsaKeygenWait;
QThread* _rsaKeygenThread { nullptr };
}; };
#endif // hifi_AccountManager_h #endif // hifi_AccountManager_h

View file

@ -25,7 +25,10 @@
RSAKeypairGenerator::RSAKeypairGenerator(QObject* parent) : RSAKeypairGenerator::RSAKeypairGenerator(QObject* parent) :
QObject(parent) QObject(parent)
{ {
}
void RSAKeypairGenerator::run() {
generateKeypair();
} }
void RSAKeypairGenerator::generateKeypair() { void RSAKeypairGenerator::generateKeypair() {
@ -92,5 +95,5 @@ void RSAKeypairGenerator::generateKeypair() {
OPENSSL_free(publicKeyDER); OPENSSL_free(publicKeyDER);
OPENSSL_free(privateKeyDER); OPENSSL_free(privateKeyDER);
emit generatedKeypair(); emit generatedKeypair(_publicKey, _privateKey);
} }

View file

@ -13,25 +13,20 @@
#define hifi_RSAKeypairGenerator_h #define hifi_RSAKeypairGenerator_h
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QRunnable>
#include <QtCore/QUuid> #include <QtCore/QUuid>
class RSAKeypairGenerator : public QObject { class RSAKeypairGenerator : public QObject, public QRunnable {
Q_OBJECT Q_OBJECT
public: public:
RSAKeypairGenerator(QObject* parent = 0); RSAKeypairGenerator(QObject* parent = nullptr);
void setDomainID(const QUuid& domainID) { _domainID = domainID; } virtual void run() override;
const QUuid& getDomainID() const { return _domainID; }
const QByteArray& getPublicKey() const { return _publicKey; }
const QByteArray& getPrivateKey() const { return _privateKey; }
public slots:
void generateKeypair(); void generateKeypair();
signals: signals:
void errorGeneratingKeypair(); void errorGeneratingKeypair();
void generatedKeypair(); void generatedKeypair(QByteArray publicKey, QByteArray privateKey);
private: private:
QUuid _domainID; QUuid _domainID;