From ddf4f029d969f433eaa111703ad18e604c22bdad Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 22 Feb 2016 16:14:11 -0800 Subject: [PATCH] request and store domain public key in ice-server memory --- ice-server/src/IceServer.cpp | 125 +++++++++++++++++++++++++++++------ ice-server/src/IceServer.h | 14 +++- 2 files changed, 116 insertions(+), 23 deletions(-) diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index 2baa7a13a7..31f576fa03 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -9,14 +9,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include "IceServer.h" + +#include +#include +#include +#include #include +#include #include #include -#include "IceServer.h" - const int CLEAR_INACTIVE_PEERS_INTERVAL_MSECS = 1 * 1000; const int PEER_SILENCE_THRESHOLD_MSECS = 5 * 1000; @@ -70,9 +74,10 @@ void IceServer::processPacket(std::unique_ptr packet) { if (nlPacket->getType() == PacketType::ICEServerHeartbeat) { SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*nlPacket); - - // so that we can send packets to the heartbeating peer when we need, we need to activate a socket now - peer->activateMatchingOrNewSymmetricSocket(nlPacket->getSenderSockAddr()); + if (peer) { + // so that we can send packets to the heartbeating peer when we need, we need to activate a socket now + peer->activateMatchingOrNewSymmetricSocket(nlPacket->getSenderSockAddr()); + } } else if (nlPacket->getType() == PacketType::ICEServerQuery) { QDataStream heartbeatStream(nlPacket.get()); @@ -114,31 +119,107 @@ SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(NLPacket& packet) { // pull the UUID, public and private sock addrs for this peer QUuid senderUUID; HifiSockAddr publicSocket, localSocket; + QByteArray signature; QDataStream heartbeatStream(&packet); - - heartbeatStream >> senderUUID; - heartbeatStream >> publicSocket >> localSocket; + heartbeatStream >> senderUUID >> publicSocket >> localSocket; - // make sure we have this sender in our peer hash - SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID); + auto signedPlaintext = QByteArray::fromRawData(packet.getPayload(), heartbeatStream.device()->pos()); + heartbeatStream >> signature; - if (!matchingPeer) { - // if we don't have this sender we need to create them now - matchingPeer = QSharedPointer::create(senderUUID, publicSocket, localSocket); - _activePeers.insert(senderUUID, matchingPeer); + // make sure this is a verified heartbeat before performing any more processing + if (isVerifiedHeartbeat(senderUUID, signedPlaintext, signature)) { + // make sure we have this sender in our peer hash + SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID); - qDebug() << "Added a new network peer" << *matchingPeer; + if (!matchingPeer) { + // if we don't have this sender we need to create them now + matchingPeer = QSharedPointer::create(senderUUID, publicSocket, localSocket); + _activePeers.insert(senderUUID, matchingPeer); + + qDebug() << "Added a new network peer" << *matchingPeer; + } else { + // we already had the peer so just potentially update their sockets + matchingPeer->setPublicSocket(publicSocket); + matchingPeer->setLocalSocket(localSocket); + } + + // update our last heard microstamp for this network peer to now + matchingPeer->setLastHeardMicrostamp(usecTimestampNow()); + + return matchingPeer; } else { - // we already had the peer so just potentially update their sockets - matchingPeer->setPublicSocket(publicSocket); - matchingPeer->setLocalSocket(localSocket); + // not verified, return the empty peer object + return SharedNetworkPeer(); } +} - // update our last heard microstamp for this network peer to now - matchingPeer->setLastHeardMicrostamp(usecTimestampNow()); +bool IceServer::isVerifiedHeartbeat(const QUuid& domainID, const QByteArray& plaintext, const QByteArray& signature) { + // check if we have a private key for this domain ID - if we do not then fire off the request for it + auto it = _domainPublicKeys.find(domainID); + if (it != _domainPublicKeys.end()) { - return matchingPeer; + return true; + } else { + // we don't have a public key for this domain so we can't verify this heartbeat + // ask the metaverse API for the right public key and return false to indicate that this is not verified + requestDomainPublicKey(domainID); + + return false; + } +} + +void IceServer::requestDomainPublicKey(const QUuid& domainID) { + // send a request to the metaverse API for the public key for this domain + QNetworkAccessManager* manager = new QNetworkAccessManager { this }; + connect(manager, &QNetworkAccessManager::finished, this, &IceServer::publicKeyReplyFinished); + + QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL }; + QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID)); + publicKeyURL.setPath(publicKeyPath); + + QNetworkRequest publicKeyRequest { publicKeyURL }; + publicKeyRequest.setAttribute(QNetworkRequest::User, domainID); + + qDebug() << "Requesting public key for domain with ID" << domainID; + + manager->get(publicKeyRequest); +} + +void IceServer::publicKeyReplyFinished(QNetworkReply* reply) { + // get the domain ID from the QNetworkReply attribute + QUuid domainID = reply->request().attribute(QNetworkRequest::User).toUuid(); + + if (reply->error() == QNetworkReply::NoError) { + // pull out the public key and store it for this domain + + // the response should be JSON + QJsonDocument responseDocument = QJsonDocument::fromJson(reply->readAll()); + + static const QString DATA_KEY = "data"; + static const QString PUBLIC_KEY_KEY = "public_key"; + static const QString STATUS_KEY = "status"; + static const QString SUCCESS_VALUE = "success"; + + auto responseObject = responseDocument.object(); + if (responseObject[STATUS_KEY].toString() == SUCCESS_VALUE) { + auto dataObject = responseObject[DATA_KEY].toObject(); + if (dataObject.contains(PUBLIC_KEY_KEY)) { + _domainPublicKeys.emplace(domainID, + QByteArray::fromBase64(dataObject[PUBLIC_KEY_KEY].toString().toUtf8())); + } else { + qWarning() << "There was no public key present in response for domain with ID" << domainID; + } + } else { + qWarning() << "The metaverse API did not return success for public key request for domain with ID" << domainID; + } + + } else { + // there was a problem getting the public key for the domain + // log it since it will be re-requested on the next heartbeat + + qWarning() << "Error retreiving public key for domain with ID" << domainID << "-" << reply->errorString(); + } } void IceServer::sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr) { diff --git a/ice-server/src/IceServer.h b/ice-server/src/IceServer.h index f1c2c06b65..81234b2c3c 100644 --- a/ice-server/src/IceServer.h +++ b/ice-server/src/IceServer.h @@ -16,13 +16,15 @@ #include #include +#include + #include #include #include #include #include -typedef QHash NetworkPeerHash; +class QNetworkReply; class IceServer : public QCoreApplication, public HTTPRequestHandler { Q_OBJECT @@ -31,6 +33,7 @@ public: bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); private slots: void clearInactivePeers(); + void publicKeyReplyFinished(QNetworkReply* reply); private: bool packetVersionMatch(const udt::Packet& packet); void processPacket(std::unique_ptr packet); @@ -38,10 +41,19 @@ private: SharedNetworkPeer addOrUpdateHeartbeatingPeer(NLPacket& incomingPacket); void sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr); + bool isVerifiedHeartbeat(const QUuid& domainID, const QByteArray& plaintext, const QByteArray& signature); + void requestDomainPublicKey(const QUuid& domainID); + QUuid _id; udt::Socket _serverSocket; + + using NetworkPeerHash = QHash; NetworkPeerHash _activePeers; + HTTPManager _httpManager; + + using DomainPublicKeyHash = std::unordered_map; + DomainPublicKeyHash _domainPublicKeys; }; #endif // hifi_IceServer_h