add signed heartbeat sending to domain-server

This commit is contained in:
Stephen Birarda 2016-02-22 15:12:02 -08:00
parent d444d5dd18
commit 11a1bc4488
7 changed files with 81 additions and 31 deletions

View file

@ -42,7 +42,7 @@
int const DomainServer::EXIT_CODE_REBOOT = 234923;
const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.io";
const QString ICE_SERVER_DEFAULT_HOSTNAME = "localhost";
DomainServer::DomainServer(int argc, char* argv[]) :
QCoreApplication(argc, argv),
@ -1053,11 +1053,55 @@ void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) {
domainUpdateJSON.toUtf8());
}
// TODO: have data-web respond with ice-server hostname to use
void DomainServer::sendHeartbeatToIceServer() {
if (!_iceServerSocket.getAddress().isNull()) {
DependencyManager::get<LimitedNodeList>()->sendHeartbeatToIceServer(_iceServerSocket);
static auto heartbeatPacket = NLPacket::create(PacketType::ICEServerHeartbeat);
bool shouldRecreatePacket = false;
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
if (heartbeatPacket->getPayloadSize() > 0) {
// if either of our sockets have changed we need to re-sign the heartbeat
// first read the sockets out from the current packet
heartbeatPacket->seek(0);
QDataStream heartbeatStream(heartbeatPacket.get());
QUuid senderUUID;
HifiSockAddr publicSocket, localSocket;
heartbeatStream >> senderUUID >> publicSocket >> localSocket;
if (publicSocket != limitedNodeList->getPublicSockAddr() || localSocket != limitedNodeList->getLocalSockAddr()) {
shouldRecreatePacket = true;
}
} else {
shouldRecreatePacket = true;
}
if (shouldRecreatePacket) {
// either we don't have a heartbeat packet yet or sockets have changed and we need to make a new one
// reset the position in the packet before writing
heartbeatPacket->reset();
// write our plaintext data to the packet
QDataStream heartbeatDataStream(heartbeatPacket.get());
heartbeatDataStream << limitedNodeList->getSessionUUID()
<< limitedNodeList->getPublicSockAddr() << limitedNodeList->getLocalSockAddr();
// setup a QByteArray that points to the plaintext data
auto plaintext = QByteArray::fromRawData(heartbeatPacket->getPayload(), heartbeatPacket->getPayloadSize());
// generate a signature for the plaintext data in the packet
auto& accountManager = AccountManager::getInstance();
auto signature = accountManager.getAccountInfo().signPlaintext(plaintext);
// pack the signature with the data
heartbeatDataStream << plaintext;
}
// send the heartbeat packet to the ice server now
limitedNodeList->sendUnreliablePacket(*heartbeatPacket, _iceServerSocket);
}
}

View file

@ -33,7 +33,7 @@
#include "AccountManager.h"
#include "NetworkLogging.h"
const bool VERBOSE_HTTP_REQUEST_DEBUGGING = false;
const bool VERBOSE_HTTP_REQUEST_DEBUGGING = true;
AccountManager& AccountManager::getInstance(bool forceReset) {
static std::unique_ptr<AccountManager> sharedInstance(new AccountManager());

View file

@ -117,41 +117,47 @@ void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject
}
QByteArray DataServerAccountInfo::getUsernameSignature(const QUuid& connectionToken) {
QByteArray lowercaseUsername = _username.toLower().toUtf8();
QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()),
QCryptographicHash::Sha256);
auto signature = signPlaintext(usernameWithToken);
if (!signature.isEmpty()) {
qDebug(networking) << "Returning username" << _username
<< "signed with connection UUID" << uuidStringWithoutCurlyBraces(connectionToken);
} else {
qCDebug(networking) << "Error signing username with connection token";
qCDebug(networking) << "Will re-attempt on next domain-server check in.";
}
return signature;
}
QByteArray DataServerAccountInfo::signPlaintext(const QByteArray& plaintext) {
if (!_privateKey.isEmpty()) {
const char* privateKeyData = _privateKey.constData();
RSA* rsaPrivateKey = d2i_RSAPrivateKey(NULL,
reinterpret_cast<const unsigned char**>(&privateKeyData),
_privateKey.size());
if (rsaPrivateKey) {
QByteArray lowercaseUsername = _username.toLower().toUtf8();
QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()),
QCryptographicHash::Sha256);
QByteArray usernameSignature(RSA_size(rsaPrivateKey), 0);
unsigned int usernameSignatureSize = 0;
QByteArray signature(RSA_size(rsaPrivateKey), 0);
unsigned int signatureBytes = 0;
int encryptReturn = RSA_sign(NID_sha256,
reinterpret_cast<const unsigned char*>(usernameWithToken.constData()),
usernameWithToken.size(),
reinterpret_cast<unsigned char*>(usernameSignature.data()),
&usernameSignatureSize,
reinterpret_cast<const unsigned char*>(plaintext.constData()),
plaintext.size(),
reinterpret_cast<unsigned char*>(signature.data()),
&signatureBytes,
rsaPrivateKey);
// free the private key RSA struct now that we are done with it
RSA_free(rsaPrivateKey);
if (encryptReturn == -1) {
qCDebug(networking) << "Error encrypting username signature.";
qCDebug(networking) << "Will re-attempt on next domain-server check in.";
} else {
qDebug(networking) << "Returning username" << _username << "signed with connection UUID" << uuidStringWithoutCurlyBraces(connectionToken);
return usernameSignature;
if (encryptReturn != -1) {
return signature;
}
} else {
qCDebug(networking) << "Could not create RSA struct from QByteArray private key.";
qCDebug(networking) << "Will re-attempt on next domain-server check in.";
}
}
return QByteArray();

View file

@ -54,6 +54,8 @@ public:
bool hasPrivateKey() const { return !_privateKey.isEmpty(); }
void setPrivateKey(const QByteArray& privateKey) { _privateKey = privateKey; }
QByteArray signPlaintext(const QByteArray& plaintext);
void setDomainID(const QUuid& domainID) { _domainID = domainID; }
const QUuid& getDomainID() const { return _domainID; }

View file

@ -902,10 +902,6 @@ void LimitedNodeList::updateLocalSockAddr() {
}
}
void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr) {
sendPacketToIceServer(PacketType::ICEServerHeartbeat, iceServerSockAddr, _sessionUUID);
}
void LimitedNodeList::sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID,
const QUuid& peerID) {
sendPacketToIceServer(PacketType::ICEServerQuery, iceServerSockAddr, clientID, peerID);

View file

@ -143,6 +143,7 @@ public:
bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; }
const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; }
const HifiSockAddr& getPublicSockAddr() const { return _publicSockAddr; }
const HifiSockAddr& getSTUNSockAddr() const { return _stunSockAddr; }
void processKillNode(ReceivedMessage& message);
@ -161,7 +162,6 @@ public:
std::unique_ptr<NLPacket> constructICEPingPacket(PingType_t pingType, const QUuid& iceID);
std::unique_ptr<NLPacket> constructICEPingReplyPacket(ReceivedMessage& message, const QUuid& iceID);
void sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr);
void sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID);
SharedNodePointer findNodeWithAddr(const HifiSockAddr& addr);

View file

@ -45,6 +45,8 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::AvatarData:
case PacketType::BulkAvatarData:
return static_cast<PacketVersion>(AvatarMixerPacketVersion::SoftAttachmentSupport);
case PacketType::ICEServerHeartbeat:
return 18; // ICE Server Heartbeat signing
default:
return 17;
}