diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 42af8ff1b9..d0a17287cb 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -861,8 +861,6 @@ void OctreeServer::readPendingDatagram(const QByteArray& receivedPacket, const H } } else if (packetType == PacketTypeJurisdictionRequest) { _jurisdictionSender->queueReceivedPacket(matchingNode, receivedPacket); - } else if (packetType == PacketTypeSignedTransactionPayment) { - handleSignedTransactionPayment(packetType, receivedPacket); } else if (_octreeInboundPacketProcessor && getOctree()->handlesEditPacketType(packetType)) { _octreeInboundPacketProcessor->queueReceivedPacket(matchingNode, receivedPacket); } else { @@ -1245,51 +1243,6 @@ QString OctreeServer::getStatusLink() { return result; } -void OctreeServer::handleSignedTransactionPayment(PacketType packetType, const QByteArray& datagram) { - // for now we're not verifying that this is actual payment for any octree edits - // just use the AccountManager to send it up to the data server and have it redeemed - AccountManager& accountManager = AccountManager::getInstance(); - - const int NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE = 72; - const int NUM_BYTES_SIGNED_TRANSACTION_BINARY_SIGNATURE = 256; - - int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(packetType); - - // pull out the transaction message in binary - QByteArray messageHex = datagram.mid(numBytesPacketHeader, NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE).toHex(); - // pull out the binary signed message digest - QByteArray signatureHex = datagram.mid(numBytesPacketHeader + NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE, - NUM_BYTES_SIGNED_TRANSACTION_BINARY_SIGNATURE).toHex(); - - // setup the QJSONObject we are posting - QJsonObject postObject; - - const QString TRANSACTION_OBJECT_MESSAGE_KEY = "message"; - const QString TRANSACTION_OBJECT_SIGNATURE_KEY = "signature"; - const QString POST_OBJECT_TRANSACTION_KEY = "transaction"; - - QJsonObject transactionObject; - transactionObject.insert(TRANSACTION_OBJECT_MESSAGE_KEY, QString(messageHex)); - transactionObject.insert(TRANSACTION_OBJECT_SIGNATURE_KEY, QString(signatureHex)); - - postObject.insert(POST_OBJECT_TRANSACTION_KEY, transactionObject); - - // setup our callback params - JSONCallbackParameters callbackParameters; - callbackParameters.jsonCallbackReceiver = this; - callbackParameters.jsonCallbackMethod = "handleSignedTransactionPaymentResponse"; - - accountManager.unauthenticatedRequest("/api/v1/transactions/redeem", QNetworkAccessManager::PostOperation, - callbackParameters, QJsonDocument(postObject).toJson()); - -} - -void OctreeServer::handleSignedTransactionPaymentResponse(const QJsonObject& jsonObject) { - // pull the ID to debug the transaction - QString transactionIDString = jsonObject["data"].toObject()["transaction"].toObject()["id"].toString(); - qDebug() << "Redeemed transaction with ID" << transactionIDString << "successfully."; -} - void OctreeServer::sendStatsPacket() { // TODO: we have too many stats to fit in a single MTU... so for now, we break it into multiple JSON objects and // send them separately. What we really should do is change the NodeList::sendStatsToDomainServer() to handle the diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index aa0c419dd4..2fcaae8c78 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -127,8 +127,6 @@ public slots: void nodeKilled(SharedNodePointer node); void sendStatsPacket(); - void handleSignedTransactionPaymentResponse(const QJsonObject& jsonObject); - void readPendingDatagrams() { }; // this will not be called since our datagram processing thread will handle void readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); @@ -141,7 +139,6 @@ protected: QString getConfiguration(); QString getStatusLink(); - void handleSignedTransactionPayment(PacketType packetType, const QByteArray& datagram); void setupDatagramProcessingThread(); int _argc; diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index 17d8b61b11..9090a1b637 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -38,4 +38,18 @@ endif () # link the shared hifi libraries link_hifi_libraries(embedded-webserver networking shared) +# find OpenSSL +find_package(OpenSSL REQUIRED) + +if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") + # this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto + message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." + "\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") +endif () + +include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") + +# append OpenSSL to our list of libraries to link +list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${OPENSSL_LIBRARIES}") + link_shared_dependencies() \ No newline at end of file diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 38df52c8eb..3a1fbd4526 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -59,6 +59,20 @@ "type": "password", "help": "Password used for basic HTTP authentication. Leave this blank if you do not want to change it.", "value-hidden": true + }, + { + "name": "allowed_users", + "type": "table", + "label": "Allowed Users", + "help": "List the High Fidelity names for people you want to be able to connect to this domain.
An empty list means everyone.
You can always connect from this machine.", + "numbered": false, + "columns": [ + { + "name": "username", + "label": "Username", + "can_set": true + } + ] } ] }, diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 2b8b42d029..3b7e449d4c 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -259,7 +259,7 @@ function makeTable(setting, setting_name, setting_value) { html += "" + col.label + "" // Data }) - html += "+/-" + html += "" // populate rows in the table from existing values var row_num = 1 @@ -279,13 +279,13 @@ function makeTable(setting, setting_name, setting_value) { html += "" if (isArray) { - colIsArray = _.isArray(row) - colValue = colIsArray ? row : row[col.name] + rowIsObject = setting.columns.length > 1 + colValue = rowIsObject ? row[col.name] : row html += colValue // for arrays we add a hidden input to this td so that values can be posted appropriately html += "" + + (rowIsObject ? "." + col.name : "") + "' value='" + colValue + "'/>" } else if (row.hasOwnProperty(col.name)) { html += row[col.name] } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index fa7a0fe012..bbfa01c709 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -9,6 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include + #include #include #include @@ -44,8 +47,6 @@ DomainServer::DomainServer(int argc, char* argv[]) : _oauthProviderURL(), _oauthClientID(), _hostname(), - _networkReplyUUIDMap(), - _sessionAuthenticationHash(), _webAuthenticationStateSet(), _cookieSessionHash(), _settingsManager() @@ -80,6 +81,9 @@ DomainServer::DomainServer(int argc, char* argv[]) : // setup automatic networking settings with data server setupAutomaticNetworking(); + + // preload some user public keys so they can connect on first request + preloadAllowedUserPublicKeys(); } } @@ -507,8 +511,6 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSetwriteUnverifiedDatagram(oauthRequestByteArray, senderSockAddr); - - return; - } + QList nodeInterestList; + QString username; + QByteArray usernameSignature; + + packetStream >> nodeInterestList >> username >> usernameSignature; + + if (!isAssignment && !shouldAllowConnectionFromNode(username, usernameSignature, senderSockAddr)) { + // this is an agent and we've decided we won't let them connect - send them a packet to deny connection + QByteArray usernameRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainConnectionDenied); + + // send this oauth request datagram back to the client + LimitedNodeList::getInstance()->writeUnverifiedDatagram(usernameRequestByteArray, senderSockAddr); + + return; } if ((!isAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) @@ -610,15 +602,109 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock } // if we have a username from an OAuth connect request, set it on the DomainServerNodeData - nodeData->setUsername(connectedUsername); - + nodeData->setUsername(username); nodeData->setSendingSockAddr(senderSockAddr); // reply back to the user with a PacketTypeDomainList - sendDomainListToNode(newNode, senderSockAddr, nodeInterestListFromPacket(packet, numPreInterestBytes)); + sendDomainListToNode(newNode, senderSockAddr, nodeInterestList.toSet()); } } +const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users"; + +bool DomainServer::shouldAllowConnectionFromNode(const QString& username, + const QByteArray& usernameSignature, + const HifiSockAddr& senderSockAddr) { + static const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(), + ALLOWED_USERS_SETTINGS_KEYPATH); + static QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList(); + + if (allowedUsers.count() > 0) { + // this is an agent, we need to ask them to provide us with their signed username to see if they are allowed in + // we always let in a user who is sending a packet from our local socket or from the localhost address + + if (senderSockAddr.getAddress() != LimitedNodeList::getInstance()->getLocalSockAddr().getAddress() + && senderSockAddr.getAddress() != QHostAddress::LocalHost) { + if (allowedUsers.contains(username)) { + // it's possible this user can be allowed to connect, but we need to check their username signature + + QByteArray publicKeyArray = _userPublicKeys.value(username); + if (!publicKeyArray.isEmpty()) { + // if we do have a public key for the user, check for a signature match + + const unsigned char* publicKeyData = reinterpret_cast(publicKeyArray.constData()); + + // first load up the public key into an RSA struct + RSA* rsaPublicKey = d2i_RSAPublicKey(NULL, &publicKeyData, publicKeyArray.size()); + + if (rsaPublicKey) { + QByteArray decryptedArray(RSA_size(rsaPublicKey), 0); + int decryptResult = RSA_public_decrypt(usernameSignature.size(), + reinterpret_cast(usernameSignature.constData()), + reinterpret_cast(decryptedArray.data()), + rsaPublicKey, RSA_PKCS1_PADDING); + + if (decryptResult != -1) { + if (username == decryptedArray) { + qDebug() << "Username signature matches for" << username << "- allowing connection."; + + // free up the public key before we return + RSA_free(rsaPublicKey); + + return true; + } else { + qDebug() << "Username signature did not match for" << username << "- denying connection."; + } + } else { + qDebug() << "Couldn't decrypt user signature for" << username << "- denying connection."; + } + + // free up the public key, we don't need it anymore + RSA_free(rsaPublicKey); + } else { + // we can't let this user in since we couldn't convert their public key to an RSA key we could use + qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection."; + } + } + + requestUserPublicKey(username); + } + } + } else { + // since we have no allowed user list, let them all in + return true; + } + + return false; +} + +void DomainServer::preloadAllowedUserPublicKeys() { + const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_USERS_SETTINGS_KEYPATH); + QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList(); + + if (allowedUsers.size() > 0) { + // in the future we may need to limit how many requests here - for now assume that lists of allowed users are not + // going to create > 100 requests + foreach(const QString& username, allowedUsers) { + requestUserPublicKey(username); + } + } +} + +void DomainServer::requestUserPublicKey(const QString& username) { + // even if we have a public key for them right now, request a new one in case it has just changed + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "publicKeyJSONCallback"; + + const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key"; + + qDebug() << "Requesting public key for user" << username; + + AccountManager::getInstance().unauthenticatedRequest(USER_PUBLIC_KEY_PATH.arg(username), + QNetworkAccessManager::GetOperation, callbackParams); +} + QUrl DomainServer::oauthRedirectURL() { return QString("https://%1:%2/oauth").arg(_hostname).arg(_httpsManager->serverPort()); } @@ -653,12 +739,9 @@ QUrl DomainServer::oauthAuthorizationURL(const QUuid& stateUUID) { return authorizationURL; } -int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr, - HifiSockAddr& localSockAddr, const QByteArray& packet, - const HifiSockAddr& senderSockAddr) { - QDataStream packetStream(packet); - packetStream.skipRawData(numBytesForPacketHeader(packet)); - +int DomainServer::parseNodeDataFromByteArray(QDataStream& packetStream, NodeType_t& nodeType, + HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr, + const HifiSockAddr& senderSockAddr) { packetStream >> nodeType; packetStream >> publicSockAddr >> localSockAddr; @@ -925,7 +1008,30 @@ void DomainServer::sendPendingTransactionsToServer() { ++i; } } +} +void DomainServer::publicKeyJSONCallback(QNetworkReply& requestReply) { + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + + if (jsonObject["status"].toString() == "success") { + // figure out which user this is for + + const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key"; + QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING); + + if (usernameRegex.indexIn(requestReply.url().toString()) != -1) { + QString username = usernameRegex.cap(1); + + qDebug() << "Storing a public key for user" << username; + + // pull the public key as a QByteArray from this response + const QString JSON_DATA_KEY = "data"; + const QString JSON_PUBLIC_KEY_KEY = "public_key"; + + _userPublicKeys[username] = + QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8()); + } + } } void DomainServer::transactionJSONCallback(const QJsonObject& data) { @@ -1095,9 +1201,13 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS if (!nodeUUID.isNull() && nodeList->nodeWithUUID(nodeUUID)) { NodeType_t throwawayNodeType; HifiSockAddr nodePublicAddress, nodeLocalAddress; + + QDataStream packetStream(receivedPacket); + packetStream.skipRawData(numBytesForPacketHeader(receivedPacket)); - int numNodeInfoBytes = parseNodeDataFromByteArray(throwawayNodeType, nodePublicAddress, nodeLocalAddress, - receivedPacket, senderSockAddr); + int numNodeInfoBytes = parseNodeDataFromByteArray(packetStream, throwawayNodeType, + nodePublicAddress, nodeLocalAddress, + senderSockAddr); SharedNodePointer checkInNode = nodeList->updateSocketsForNode(nodeUUID, nodePublicAddress, nodeLocalAddress); @@ -1545,13 +1655,6 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u // we've redirected the user back to our homepage return true; - } else { - qDebug() << "Requesting a token for user with session UUID" << uuidStringWithoutCurlyBraces(stateUUID); - - // insert this to our pending token replies so we can associate the returned access token with the right UUID - _networkReplyUUIDMap.insert(tokenReply, stateUUID); - - connect(tokenReply, &QNetworkReply::finished, this, &DomainServer::handleTokenRequestFinished); } } @@ -1695,22 +1798,6 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl const QString OAUTH_JSON_ACCESS_TOKEN_KEY = "access_token"; -void DomainServer::handleTokenRequestFinished() { - QNetworkReply* networkReply = reinterpret_cast(sender()); - QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); - - if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) { - - qDebug() << "Received access token for user with UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID) - << "-" << "requesting profile."; - - QNetworkReply* profileReply = profileRequestGivenTokenReply(networkReply); - - connect(profileReply, &QNetworkReply::finished, this, &DomainServer::handleProfileRequestFinished); - - _networkReplyUUIDMap.insert(profileReply, matchingSessionUUID); - } -} QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenReply) { // pull the access token from the returned JSON and store it with the matching session UUID @@ -1719,54 +1806,12 @@ QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenR // fire off a request to get this user's identity so we can see if we will let them in QUrl profileURL = _oauthProviderURL; - profileURL.setPath("/api/v1/users/profile"); + profileURL.setPath("/api/v1/user/profile"); profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken)); return NetworkAccessManager::getInstance().get(QNetworkRequest(profileURL)); } -void DomainServer::handleProfileRequestFinished() { - QNetworkReply* networkReply = reinterpret_cast(sender()); - QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); - - if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) { - QJsonDocument profileJSON = QJsonDocument::fromJson(networkReply->readAll()); - - if (profileJSON.object()["status"].toString() == "success") { - // pull the user roles from the response - QJsonArray userRolesArray = profileJSON.object()["data"].toObject()["user"].toObject()["roles"].toArray(); - - QStringList allowedRolesArray = _settingsManager.getSettingsMap().value(ALLOWED_ROLES_CONFIG_KEY).toStringList(); - - QString connectableUsername; - QString profileUsername = profileJSON.object()["data"].toObject()["user"].toObject()["username"].toString(); - - foreach(const QJsonValue& roleValue, userRolesArray) { - if (allowedRolesArray.contains(roleValue.toString())) { - // the user has a role that lets them in - // set the bool to true and break - connectableUsername = profileUsername; - break; - } - } - - if (connectableUsername.isEmpty()) { - qDebug() << "User" << profileUsername << "with session UUID" - << uuidStringWithoutCurlyBraces(matchingSessionUUID) - << "does not have an allowable role. Refusing connection."; - } else { - qDebug() << "User" << profileUsername << "with session UUID" - << uuidStringWithoutCurlyBraces(matchingSessionUUID) - << "has an allowable role. Can connect."; - } - - // insert this UUID and a flag that indicates if they are allowed to connect - _sessionAuthenticationHash.insert(matchingSessionUUID, connectableUsername); - } - } -} - - const QString DS_SETTINGS_SESSIONS_GROUP = "web-sessions"; Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileReply) { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 0ad2aae8a8..5e4da00601 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -52,6 +52,7 @@ public slots: /// Called by NodeList to inform us a node has been killed void nodeKilled(SharedNodePointer node); + void publicKeyJSONCallback(QNetworkReply& requestReply); void transactionJSONCallback(const QJsonObject& data); void restart(); @@ -82,8 +83,17 @@ private: void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); void handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr); - int parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr, - HifiSockAddr& localSockAddr, const QByteArray& packet, const HifiSockAddr& senderSockAddr); + bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, + const HifiSockAddr& senderSockAddr); + + void preloadAllowedUserPublicKeys(); + void requestUserPublicKey(const QString& username); + + int parseNodeDataFromByteArray(QDataStream& packetStream, + NodeType_t& nodeType, + HifiSockAddr& publicSockAddr, + HifiSockAddr& localSockAddr, + const HifiSockAddr& senderSockAddr); NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes); void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr, const NodeSet& nodeInterestList); @@ -131,13 +141,11 @@ private: QString _oauthClientID; QString _oauthClientSecret; QString _hostname; - QMap _networkReplyUUIDMap; - QHash _sessionAuthenticationHash; QSet _webAuthenticationStateSet; QHash _cookieSessionHash; - HifiSockAddr _localSockAddr; + QHash _userPublicKeys; QHash _connectingICEPeers; QHash _connectedICEPeers; diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index cab75aab0a..a85181b89e 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -105,7 +105,6 @@ link_hifi_libraries(shared octree voxels fbx metavoxels networking particles ent # find any optional and required libraries find_package(ZLIB REQUIRED) -find_package(OpenSSL REQUIRED) # perform standard include and linking for found externals foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) @@ -169,10 +168,9 @@ endif () # include headers for interface and InterfaceConfig. include_directories("${PROJECT_SOURCE_DIR}/src" "${PROJECT_BINARY_DIR}/includes") -include_directories("${OPENSSL_INCLUDE_DIR}") target_link_libraries( - ${TARGET_NAME} ${ZLIB_LIBRARIES} ${OPENSSL_LIBRARIES} + ${TARGET_NAME} ${ZLIB_LIBRARIES} Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL Qt5::Script Qt5::Svg Qt5::WebKitWidgets ) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b7da4903e3..b85e1e4440 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -72,7 +72,6 @@ #include "InterfaceVersion.h" #include "Menu.h" #include "ModelUploader.h" -#include "PaymentManager.h" #include "Util.h" #include "devices/MIDIManager.h" #include "devices/OculusManager.h" @@ -90,7 +89,6 @@ #include "scripting/WindowScriptingInterface.h" #include "ui/InfoView.h" -#include "ui/OAuthWebViewHandler.h" #include "ui/Snapshot.h" #include "ui/Stats.h" #include "ui/TextRenderer.h" @@ -220,10 +218,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : listenPort = atoi(portStr); } - // call the OAuthWebviewHandler static getter so that its instance lives in our thread - // make sure it is ready before the NodeList might need it - OAuthWebViewHandler::getInstance(); - // start the nodeThread so its event loop is running _nodeThread->start(); @@ -256,11 +250,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); connect(&domainHandler, &DomainHandler::hostnameChanged, Menu::getInstance(), &Menu::clearLoginDialogDisplayedFlag); - // hookup VoxelEditSender to PaymentManager so we can pay for octree edits - const PaymentManager& paymentManager = PaymentManager::getInstance(); - connect(&_voxelEditSender, &VoxelEditPacketSender::octreePaymentRequired, - &paymentManager, &PaymentManager::sendSignedPayment); - // update our location every 5 seconds in the data-server, assuming that we are authenticated with one const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; @@ -3483,7 +3472,7 @@ void Application::updateLocationInServer() { rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject); - accountManager.authenticatedRequest("/api/v1/users/location", QNetworkAccessManager::PutOperation, + accountManager.authenticatedRequest("/api/v1/user/location", QNetworkAccessManager::PutOperation, JSONCallbackParameters(), QJsonDocument(rootObject).toJson()); } } @@ -3526,20 +3515,18 @@ void Application::domainChanged(const QString& domainHostname) { // reset the voxels renderer _voxels.killLocalVoxels(); - - // reset the auth URL for OAuth web view handler - OAuthWebViewHandler::getInstance().clearLastAuthorizationURL(); } void Application::connectedToDomain(const QString& hostname) { AccountManager& accountManager = AccountManager::getInstance(); + const QUuid& domainID = NodeList::getInstance()->getDomainHandler().getUUID(); + + if (accountManager.isLoggedIn() && !domainID.isNull()) { + // update our data-server with the domain-server we're logged in with - if (accountManager.isLoggedIn()) { - // update our domain-server with the data-server we're logged in with + QString domainPutJsonString = "{\"location\":{\"domain_id\":\"" + uuidStringWithoutCurlyBraces(domainID) + "\"}}"; - QString domainPutJsonString = "{\"address\":{\"domain\":\"" + hostname + "\"}}"; - - accountManager.authenticatedRequest("/api/v1/users/address", QNetworkAccessManager::PutOperation, + accountManager.authenticatedRequest("/api/v1/user/location", QNetworkAccessManager::PutOperation, JSONCallbackParameters(), domainPutJsonString.toUtf8()); } } diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 0889007c76..0232e0e71d 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -11,11 +11,11 @@ #include +#include #include #include "Application.h" #include "Menu.h" -#include "ui/OAuthWebViewHandler.h" #include "DatagramProcessor.h" @@ -136,16 +136,11 @@ void DatagramProcessor::processDatagrams() { application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size()); break; } - case PacketTypeDomainOAuthRequest: { - QDataStream readStream(incomingPacket); - readStream.skipRawData(numBytesForPacketHeader(incomingPacket)); - - QUrl authorizationURL; - readStream >> authorizationURL; - - QMetaObject::invokeMethod(&OAuthWebViewHandler::getInstance(), "displayWebviewForAuthorizationURL", - Q_ARG(const QUrl&, authorizationURL)); - + case PacketTypeDomainConnectionDenied: { + // output to the log so the user knows they got a denied connection request + // and check and signal for an access token so that we can make sure they are logged in + qDebug() << "The domain-server denied a connection request."; + AccountManager::getInstance().checkAndSignalForAccessToken(); break; } case PacketTypeMuteEnvironment: { diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index f11d035ebe..7077c44287 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -364,7 +364,9 @@ void ModelUploader::send() { _progressBar = NULL; } -void ModelUploader::checkJSON(const QJsonObject& jsonResponse) { +void ModelUploader::checkJSON(QNetworkReply& requestReply) { + QJsonObject jsonResponse = QJsonDocument::fromJson(requestReply.readAll()).object(); + if (jsonResponse.contains("status") && jsonResponse.value("status").toString() == "success") { qDebug() << "status : success"; JSONCallbackParameters callbackParams; @@ -426,7 +428,7 @@ void ModelUploader::uploadUpdate(qint64 bytesSent, qint64 bytesTotal) { } } -void ModelUploader::uploadSuccess(const QJsonObject& jsonResponse) { +void ModelUploader::uploadSuccess(QNetworkReply& requestReply) { if (_progressDialog) { _progressDialog->accept(); } diff --git a/interface/src/ModelUploader.h b/interface/src/ModelUploader.h index a1c7a27393..7d8ad2b526 100644 --- a/interface/src/ModelUploader.h +++ b/interface/src/ModelUploader.h @@ -40,9 +40,9 @@ public slots: void send(); private slots: - void checkJSON(const QJsonObject& jsonResponse); + void checkJSON(QNetworkReply& requestReply); void uploadUpdate(qint64 bytesSent, qint64 bytesTotal); - void uploadSuccess(const QJsonObject& jsonResponse); + void uploadSuccess(QNetworkReply& requestReply); void uploadFailed(QNetworkReply& errorReply); void checkS3(); void processCheck(); diff --git a/interface/src/PaymentManager.cpp b/interface/src/PaymentManager.cpp deleted file mode 100644 index a0c05f34b3..0000000000 --- a/interface/src/PaymentManager.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// -// PaymentManager.cpp -// interface/src -// -// Created by Stephen Birarda on 2014-07-30. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include - -#include -#include -#include - -#include "SignedWalletTransaction.h" - -#include "PaymentManager.h" - -PaymentManager& PaymentManager::getInstance() { - static PaymentManager sharedInstance; - return sharedInstance; -} - -void PaymentManager::sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID) { - - QByteArray walletPrivateKeyByteArray = Menu::getInstance()->getWalletPrivateKey(); - - if (!walletPrivateKeyByteArray.isEmpty()) { - // setup a signed wallet transaction - const qint64 DEFAULT_TRANSACTION_EXPIRY_SECONDS = 60; - qint64 currentTimestamp = QDateTime::currentDateTimeUtc().toTime_t(); - SignedWalletTransaction newTransaction(destinationWalletUUID, satoshiAmount, - currentTimestamp, DEFAULT_TRANSACTION_EXPIRY_SECONDS); - - // send the signed transaction to the redeeming node - QByteArray transactionByteArray = byteArrayWithPopulatedHeader(PacketTypeSignedTransactionPayment); - - // append the binary message and the signed message digest - transactionByteArray.append(newTransaction.binaryMessage()); - QByteArray signedMessageDigest = newTransaction.signedMessageDigest(); - - if (!signedMessageDigest.isEmpty()) { - transactionByteArray.append(signedMessageDigest); - - qDebug() << "Paying" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID; - - // use the NodeList to send that to the right node - NodeList* nodeList = NodeList::getInstance(); - nodeList->writeDatagram(transactionByteArray, nodeList->nodeWithUUID(nodeUUID)); - } else { - qDebug() << "Payment of" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID << - "cannot be sent because there was an error signing the transaction."; - } - - } else { - qDebug() << "Payment of" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID << - "cannot be sent because there is no stored wallet private key."; - } -} \ No newline at end of file diff --git a/interface/src/PaymentManager.h b/interface/src/PaymentManager.h deleted file mode 100644 index 67419a39a4..0000000000 --- a/interface/src/PaymentManager.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// PaymentManager.h -// interface/src -// -// Created by Stephen Birarda on 2014-07-30. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_PaymentManager_h -#define hifi_PaymentManager_h - -#include - -class PaymentManager : public QObject { - Q_OBJECT -public: - static PaymentManager& getInstance(); -public slots: - void sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID); -}; - -#endif // hifi_PaymentManager_h \ No newline at end of file diff --git a/interface/src/SignedWalletTransaction.cpp b/interface/src/SignedWalletTransaction.cpp deleted file mode 100644 index 8a0d4e6fdb..0000000000 --- a/interface/src/SignedWalletTransaction.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// -// SignedWalletTransaction.cpp -// interface/src -// -// Created by Stephen Birarda on 2014-07-11. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include - -#include -#include -#include - -#include - -#include "Menu.h" - -#include "SignedWalletTransaction.h" - -SignedWalletTransaction::SignedWalletTransaction(const QUuid& destinationUUID, qint64 amount, - qint64 messageTimestamp, qint64 expiryDelta) : - WalletTransaction(destinationUUID, amount), - _messageTimestamp(messageTimestamp), - _expiryDelta(expiryDelta) -{ - -} - -QByteArray SignedWalletTransaction::binaryMessage() { - // build the message using the components of this transaction - - // UUID, source UUID, destination UUID, message timestamp, expiry delta, amount - QByteArray messageBinary; - - messageBinary.append(_uuid.toRfc4122()); - - messageBinary.append(reinterpret_cast(&_messageTimestamp), sizeof(_messageTimestamp)); - messageBinary.append(reinterpret_cast(&_expiryDelta), sizeof(_expiryDelta)); - - messageBinary.append(AccountManager::getInstance().getAccountInfo().getWalletID().toRfc4122()); - - messageBinary.append(_destinationUUID.toRfc4122()); - - messageBinary.append(reinterpret_cast(&_amount), sizeof(_amount)); - - return messageBinary; -} - -QByteArray SignedWalletTransaction::hexMessage() { - return binaryMessage().toHex(); -} - -QByteArray SignedWalletTransaction::messageDigest() { - return QCryptographicHash::hash(hexMessage(), QCryptographicHash::Sha256).toHex(); -} - -QByteArray SignedWalletTransaction::signedMessageDigest() { - // pull the current private key from menu into RSA structure in memory - QByteArray privateKeyByteArray = Menu::getInstance()->getWalletPrivateKey(); - - BIO* privateKeyBIO = NULL; - RSA* rsaPrivateKey = NULL; - - privateKeyBIO = BIO_new_mem_buf(privateKeyByteArray.data(), privateKeyByteArray.size()); - PEM_read_bio_RSAPrivateKey(privateKeyBIO, &rsaPrivateKey, NULL, NULL); - - QByteArray digestToEncrypt = messageDigest(); - QByteArray encryptedDigest(RSA_size(rsaPrivateKey), 0); - - int encryptReturn = RSA_private_encrypt(digestToEncrypt.size(), - reinterpret_cast(digestToEncrypt.constData()), - reinterpret_cast(encryptedDigest.data()), - rsaPrivateKey, RSA_PKCS1_PADDING); - - // free the two structures used - BIO_free(privateKeyBIO); - RSA_free(rsaPrivateKey); - - return (encryptReturn != -1) ? encryptedDigest : QByteArray(); -} \ No newline at end of file diff --git a/interface/src/SignedWalletTransaction.h b/interface/src/SignedWalletTransaction.h deleted file mode 100644 index 6bc66a535e..0000000000 --- a/interface/src/SignedWalletTransaction.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// SignedWalletTransaction.h -// interfac/src -// -// Created by Stephen Birarda on 2014-07-11. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_SignedWalletTransaction_h -#define hifi_SignedWalletTransaction_h - -#include - -class SignedWalletTransaction : public WalletTransaction { - Q_OBJECT -public: - SignedWalletTransaction(const QUuid& destinationUUID, qint64 amount, qint64 messageTimestamp, qint64 expiryDelta); - - QByteArray binaryMessage(); - QByteArray hexMessage(); - QByteArray messageDigest(); - QByteArray signedMessageDigest(); - -private: - qint64 _messageTimestamp; - qint64 _expiryDelta; -}; - -#endif // hifi_SignedWalletTransaction_h \ No newline at end of file diff --git a/interface/src/devices/Joystick.cpp b/interface/src/devices/Joystick.cpp index 5446ca1330..25b8bc142d 100644 --- a/interface/src/devices/Joystick.cpp +++ b/interface/src/devices/Joystick.cpp @@ -20,10 +20,10 @@ const float MAX_AXIS = 32768.0f; Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) : - _instanceId(instanceId), - _name(name), _sdlGameController(sdlGameController), _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)), + _instanceId(instanceId), + _name(name), _axes(QVector(SDL_JoystickNumAxes(_sdlJoystick))), _buttons(QVector(SDL_JoystickNumButtons(_sdlJoystick))) { diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp deleted file mode 100644 index 86db54afb4..0000000000 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// -// OAuthWebViewHandler.cpp -// interface/src/ui -// -// Created by Stephen Birarda on 2014-05-01. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include - -#include - -#include "Application.h" - -#include "OAuthWebViewHandler.h" - -OAuthWebViewHandler& OAuthWebViewHandler::getInstance() { - static OAuthWebViewHandler sharedInstance; - return sharedInstance; -} - -OAuthWebViewHandler::OAuthWebViewHandler() : - _activeWebView(NULL), - _webViewRedisplayTimer(), - _lastAuthorizationURL() -{ - addHighFidelityRootCAToSSLConfig(); -} - -const char HIGH_FIDELITY_CA[] = "-----BEGIN CERTIFICATE-----\n" - "MIID6TCCA1KgAwIBAgIJANlfRkRD9A8bMA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n" - "VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j\n" - "aXNjbzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVy\n" - "YXRpb25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEW\n" - "E29wc0BoaWdoZmlkZWxpdHkuaW8wHhcNMTQwMzI4MjIzMzM1WhcNMjQwMzI1MjIz\n" - "MzM1WjCBqjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNV\n" - "BAcTDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoTEkhpZ2ggRmlkZWxpdHksIEluYzET\n" - "MBEGA1UECxMKT3BlcmF0aW9uczEYMBYGA1UEAxMPaGlnaGZpZGVsaXR5LmlvMSIw\n" - "IAYJKoZIhvcNAQkBFhNvcHNAaGlnaGZpZGVsaXR5LmlvMIGfMA0GCSqGSIb3DQEB\n" - "AQUAA4GNADCBiQKBgQDyo1euYiPPEdnvDZnIjWrrP230qUKMSj8SWoIkbTJF2hE8\n" - "2eP3YOgbgSGBzZ8EJBxIOuNmj9g9Eg6691hIKFqy5W0BXO38P04Gg+pVBvpHFGBi\n" - "wpqGbfsjaUDuYmBeJRcMO0XYkLCRQG+lAQNHoFDdItWAJfC3FwtP3OCDnz8cNwID\n" - "AQABo4IBEzCCAQ8wHQYDVR0OBBYEFCSv2kmiGg6VFMnxXzLDNP304cPAMIHfBgNV\n" - "HSMEgdcwgdSAFCSv2kmiGg6VFMnxXzLDNP304cPAoYGwpIGtMIGqMQswCQYDVQQG\n" - "EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj\n" - "bzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVyYXRp\n" - "b25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEWE29w\n" - "c0BoaWdoZmlkZWxpdHkuaW+CCQDZX0ZEQ/QPGzAMBgNVHRMEBTADAQH/MA0GCSqG\n" - "SIb3DQEBBQUAA4GBAEkQl3p+lH5vuoCNgyfa67nL0MsBEt+5RSBOgjwCjjASjzou\n" - "FTv5w0he2OypgMQb8i/BYtS1lJSFqjPJcSM1Salzrm3xDOK5pOXJ7h6SQLPDVEyf\n" - "Hy2/9d/to+99+SOUlvfzfgycgjOc+s/AV7Y+GBd7uzGxUdrN4egCZW1F6/mH\n" - "-----END CERTIFICATE-----\n"; - -void OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig() { - QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration(); - - // add the High Fidelity root CA to the list of trusted CA certificates - QByteArray highFidelityCACertificate(HIGH_FIDELITY_CA, sizeof(HIGH_FIDELITY_CA)); - sslConfig.setCaCertificates(sslConfig.caCertificates() + QSslCertificate::fromData(highFidelityCACertificate)); - - // set the modified configuration - QSslConfiguration::setDefaultConfiguration(sslConfig); -} - -const int WEB_VIEW_REDISPLAY_ELAPSED_MSECS = 5 * 1000; - -void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authorizationURL) { - if (!_activeWebView) { - - if (!_lastAuthorizationURL.isEmpty()) { - if (_lastAuthorizationURL.host() == authorizationURL.host() - && _webViewRedisplayTimer.elapsed() < WEB_VIEW_REDISPLAY_ELAPSED_MSECS) { - // this would be re-displaying an OAuth dialog for the same auth URL inside of the redisplay ms - // so return instead - return; - } - } - - _lastAuthorizationURL = authorizationURL; - - _activeWebView = new QWebView; - - // keep the window on top and delete it when it closes - _activeWebView->setWindowFlags(Qt::Sheet); - _activeWebView->setAttribute(Qt::WA_DeleteOnClose); - - qDebug() << "Displaying QWebView for OAuth authorization at" << authorizationURL.toString(); - - AccountManager& accountManager = AccountManager::getInstance(); - - QUrl codedAuthorizationURL = authorizationURL; - - // check if we have an access token for this host - if so we can bypass login by adding it to the URL - if (accountManager.getAuthURL().host() == authorizationURL.host() - && accountManager.hasValidAccessToken()) { - - const QString ACCESS_TOKEN_QUERY_STRING_KEY = "access_token"; - - QUrlQuery authQuery(codedAuthorizationURL); - authQuery.addQueryItem(ACCESS_TOKEN_QUERY_STRING_KEY, accountManager.getAccountInfo().getAccessToken().token); - - codedAuthorizationURL.setQuery(authQuery); - } - - connect(_activeWebView.data(), &QWebView::urlChanged, this, &OAuthWebViewHandler::handleURLChanged); - - _activeWebView->load(codedAuthorizationURL); - - connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors, - this, &OAuthWebViewHandler::handleSSLErrors); - connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::finished, - this, &OAuthWebViewHandler::handleReplyFinished); - connect(_activeWebView.data(), &QWebView::loadFinished, this, &OAuthWebViewHandler::handleLoadFinished); - - // connect to the destroyed signal so after the web view closes we can start a timer - connect(_activeWebView.data(), &QWebView::destroyed, this, &OAuthWebViewHandler::handleWebViewDestroyed); - } -} - -void OAuthWebViewHandler::handleSSLErrors(QNetworkReply* networkReply, const QList& errorList) { - qDebug() << "SSL Errors:" << errorList; -} - -void OAuthWebViewHandler::handleLoadFinished(bool success) { - if (success && _activeWebView->url().host() == NodeList::getInstance()->getDomainHandler().getHostname()) { - qDebug() << "OAuth authorization code passed successfully to domain-server."; - - // grab the UUID that is set as the state parameter in the auth URL - // since that is our new session UUID - QUrlQuery authQuery(_activeWebView->url()); - - const QString AUTH_STATE_QUERY_KEY = "state"; - NodeList::getInstance()->setSessionUUID(QUuid(authQuery.queryItemValue(AUTH_STATE_QUERY_KEY))); - - _activeWebView->close(); - _activeWebView = NULL; - } -} - -void OAuthWebViewHandler::handleReplyFinished(QNetworkReply* reply) { - if (_activeWebView && reply->error() != QNetworkReply::NoError) { - qDebug() << "Error loading" << reply->url() << "-" << reply->errorString(); - _activeWebView->close(); - } -} - -void OAuthWebViewHandler::handleWebViewDestroyed(QObject* destroyedObject) { - _webViewRedisplayTimer.restart(); -} - -void OAuthWebViewHandler::handleURLChanged(const QUrl& url) { - // check if this is the authorization screen - if it is then we need to show the OAuthWebViewHandler - const QString ACCESS_TOKEN_URL_REGEX_STRING = "redirect_uri=[\\w:\\/\\.]+&access_token="; - QRegExp accessTokenRegex(ACCESS_TOKEN_URL_REGEX_STRING); - - if (accessTokenRegex.indexIn(url.toString()) != -1) { - _activeWebView->show(); - } else if (url.toString() == DEFAULT_NODE_AUTH_URL.toString() + "/login") { - // this is a login request - we're going to close the webview and signal the AccountManager that we need a login - qDebug() << "data-server replied with login request. Signalling that login is required to proceed with OAuth."; - _activeWebView->close(); - AccountManager::getInstance().checkAndSignalForAccessToken(); - } -} diff --git a/interface/src/ui/OAuthWebViewHandler.h b/interface/src/ui/OAuthWebViewHandler.h deleted file mode 100644 index 1a95f17dfd..0000000000 --- a/interface/src/ui/OAuthWebViewHandler.h +++ /dev/null @@ -1,43 +0,0 @@ -// -// OAuthWebviewHandler.h -// interface/src/ui -// -// Created by Stephen Birarda on 2014-05-01. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_OAuthWebviewHandler_h -#define hifi_OAuthWebviewHandler_h - -#include - -class QWebView; - -class OAuthWebViewHandler : public QObject { - Q_OBJECT -public: - OAuthWebViewHandler(); - static OAuthWebViewHandler& getInstance(); - static void addHighFidelityRootCAToSSLConfig(); - - void clearLastAuthorizationURL() { _lastAuthorizationURL = QUrl(); } - -public slots: - void displayWebviewForAuthorizationURL(const QUrl& authorizationURL); - -private slots: - void handleSSLErrors(QNetworkReply* networkReply, const QList& errorList); - void handleLoadFinished(bool success); - void handleReplyFinished(QNetworkReply* reply); - void handleWebViewDestroyed(QObject* destroyedObject); - void handleURLChanged(const QUrl& url); -private: - QPointer _activeWebView; - QElapsedTimer _webViewRedisplayTimer; - QUrl _lastAuthorizationURL; -}; - -#endif // hifi_OAuthWebviewHandler_h \ No newline at end of file diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index 501437fab2..e4dd37234b 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -10,5 +10,19 @@ if (WIN32) target_link_libraries(${TARGET_NAME} ws2_32.lib) endif () +# find OpenSSL +find_package(OpenSSL REQUIRED) + +if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") + # this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto + message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." + "\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") +endif () + +include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") + +# append OpenSSL to our list of libraries to link +list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${OPENSSL_LIBRARIES}") + # call macro to link our dependencies and bubble them up via a property on our target link_shared_dependencies() \ No newline at end of file diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index bb471442ea..650e0e3321 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -19,9 +19,11 @@ #include #include #include +#include #include "NodeList.h" #include "PacketHeaders.h" +#include "RSAKeypairGenerator.h" #include "AccountManager.h" @@ -144,6 +146,12 @@ void AccountManager::setAuthURL(const QUrl& authURL) { } else { requestProfile(); } + + // if we don't have a private key in settings we should generate a new keypair + if (!_accountInfo.hasPrivateKey()) { + qDebug() << "No private key present - generating a new key-pair."; + generateNewKeypair(); + } } } } @@ -280,14 +288,12 @@ void AccountManager::processReply() { } void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) { - QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll()); - JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply); if (callbackParams.jsonCallbackReceiver) { // invoke the right method on the callback receiver QMetaObject::invokeMethod(callbackParams.jsonCallbackReceiver, qPrintable(callbackParams.jsonCallbackMethod), - Q_ARG(const QJsonObject&, jsonResponse.object())); + Q_ARG(QNetworkReply&, *requestReply)); // remove the related reply-callback group from the map _pendingCallbackMap.remove(requestReply); @@ -295,7 +301,7 @@ void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) { } else { if (VERBOSE_HTTP_REQUEST_DEBUGGING) { qDebug() << "Received JSON response from data-server that has no matching callback."; - qDebug() << jsonResponse; + qDebug() << QJsonDocument::fromJson(requestReply->readAll()); } } } @@ -319,6 +325,16 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) { } } +void AccountManager::persistAccountToSettings() { + if (_shouldPersistToSettingsFile) { + // store this access token into the local settings + QSettings localSettings; + localSettings.beginGroup(ACCOUNTS_GROUP); + localSettings.setValue(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE), + QVariant::fromValue(_accountInfo)); + } +} + bool AccountManager::hasValidAccessToken() { if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) { @@ -408,15 +424,10 @@ void AccountManager::requestAccessTokenFinished() { emit loginComplete(rootURL); - if (_shouldPersistToSettingsFile) { - // store this access token into the local settings - QSettings localSettings; - localSettings.beginGroup(ACCOUNTS_GROUP); - localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE), - QVariant::fromValue(_accountInfo)); - } + persistAccountToSettings(); requestProfile(); + generateNewKeypair(); } } else { // TODO: error handling @@ -434,7 +445,7 @@ void AccountManager::requestProfile() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QUrl profileURL = _authURL; - profileURL.setPath("/api/v1/users/profile"); + profileURL.setPath("/api/v1/user/profile"); QNetworkRequest profileRequest(profileURL); profileRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, _accountInfo.getAccessToken().authorizationHeaderValue()); @@ -458,15 +469,8 @@ void AccountManager::requestProfileFinished() { // the username has changed to whatever came back emit usernameChanged(_accountInfo.getUsername()); - if (_shouldPersistToSettingsFile) { - // store the whole profile into the local settings - QUrl rootURL = profileReply->url(); - rootURL.setPath(""); - QSettings localSettings; - localSettings.beginGroup(ACCOUNTS_GROUP); - localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE), - QVariant::fromValue(_accountInfo)); - } + // store the whole profile into the local settings + persistAccountToSettings(); } else { // TODO: error handling @@ -478,3 +482,57 @@ void AccountManager::requestProfileError(QNetworkReply::NetworkError error) { // TODO: error handling qDebug() << "AccountManager requestProfileError - " << error; } + +void AccountManager::generateNewKeypair() { + // setup a new QThread to generate the keypair on, in case it takes a while + QThread* generateThread = new QThread(this); + + // setup a keypair generator + RSAKeypairGenerator* keypairGenerator = new RSAKeypairGenerator(); + + connect(generateThread, &QThread::started, keypairGenerator, &RSAKeypairGenerator::generateKeypair); + connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this, &AccountManager::processGeneratedKeypair); + connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair, + this, &AccountManager::handleKeypairGenerationError); + connect(keypairGenerator, &QObject::destroyed, generateThread, &QThread::quit); + connect(generateThread, &QThread::finished, generateThread, &QThread::deleteLater); + + keypairGenerator->moveToThread(generateThread); + + qDebug() << "Starting worker thread to generate 2048-bit RSA key-pair."; + generateThread->start(); +} + +void AccountManager::processGeneratedKeypair(const QByteArray& publicKey, const QByteArray& privateKey) { + + qDebug() << "Generated 2048-bit RSA key-pair. Storing private key and uploading public key."; + + // set the private key on our data-server account info + _accountInfo.setPrivateKey(privateKey); + persistAccountToSettings(); + + // upload the public key so data-web has an up-to-date key + const QString PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key"; + + // setup a multipart upload to send up the public key + QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + QHttpPart keyPart; + keyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); + keyPart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"public_key\"; filename=\"public_key\"")); + keyPart.setBody(publicKey); + + requestMultiPart->append(keyPart); + + authenticatedRequest(PUBLIC_KEY_UPDATE_PATH, QNetworkAccessManager::PutOperation, + JSONCallbackParameters(), QByteArray(), requestMultiPart); + + // get rid of the keypair generator now that we don't need it anymore + sender()->deleteLater(); +} + +void AccountManager::handleKeypairGenerationError() { + // for now there isn't anything we do with this except get the worker thread to clean up + sender()->deleteLater(); +} diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 3628b5a865..7b5ea8de8b 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -70,7 +70,7 @@ public: void requestAccessToken(const QString& login, const QString& password); void requestProfile(); - const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; } + DataServerAccountInfo& getAccountInfo() { return _accountInfo; } public slots: void requestAccessTokenFinished(); @@ -91,13 +91,19 @@ signals: void balanceChanged(qint64 newBalance); private slots: void processReply(); + void handleKeypairGenerationError(); + void processGeneratedKeypair(const QByteArray& publicKey, const QByteArray& privateKey); private: AccountManager(); AccountManager(AccountManager const& other); // not implemented void operator=(AccountManager const& other); // not implemented + + void persistAccountToSettings(); void passSuccessToCallback(QNetworkReply* reply); void passErrorToCallback(QNetworkReply* reply); + + void generateNewKeypair(); Q_INVOKABLE void invokedRequest(const QString& path, bool requiresAuthentication, diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 3d7617e17b..1a7f6d3dbc 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include @@ -134,8 +135,9 @@ void AddressManager::handleLookupString(const QString& lookupString) { } } -void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) { - QJsonObject dataObject = jsonObject["data"].toObject(); +void AddressManager::handleAPIResponse(QNetworkReply& requestReply) { + QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + QJsonObject dataObject = responseObject["data"].toObject(); const QString ADDRESS_API_DOMAIN_KEY = "domain"; const QString ADDRESS_API_ONLINE_KEY = "online"; @@ -190,7 +192,7 @@ void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) { } else { qDebug() << "Received an address manager API response with no domain key. Cannot parse."; - qDebug() << jsonObject; + qDebug() << responseObject; } } else { // we've been told that this result exists but is offline, emit our signal so the application can handle diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index fe6219b3f7..bc0f2716dd 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -42,7 +42,7 @@ public: public slots: void handleLookupString(const QString& lookupString); - void handleAPIResponse(const QJsonObject& jsonObject); + void handleAPIResponse(QNetworkReply& requestReply); void handleAPIError(QNetworkReply& errorReply); void goToUser(const QString& username); signals: diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 9bdb012f70..61b1bbf418 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -9,6 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + +#include #include #include "DataServerAccountInfo.h" @@ -20,7 +23,9 @@ DataServerAccountInfo::DataServerAccountInfo() : _discourseApiKey(), _walletID(), _balance(0), - _hasBalance(false) + _hasBalance(false), + _privateKey(), + _usernameSignature() { } @@ -33,6 +38,7 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI _walletID = otherInfo._walletID; _balance = otherInfo._balance; _hasBalance = otherInfo._hasBalance; + _privateKey = otherInfo._privateKey; } DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) { @@ -51,6 +57,7 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) { swap(_walletID, otherInfo._walletID); swap(_balance, otherInfo._balance); swap(_hasBalance, otherInfo._hasBalance); + swap(_privateKey, otherInfo._privateKey); } void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject) { @@ -61,6 +68,9 @@ void DataServerAccountInfo::setUsername(const QString& username) { if (_username != username) { _username = username; + // clear our username signature so it has to be re-created + _usernameSignature = QByteArray(); + qDebug() << "Username changed to" << username; } } @@ -92,7 +102,8 @@ void DataServerAccountInfo::setBalance(qint64 balance) { } } -void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) { +void DataServerAccountInfo::setBalanceFromJSON(QNetworkReply& requestReply) { + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); if (jsonObject["status"].toString() == "success") { qint64 balanceInSatoshis = jsonObject["data"].toObject()["wallet"].toObject()["balance"].toDouble(); setBalance(balanceInSatoshis); @@ -111,12 +122,58 @@ void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject setWalletID(QUuid(user["wallet_id"].toString())); } +const QByteArray& DataServerAccountInfo::getUsernameSignature() { + if (_usernameSignature.isEmpty()) { + if (!_privateKey.isEmpty()) { + const char* privateKeyData = _privateKey.constData(); + RSA* rsaPrivateKey = d2i_RSAPrivateKey(NULL, + reinterpret_cast(&privateKeyData), + _privateKey.size()); + if (rsaPrivateKey) { + QByteArray usernameByteArray = _username.toUtf8(); + _usernameSignature.resize(RSA_size(rsaPrivateKey)); + + int encryptReturn = RSA_private_encrypt(usernameByteArray.size(), + reinterpret_cast(usernameByteArray.constData()), + reinterpret_cast(_usernameSignature.data()), + rsaPrivateKey, RSA_PKCS1_PADDING); + + if (encryptReturn == -1) { + qDebug() << "Error encrypting username signature."; + qDebug() << "Will re-attempt on next domain-server check in."; + _usernameSignature = QByteArray(); + } + + // free the private key RSA struct now that we are done with it + RSA_free(rsaPrivateKey); + } else { + qDebug() << "Could not create RSA struct from QByteArray private key."; + qDebug() << "Will re-attempt on next domain-server check in."; + } + } else { + qDebug() << "No private key present in DataServerAccountInfo. Re-log to generate new key."; + qDebug() << "Returning empty username signature."; + } + } + + return _usernameSignature; +} + +void DataServerAccountInfo::setPrivateKey(const QByteArray& privateKey) { + _privateKey = privateKey; + + // clear our username signature so it has to be re-created + _usernameSignature = QByteArray(); +} + QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { - out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey << info._walletID; + out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey + << info._walletID << info._privateKey; return out; } QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) { - in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey >> info._walletID; + in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey + >> info._walletID >> info._privateKey; return in; } diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 91675594ad..9804bd755c 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -13,6 +13,7 @@ #define hifi_DataServerAccountInfo_h #include +#include #include #include "OAuthAccessToken.h" @@ -41,13 +42,17 @@ public: const QUuid& getWalletID() const { return _walletID; } void setWalletID(const QUuid& walletID); + + const QByteArray& getUsernameSignature(); + bool hasPrivateKey() const { return !_privateKey.isEmpty(); } + void setPrivateKey(const QByteArray& privateKey); qint64 getBalance() const { return _balance; } float getBalanceInSatoshis() const { return _balance / SATOSHIS_PER_CREDIT; } void setBalance(qint64 balance); bool hasBalance() const { return _hasBalance; } void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; } - Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject); + Q_INVOKABLE void setBalanceFromJSON(QNetworkReply& requestReply); bool hasProfile() const; @@ -67,6 +72,8 @@ private: QUuid _walletID; qint64 _balance; bool _hasBalance; + QByteArray _privateKey; + QByteArray _usernameSignature; }; #endif // hifi_DataServerAccountInfo_h diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index dd33c96d74..919dc75c23 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -679,7 +679,6 @@ void LimitedNodeList::updateLocalSockAddr() { qDebug() << "Local socket has changed from" << _localSockAddr << "to" << newSockAddr; } - _localSockAddr = newSockAddr; emit localSockAddrChanged(_localSockAddr); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index c416773201..73381d01a5 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -101,6 +101,8 @@ public: const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket); SharedNodePointer updateSocketsForNode(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket); + + const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; } void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet); void processKillNode(const QByteArray& datagram); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 25531861ca..13ebfb59b8 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -302,11 +302,22 @@ void NodeList::sendDomainServerCheckIn() { QDataStream packetStream(&domainServerPacket, QIODevice::Append); // pack our data to send to the domain-server - packetStream << _ownerType << _publicSockAddr << _localSockAddr << (quint8) _nodeTypesOfInterest.size(); + packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList(); - // copy over the bytes for node types of interest, if required - foreach (NodeType_t nodeTypeOfInterest, _nodeTypesOfInterest) { - packetStream << nodeTypeOfInterest; + + // if this is a connect request, and we can present a username signature, send it along + if (!_domainHandler.isConnected()) { + DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo(); + packetStream << accountInfo.getUsername(); + + const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature(); + + if (!usernameSignature.isEmpty()) { + qDebug() << "Including username signature in domain connect request."; + packetStream << usernameSignature; + } else { + qDebug() << "Private key not present - domain connect request cannot include username signature"; + } } if (!isUsingDTLS) { diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index e3bfbb8f84..be4fb68bfb 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -114,7 +114,7 @@ QString nameForPacketType(PacketType type) { PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainListRequest); PACKET_TYPE_NAME_LOOKUP(PacketTypeRequestAssignment); PACKET_TYPE_NAME_LOOKUP(PacketTypeCreateAssignment); - PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainOAuthRequest); + PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainConnectionDenied); PACKET_TYPE_NAME_LOOKUP(PacketTypeMuteEnvironment); PACKET_TYPE_NAME_LOOKUP(PacketTypeAudioStreamStats); PACKET_TYPE_NAME_LOOKUP(PacketTypeDataServerConfirm); diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 2d298d253d..ab596673f5 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -38,7 +38,7 @@ enum PacketType { PacketTypeDomainListRequest, PacketTypeRequestAssignment, PacketTypeCreateAssignment, - PacketTypeDomainOAuthRequest, + PacketTypeDomainConnectionDenied, PacketTypeMuteEnvironment, PacketTypeAudioStreamStats, PacketTypeDataServerConfirm, @@ -81,7 +81,7 @@ typedef char PacketVersion; const QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest - << PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainOAuthRequest + << PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainConnectionDenied << PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse << PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery << PacketTypeEntityQuery << PacketTypeOctreeDataNack << PacketTypeVoxelEditNack << PacketTypeParticleEditNack << PacketTypeEntityEditNack diff --git a/libraries/networking/src/RSAKeypairGenerator.cpp b/libraries/networking/src/RSAKeypairGenerator.cpp new file mode 100644 index 0000000000..5b53a84dc4 --- /dev/null +++ b/libraries/networking/src/RSAKeypairGenerator.cpp @@ -0,0 +1,91 @@ +// +// RSAKeypairGenerator.cpp +// libraries/networking/src +// +// Created by Stephen Birarda on 2014-10-14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include + +#include + +#include "RSAKeypairGenerator.h" + +RSAKeypairGenerator::RSAKeypairGenerator(QObject* parent) : + QObject(parent) +{ + +} + +void RSAKeypairGenerator::generateKeypair() { + + RSA* keyPair = RSA_new(); + BIGNUM* exponent = BN_new(); + + const unsigned long RSA_KEY_EXPONENT = 65537; + BN_set_word(exponent, RSA_KEY_EXPONENT); + + // seed the random number generator before we call RSA_generate_key_ex + srand(time(NULL)); + + const int RSA_KEY_BITS = 2048; + + if (!RSA_generate_key_ex(keyPair, RSA_KEY_BITS, exponent, NULL)) { + qDebug() << "Error generating 2048-bit RSA Keypair -" << ERR_get_error(); + + emit errorGeneratingKeypair(); + + // we're going to bust out of here but first we cleanup the BIGNUM + BN_free(exponent); + return; + } + + // we don't need the BIGNUM anymore so clean that up + BN_free(exponent); + + // grab the public key and private key from the file + unsigned char* publicKeyDER = NULL; + int publicKeyLength = i2d_RSAPublicKey(keyPair, &publicKeyDER); + + unsigned char* privateKeyDER = NULL; + int privateKeyLength = i2d_RSAPrivateKey(keyPair, &privateKeyDER); + + if (publicKeyLength <= 0 || privateKeyLength <= 0) { + qDebug() << "Error getting DER public or private key from RSA struct -" << ERR_get_error(); + + emit errorGeneratingKeypair(); + + // cleanup the RSA struct + RSA_free(keyPair); + + // cleanup the public and private key DER data, if required + if (publicKeyLength > 0) { + delete publicKeyDER; + } + + if (privateKeyLength > 0) { + delete privateKeyDER; + } + + return; + } + + // we have the public key and private key in memory + // we can cleanup the RSA struct before we continue on + RSA_free(keyPair); + + QByteArray publicKeyArray(reinterpret_cast(publicKeyDER), publicKeyLength); + QByteArray privateKeyArray(reinterpret_cast(privateKeyDER), privateKeyLength); + + // cleanup the publicKeyDER and publicKeyDER data + delete publicKeyDER; + delete privateKeyDER; + + emit generatedKeypair(publicKeyArray, privateKeyArray); +} \ No newline at end of file diff --git a/libraries/networking/src/RSAKeypairGenerator.h b/libraries/networking/src/RSAKeypairGenerator.h new file mode 100644 index 0000000000..dd90313625 --- /dev/null +++ b/libraries/networking/src/RSAKeypairGenerator.h @@ -0,0 +1,28 @@ +// +// RSAKeypairGenerator.h +// libraries/networking/src +// +// Created by Stephen Birarda on 2014-10-14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RSAKeypairGenerator_h +#define hifi_RSAKeypairGenerator_h + +#include + +class RSAKeypairGenerator : public QObject { + Q_OBJECT +public: + RSAKeypairGenerator(QObject* parent = 0); +public slots: + void generateKeypair(); +signals: + void errorGeneratingKeypair(); + void generatedKeypair(const QByteArray& publicKey, const QByteArray& privateKey); +}; + +#endif // hifi_RSAKeypairGenerator_h \ No newline at end of file diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 549f02ae3c..4fc6905e3a 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -69,7 +69,7 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall multipart); } -void UserActivityLogger::requestFinished(const QJsonObject& object) { +void UserActivityLogger::requestFinished(QNetworkReply& requestReply) { // qDebug() << object; } diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index 1bd966d632..9b100461ee 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -39,7 +39,7 @@ public slots: void wentTo(QString destinationType, QString destinationName); private slots: - void requestFinished(const QJsonObject& object); + void requestFinished(QNetworkReply& requestReply); void requestError(QNetworkReply& errorReply); private: