diff options
-rw-r--r-- | src/Connection.h | 4 | ||||
-rw-r--r-- | src/CryptState.cpp | 137 | ||||
-rw-r--r-- | src/CryptState.h | 6 | ||||
-rw-r--r-- | src/Message.cpp | 47 | ||||
-rw-r--r-- | src/Message.h | 53 | ||||
-rw-r--r-- | src/PacketDataStream.h | 4 | ||||
-rw-r--r-- | src/mumble/MainWindow.h | 3 | ||||
-rw-r--r-- | src/mumble/Messages.cpp | 13 | ||||
-rw-r--r-- | src/mumble/ServerHandler.cpp | 24 | ||||
-rw-r--r-- | src/murmur/Messages.cpp | 26 | ||||
-rw-r--r-- | src/murmur/Server.cpp | 132 | ||||
-rw-r--r-- | src/murmur/Server.h | 9 | ||||
-rw-r--r-- | src/tests/Benchmark.cpp | 4 |
13 files changed, 357 insertions, 105 deletions
diff --git a/src/Connection.h b/src/Connection.h index a40c1c811..abcf8f4f9 100644 --- a/src/Connection.h +++ b/src/Connection.h @@ -63,8 +63,8 @@ class Connection : public QObject { void disconnectSocket(); void forceFlush(); int activityTime() const; - - CryptState csIn, csOut; + + CryptState csCrypt; QList<QSslCertificate> peerCertificateChain() const; QSslCipher sessionCipher() const; diff --git a/src/CryptState.cpp b/src/CryptState.cpp index 465826901..23f1c52f5 100644 --- a/src/CryptState.cpp +++ b/src/CryptState.cpp @@ -28,6 +28,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* + * This code implements OCB-AES128. + * In the US, OCB is covered by patents. The inventor has given a license + * to all programs distributed under the GPL. + * Mumble is BSD (revised) licensed, meaning you can use the code in a + * closed-source program. If you do, you'll have to either replace + * OCB with something else or get yourself a license. + */ + #include "CryptState.h" CryptState::CryptState() { @@ -42,7 +51,8 @@ void CryptState::genKey() { RAND_bytes(raw_key, AES_BLOCK_SIZE); RAND_bytes(encrypt_iv, AES_BLOCK_SIZE); RAND_bytes(decrypt_iv, AES_BLOCK_SIZE); - AES_set_encrypt_key(raw_key, 128, &crypt_key); + AES_set_encrypt_key(raw_key, 128, &encrypt_key); + AES_set_decrypt_key(raw_key, 128, &decrypt_key); bInit = true; } @@ -50,7 +60,8 @@ void CryptState::setKey(const unsigned char *rkey, const unsigned char *eiv, con memcpy(raw_key, rkey, AES_BLOCK_SIZE); memcpy(encrypt_iv, eiv, AES_BLOCK_SIZE); memcpy(decrypt_iv, div, AES_BLOCK_SIZE); - AES_set_encrypt_key(raw_key, 128, &crypt_key); + AES_set_encrypt_key(raw_key, 128, &encrypt_key); + AES_set_decrypt_key(raw_key, 128, &decrypt_key); bInit = true; } @@ -59,20 +70,19 @@ void CryptState::setDecryptIV(const unsigned char *iv) { } void CryptState::encrypt(const unsigned char *source, unsigned char *dst, unsigned int plain_length) { - unsigned int crypted_length = plain_length + 4; - + unsigned char tag[AES_BLOCK_SIZE]; + // First, increase our IV. - for(int i=0;i<AES_BLOCK_SIZE;i++) + for (int i=0;i<AES_BLOCK_SIZE;i++) if (++encrypt_iv[i]) break; - + + ocb_encrypt(source, dst+4, plain_length, encrypt_iv, tag); + dst[0] = encrypt_iv[0]; - - // Checksum (cheeze it) - dst[1] = encrypt_iv[1]; - dst[2] = encrypt_iv[2]; - dst[3] = encrypt_iv[3]; - memcpy(dst + 4, source, plain_length); + dst[1] = tag[0]; + dst[2] = tag[1]; + dst[3] = tag[2]; } bool CryptState::decrypt(const unsigned char *source, unsigned char *dst, unsigned int crypted_length) { @@ -80,7 +90,7 @@ bool CryptState::decrypt(const unsigned char *source, unsigned char *dst, unsign return false; unsigned int plain_length = crypted_length - 4; - + unsigned char saveiv[AES_BLOCK_SIZE]; unsigned char ivbyte = source[0]; @@ -89,19 +99,114 @@ bool CryptState::decrypt(const unsigned char *source, unsigned char *dst, unsign if (ivbyte > decrypt_iv[0]) { decrypt_iv[0] = ivbyte; } else if (ivbyte < decrypt_iv[0]) { - for(int i=1;i<AES_BLOCK_SIZE;i++) + for (int i=1;i<AES_BLOCK_SIZE;i++) if (++decrypt_iv[i]) break; decrypt_iv[0] = ivbyte; } else { return false; } - if (memcmp(source+1, & decrypt_iv[1], 3) != 0) { + + unsigned char tag[AES_BLOCK_SIZE]; + ocb_decrypt(source+4, dst, plain_length, decrypt_iv, tag); + + if (memcmp(tag, source+1, 3) != 0) { memcpy(decrypt_iv, saveiv, AES_BLOCK_SIZE); return false; } - memcpy(dst, source + 4, plain_length); tLastGood.restart(); return true; } +static void inline XOR(unsigned char *dst, const unsigned char *a, const unsigned char *b) { + for (int i=0;i<AES_BLOCK_SIZE;i++) + dst[i] = a[i] ^ b[i]; +} + +static void inline S2(unsigned char *block) { + unsigned char carry = block[0] >> 7; + for (int i=0;i<AES_BLOCK_SIZE-1;i++) + block[i] = (block[i] << 1) | (block[i+1] >> 7); + block[AES_BLOCK_SIZE-1] = (block[AES_BLOCK_SIZE-1] << 1) ^(carry * 0x87); +} + +static void inline S3(unsigned char *block) { + unsigned char carry = block[0] >> 7; + for (int i=0;i<AES_BLOCK_SIZE-1;i++) + block[i] ^= (block[i] << 1) | (block[i+1] >> 7); + block[AES_BLOCK_SIZE-1] ^= (block[AES_BLOCK_SIZE-1] << 1) ^(carry * 0x87); +} + +void CryptState::ocb_encrypt(const unsigned char *plain, unsigned char *encrypted, unsigned int len, const unsigned char *nonce, unsigned char *tag) { + unsigned char checksum[AES_BLOCK_SIZE]; + unsigned char delta[AES_BLOCK_SIZE]; + unsigned char tmp[AES_BLOCK_SIZE]; + unsigned char pad[AES_BLOCK_SIZE]; + + // Initialize + AES_encrypt(nonce, delta, &encrypt_key); + memset(checksum, 0, AES_BLOCK_SIZE); + + while (len > AES_BLOCK_SIZE) { + S2(delta); + XOR(tmp, delta, plain); + AES_encrypt(tmp, tmp, &encrypt_key); + XOR(encrypted, delta, tmp); + XOR(checksum, checksum, plain); + len -= AES_BLOCK_SIZE; + plain += AES_BLOCK_SIZE; + encrypted += AES_BLOCK_SIZE; + } + + S2(delta); + memset(tmp, 0, AES_BLOCK_SIZE); + tmp[AES_BLOCK_SIZE - 1] = len * 8; + XOR(tmp, tmp, delta); + AES_encrypt(tmp, pad, &encrypt_key); + memcpy(tmp, plain, len); + memcpy(tmp+len, pad+len, AES_BLOCK_SIZE - len); + XOR(checksum, checksum, tmp); + XOR(tmp, pad, tmp); + memcpy(encrypted, tmp, len); + + S3(delta); + XOR(tmp, delta, checksum); + AES_encrypt(tmp, tag, &encrypt_key); +} + +void CryptState::ocb_decrypt(const unsigned char *encrypted, unsigned char *plain, unsigned int len, const unsigned char *nonce, unsigned char *tag) { + unsigned char checksum[AES_BLOCK_SIZE]; + unsigned char delta[AES_BLOCK_SIZE]; + unsigned char tmp[AES_BLOCK_SIZE]; + unsigned char pad[AES_BLOCK_SIZE]; + + // Initialize + AES_encrypt(nonce, delta, &encrypt_key); + memset(checksum, 0, AES_BLOCK_SIZE); + + while (len > AES_BLOCK_SIZE) { + S2(delta); + XOR(tmp, delta, encrypted); + AES_decrypt(tmp, tmp, &decrypt_key); + XOR(plain, delta, tmp); + XOR(checksum, checksum, plain); + len -= AES_BLOCK_SIZE; + plain += AES_BLOCK_SIZE; + encrypted += AES_BLOCK_SIZE; + } + + S2(delta); + memset(tmp, 0, AES_BLOCK_SIZE); + tmp[AES_BLOCK_SIZE - 1] = len * 8; + XOR(tmp, tmp, delta); + AES_encrypt(tmp, pad, &encrypt_key); + memset(tmp, 0, AES_BLOCK_SIZE); + memcpy(tmp, encrypted, len); + XOR(tmp, tmp, pad); + XOR(checksum, checksum, tmp); + memcpy(plain, tmp, len); + + S3(delta); + XOR(tmp, delta, checksum); + AES_encrypt(tmp, tag, &encrypt_key); +} diff --git a/src/CryptState.h b/src/CryptState.h index ae636ccf0..de664fc4a 100644 --- a/src/CryptState.h +++ b/src/CryptState.h @@ -40,7 +40,8 @@ class CryptState { unsigned char encrypt_iv[AES_BLOCK_SIZE]; unsigned char decrypt_iv[AES_BLOCK_SIZE]; - AES_KEY crypt_key; + AES_KEY encrypt_key; + AES_KEY decrypt_key; Timer tLastGood; Timer tLastRequest; bool bInit; @@ -51,6 +52,9 @@ class CryptState { void setKey(const unsigned char *rkey, const unsigned char *eiv, const unsigned char *div); void setDecryptIV(const unsigned char *iv); + void ocb_encrypt(const unsigned char *plain, unsigned char *encrypted, unsigned int len, const unsigned char *nonce, unsigned char *tag); + void ocb_decrypt(const unsigned char *encrypted, unsigned char *plain, unsigned int len, const unsigned char *nonce, unsigned char *tag); + bool decrypt(const unsigned char *source, unsigned char *dst, unsigned int crypted_length); void encrypt(const unsigned char *source, unsigned char *dst, unsigned int plain_length); }; diff --git a/src/Message.cpp b/src/Message.cpp index 08a95d08f..80c14a90f 100644 --- a/src/Message.cpp +++ b/src/Message.cpp @@ -139,8 +139,14 @@ Message *Message::networkToMessage(PacketDataStream &qdsIn) { case PlayerTexture: mMsg = new MessageTexture(); break; + case CryptSetup: + mMsg = new MessageCryptSetup(); + break; + case CryptSync: + mMsg = new MessageCryptSync(); + break; default: - qWarning("Message: %d[%d] is unknown type", iMessageType, uiSession); + qWarning("Message: Type %d (session %d, size %d) is unknown type", iMessageType, uiSession, qdsIn.capacity()); } if (mMsg) { mMsg->uiSession=uiSession; @@ -148,15 +154,15 @@ Message *Message::networkToMessage(PacketDataStream &qdsIn) { if (! qdsIn.isValid()) { delete mMsg; mMsg = NULL; - qWarning("Message: %d[%d] Corrupt or short packet", iMessageType, uiSession); + qWarning("Message: Type %d (session %d, size %d) corrupt or short packet", iMessageType, uiSession, qdsIn.capacity()); } else if (qdsIn.left() != 0) { delete mMsg; mMsg = NULL; - qWarning("Message: %d[%d] Long packet: %d/%d leftover bytes", iMessageType, uiSession, qdsIn.left(), qdsIn.size()); + qWarning("Message: Type %d (session %d) Long packet: %d/%d leftover bytes", iMessageType, uiSession, qdsIn.left(), qdsIn.size()); } else if (! mMsg->isValid()) { delete mMsg; mMsg = NULL; - qWarning("Message: %d[%d] Failed to validate", iMessageType, uiSession); + qWarning("Message: Type %d (session %d, size %d) failed to validate", iMessageType, uiSession, qdsIn.capacity()); } } @@ -237,6 +243,12 @@ void MessageHandler::dispatch(Connection *cCon, Message *msg) { case Message::PlayerTexture: msgTexture(cCon, static_cast<MessageTexture *>(msg)); break; + case Message::CryptSetup: + msgCryptSetup(cCon, static_cast<MessageCryptSetup *>(msg)); + break; + case Message::CryptSync: + msgCryptSync(cCon, static_cast<MessageCryptSync *>(msg)); + break; default: qFatal("MessageHandler called with unknown message type %d", msg->messageType()); } @@ -520,6 +532,33 @@ void MessageTexture::restoreStream(PacketDataStream &qdsIn) { qdsIn >> qbaTexture; } +void MessageCryptSetup::saveStream(PacketDataStream &qdsOut) const { + qdsOut << qbaKey << qbaServerNonce << qbaClientNonce; +} + +void MessageCryptSetup::restoreStream(PacketDataStream &qdsIn) { + qdsIn >> qbaKey >> qbaServerNonce >> qbaClientNonce; +} + +bool MessageCryptSetup::isValid() const { + return ((qbaKey.size() == AES_BLOCK_SIZE) && + (qbaClientNonce.size() == AES_BLOCK_SIZE) && + (qbaServerNonce.size() == AES_BLOCK_SIZE)); +} + + +void MessageCryptSync::saveStream(PacketDataStream &qdsOut) const { + qdsOut << qbaNonce; +} + +void MessageCryptSync::restoreStream(PacketDataStream &qdsIn) { + qdsIn >> qbaNonce; +} + +bool MessageCryptSync::isValid() const { + return (qbaNonce.isEmpty() || (qbaNonce.size() == AES_BLOCK_SIZE)); +} + PacketDataStream & operator<< (PacketDataStream & out, const MessageEditACL::GroupStruct &gs) { out << gs.qsName; out << gs.bInherited; diff --git a/src/Message.h b/src/Message.h index d7cb6ad2f..aae09fc95 100644 --- a/src/Message.h +++ b/src/Message.h @@ -43,13 +43,12 @@ class Message { virtual void saveStream(PacketDataStream &) const; virtual void restoreStream(PacketDataStream &); public: - enum MessageType { ServerReject, ServerAuthenticate, Speex, ServerSync, ServerJoin, ServerLeave, ServerBanList, PlayerMute, PlayerDeaf, PlayerKick, PlayerRename, PlayerBan, PlayerMove, PlayerSelfMuteDeaf, ChannelAdd, ChannelRemove, ChannelMove, ChannelLink, PermissionDenied, EditACL, QueryUsers, Ping, TextMessage, PlayerTexture }; + enum MessageType { ServerReject, ServerAuthenticate, Speex, ServerSync, ServerJoin, ServerLeave, ServerBanList, PlayerMute, PlayerDeaf, PlayerKick, PlayerRename, PlayerBan, PlayerMove, PlayerSelfMuteDeaf, ChannelAdd, ChannelRemove, ChannelMove, ChannelLink, PermissionDenied, EditACL, QueryUsers, Ping, TextMessage, PlayerTexture, CryptSetup, CryptSync }; unsigned int uiSession; Message(); virtual ~Message(); virtual Message::MessageType messageType() const = 0; -// virtual void process(Connection *) = 0; virtual bool isValid() const; void messageToNetwork(QByteArray &) const; @@ -70,7 +69,6 @@ class MessageSpeex : public Message { Message::MessageType messageType() const { return Speex; }; - //void process(Connection *); bool isValid() const; }; @@ -87,7 +85,6 @@ class MessageServerAuthenticate : public Message { Message::MessageType messageType() const { return ServerAuthenticate; }; - //void process(Connection *); }; class MessagePing : public Message { @@ -99,7 +96,6 @@ class MessagePing : public Message { Message::MessageType messageType() const { return Ping; }; - //void process(Connection *); }; class MessageServerReject : public Message { @@ -113,7 +109,6 @@ class MessageServerReject : public Message { Message::MessageType messageType() const { return ServerReject; }; - //void process(Connection *); }; class MessageServerSync : public Message { @@ -126,7 +121,6 @@ class MessageServerSync : public Message { Message::MessageType messageType() const { return ServerSync; }; - //void process(Connection *); }; class MessageServerJoin : public Message { @@ -139,7 +133,6 @@ class MessageServerJoin : public Message { Message::MessageType messageType() const { return ServerJoin; }; - //void process(Connection *); }; class MessageServerLeave : public Message { @@ -147,7 +140,6 @@ class MessageServerLeave : public Message { Message::MessageType messageType() const { return ServerLeave; }; - //void process(Connection *); }; class MessagePlayerMute : public Message { @@ -160,7 +152,6 @@ class MessagePlayerMute : public Message { Message::MessageType messageType() const { return PlayerMute; }; - //void process(Connection *); }; class MessagePlayerDeaf : public Message { @@ -173,7 +164,6 @@ class MessagePlayerDeaf : public Message { Message::MessageType messageType() const { return PlayerDeaf; }; - //void process(Connection *); }; class MessagePlayerSelfMuteDeaf : public Message { @@ -186,7 +176,6 @@ class MessagePlayerSelfMuteDeaf : public Message { Message::MessageType messageType() const { return PlayerSelfMuteDeaf; }; - //void process(Connection *); }; class MessagePlayerKick : public Message { @@ -199,7 +188,6 @@ class MessagePlayerKick : public Message { Message::MessageType messageType() const { return PlayerKick; }; - //void process(Connection *); }; class MessagePlayerBan : public Message { @@ -212,7 +200,6 @@ class MessagePlayerBan : public Message { Message::MessageType messageType() const { return PlayerBan; }; - //void process(Connection *); }; class MessagePlayerMove : public Message { @@ -225,7 +212,6 @@ class MessagePlayerMove : public Message { Message::MessageType messageType() const { return PlayerMove; }; - //void process(Connection *); }; class MessagePlayerRename : public Message { @@ -237,7 +223,6 @@ class MessagePlayerRename : public Message { Message::MessageType messageType() const { return PlayerRename; }; - //void process(Connection *); }; class MessageChannelAdd : public Message { @@ -251,7 +236,6 @@ class MessageChannelAdd : public Message { Message::MessageType messageType() const { return ChannelAdd; }; - //void process(Connection *); }; class MessageChannelRemove : public Message { @@ -263,7 +247,6 @@ class MessageChannelRemove : public Message { Message::MessageType messageType() const { return ChannelRemove; }; - //void process(Connection *); }; class MessageChannelMove : public Message { @@ -276,7 +259,6 @@ class MessageChannelMove : public Message { Message::MessageType messageType() const { return ChannelMove; }; - //void process(Connection *); }; class MessageChannelLink : public Message { @@ -293,7 +275,6 @@ class MessageChannelLink : public Message { Message::MessageType messageType() const { return ChannelLink; }; - //void process(Connection *); }; class MessageServerBanList : public Message { @@ -306,7 +287,6 @@ class MessageServerBanList : public Message { Message::MessageType messageType() const { return ServerBanList; }; - //void process(Connection *); }; class MessageTextMessage : public Message { @@ -319,7 +299,6 @@ class MessageTextMessage : public Message { Message::MessageType messageType() const { return TextMessage; }; - //void process(Connection *); }; class MessagePermissionDenied : public Message { @@ -331,7 +310,6 @@ class MessagePermissionDenied : public Message { Message::MessageType messageType() const { return PermissionDenied; }; - //void process(Connection *); }; class MessageEditACL : public Message { @@ -367,7 +345,6 @@ class MessageEditACL : public Message { Message::MessageType messageType() const { return EditACL; }; - //void process(Connection *); }; PacketDataStream & operator<< (PacketDataStream & out, const MessageEditACL::GroupStruct &gs); @@ -385,7 +362,6 @@ class MessageQueryUsers : public Message { Message::MessageType messageType() const { return QueryUsers; }; - //void process(Connection *); bool isValid() const; }; @@ -399,7 +375,30 @@ class MessageTexture : public Message { Message::MessageType messageType() const { return PlayerTexture; } - //void process(Connection *); +}; + +class MessageCryptSetup : public Message { + protected: + void saveStream(PacketDataStream &) const; + void restoreStream(PacketDataStream &); + public: + QByteArray qbaKey, qbaClientNonce, qbaServerNonce; + Message::MessageType messageType() const { + return CryptSetup; + } + bool isValid() const; +}; + +class MessageCryptSync : public Message { + protected: + void saveStream(PacketDataStream &) const; + void restoreStream(PacketDataStream &); + public: + QByteArray qbaNonce; + Message::MessageType messageType() const { + return PlayerTexture; + } + bool isValid() const; }; class MessageHandler { @@ -428,6 +427,8 @@ class MessageHandler { virtual void msgEditACL(Connection *, MessageEditACL *) = 0; virtual void msgQueryUsers(Connection *, MessageQueryUsers *) = 0; virtual void msgTexture(Connection *, MessageTexture *) = 0; + virtual void msgCryptSetup(Connection *, MessageCryptSetup *) = 0; + virtual void msgCryptSync(Connection *, MessageCryptSync *) = 0; void dispatch(Connection *, Message *); public: virtual ~MessageHandler() { }; diff --git a/src/PacketDataStream.h b/src/PacketDataStream.h index 8fb6c1c4e..6ee6ec34e 100644 --- a/src/PacketDataStream.h +++ b/src/PacketDataStream.h @@ -46,6 +46,10 @@ class PacketDataStream { return offset; } + quint32 capacity() const { + return maxsize; + } + bool isValid() const { return ok; } diff --git a/src/mumble/MainWindow.h b/src/mumble/MainWindow.h index 9eb031d24..a9483ed73 100644 --- a/src/mumble/MainWindow.h +++ b/src/mumble/MainWindow.h @@ -163,6 +163,9 @@ class MainWindow : public QMainWindow, public MessageHandler { virtual void msgEditACL(Connection *, MessageEditACL *); virtual void msgQueryUsers(Connection *, MessageQueryUsers *); virtual void msgTexture(Connection *, MessageTexture *); + virtual void msgCryptSetup(Connection *, MessageCryptSetup *); + virtual void msgCryptSync(Connection *, MessageCryptSync *); + }; #else diff --git a/src/mumble/Messages.cpp b/src/mumble/Messages.cpp index ebf5d59f9..ee8d10a21 100644 --- a/src/mumble/Messages.cpp +++ b/src/mumble/Messages.cpp @@ -316,3 +316,16 @@ void MainWindow::msgTexture(Connection *, MessageTexture *msg) { if (! msg->qbaTexture.isEmpty()) g.o->textureResponse(msg->iPlayerId,msg->qbaTexture); } + +void MainWindow::msgCryptSetup(Connection *cCon, MessageCryptSetup *msg) { + cCon->csCrypt.setKey(reinterpret_cast<const unsigned char *>(msg->qbaKey.constData()), reinterpret_cast<const unsigned char *>(msg->qbaClientNonce.constData()), reinterpret_cast<const unsigned char *>(msg->qbaServerNonce.constData())); +} + +void MainWindow::msgCryptSync(Connection *cCon, MessageCryptSync *msg) { + if (msg->qbaNonce.isEmpty()) { + msg->qbaNonce = QByteArray(reinterpret_cast<const char *>(cCon->csCrypt.encrypt_iv), AES_BLOCK_SIZE); + g.sh->sendMessage(msg); + } else if (msg->qbaNonce.size() == AES_BLOCK_SIZE) { + memcpy(cCon->csCrypt.decrypt_iv, msg->qbaNonce.constData(), AES_BLOCK_SIZE); + } +} diff --git a/src/mumble/ServerHandler.cpp b/src/mumble/ServerHandler.cpp index 791910804..45592124a 100644 --- a/src/mumble/ServerHandler.cpp +++ b/src/mumble/ServerHandler.cpp @@ -87,16 +87,34 @@ void ServerHandler::customEvent(QEvent *evt) { void ServerHandler::udpReady() { while (qusUdp->hasPendingDatagrams()) { - char buffer[65536]; + char encrypted[65536]; + char buffer[65535]; quint32 buflen = qusUdp->pendingDatagramSize(); QHostAddress senderAddr; quint16 senderPort; - qusUdp->readDatagram(buffer, qMin(65536U, buflen), &senderAddr, &senderPort); + qusUdp->readDatagram(encrypted, qMin(65536U, buflen), &senderAddr, &senderPort); if (!(senderAddr == qhaRemote) || (senderPort != iPort)) continue; - PacketDataStream pds(buffer, buflen); + if (! cConnection) + continue; + + if (! cConnection->csCrypt.isValid()) + continue; + + if (! cConnection->csCrypt.decrypt(reinterpret_cast<const unsigned char *>(encrypted), reinterpret_cast<unsigned char *>(buffer), buflen)) { + if (cConnection->csCrypt.tLastGood.elapsed() > 5000000ULL) { + if (cConnection->csCrypt.tLastRequest.elapsed() > 5000000ULL) { + cConnection->csCrypt.tLastRequest.restart(); + MessageCryptSync mcs; + sendMessage(&mcs); + } + } + continue; + } + + PacketDataStream pds(buffer, buflen-4); quint32 msgType, uiSession; pds >> msgType >> uiSession; diff --git a/src/murmur/Messages.cpp b/src/murmur/Messages.cpp index 1ad1b0987..880474cd9 100644 --- a/src/murmur/Messages.cpp +++ b/src/murmur/Messages.cpp @@ -151,6 +151,15 @@ void Server::msgServerAuthenticate(Connection *cCon, MessageServerAuthenticate * uOld->disconnectSocket(); } + // Setup UDP encryption + uSource->csCrypt.genKey(); + MessageCryptSetup mcs; + mcs.uiSession = uSource->uiSession; + mcs.qbaKey = QByteArray(reinterpret_cast<const char *>(uSource->csCrypt.raw_key), AES_BLOCK_SIZE); + mcs.qbaServerNonce = QByteArray(reinterpret_cast<const char *>(uSource->csCrypt.encrypt_iv), AES_BLOCK_SIZE); + mcs.qbaClientNonce = QByteArray(reinterpret_cast<const char *>(uSource->csCrypt.decrypt_iv), AES_BLOCK_SIZE); + sendMessage(cCon, &mcs); + int lchan = readLastChannel(uSource); Channel *lc = qhChannels.value(lchan); if (! lc) @@ -296,6 +305,10 @@ void Server::msgPlayerRename(Connection *cCon, MessagePlayerRename *) { cCon->disconnectSocket(); } +void Server::msgCryptSetup(Connection *cCon, MessageCryptSetup *) { + cCon->disconnectSocket(); +} + void Server::msgSpeex(Connection *cCon, MessageSpeex *msg) { MSG_SETUP(Player::Authenticated); fakeUdpPacket(msg, cCon); @@ -810,3 +823,16 @@ void Server::msgTexture(Connection *cCon, MessageTexture *msg) { if (! msg->qbaTexture.isEmpty()) sendMessage(cCon, msg); } + +void Server::msgCryptSync(Connection *cCon, MessageCryptSync *msg) { + MSG_SETUP(Player::Authenticated); + if (msg->qbaNonce.isEmpty()) { + log(uSource, "Requested crypt-nonce resync"); + msg->qbaNonce = QByteArray(reinterpret_cast<const char *>(uSource->csCrypt.encrypt_iv), AES_BLOCK_SIZE); + sendMessage(cCon, msg); + } else if (msg->qbaNonce.size() == AES_BLOCK_SIZE) { + memcpy(uSource->csCrypt.decrypt_iv, msg->qbaNonce.constData(), AES_BLOCK_SIZE); + } else { + cCon->disconnectSocket(); + } +} diff --git a/src/murmur/Server.cpp b/src/murmur/Server.cpp index 0d906b465..8a1a7cbe9 100644 --- a/src/murmur/Server.cpp +++ b/src/murmur/Server.cpp @@ -68,7 +68,7 @@ QSslSocket *SslServer::nextPendingSSLConnection() { User::User(Server *p, QSslSocket *socket) : Connection(p, socket), Player() { saiUdpAddress.sin_port = 0; - saiUdpAddress.sin_addr.s_addr = 0; + saiUdpAddress.sin_addr.s_addr = htonl(socket->peerAddress().toIPv4Address()); saiUdpAddress.sin_family = AF_INET; } @@ -112,6 +112,7 @@ Server::Server(int snum, QObject *p) : QThread(p) { } connect(this, SIGNAL(tcpTransmit(QByteArray, unsigned int)), this, SLOT(tcpTransmitData(QByteArray, unsigned int)), Qt::QueuedConnection); + connect(this, SIGNAL(reqSync(unsigned int)), this, SLOT(doSync(unsigned int))); for (int i=1;i<5000;i++) qqIds.enqueue(i); @@ -212,10 +213,11 @@ void Server::run() { log("Starting UDP Thread"); qint32 len; + char encrypted[65535]; char buffer[65535]; - quint32 msgType; - int uiSession; + quint32 msgType = 0; + unsigned int uiSession = 0; sockaddr_in from; #ifdef Q_OS_UNIX @@ -226,41 +228,69 @@ void Server::run() { forever { fromlen = sizeof(from); - len=::recvfrom(sUdpSocket, buffer, 65535, 0, reinterpret_cast<struct sockaddr *>(&from), &fromlen); + len=::recvfrom(sUdpSocket, encrypted, 65535, 0, reinterpret_cast<struct sockaddr *>(&from), &fromlen); if (len == 0) { break; } else if (len == SOCKET_ERROR) { log("Failure during UDP Socket read"); break; + } else if (len < 6) { + // 4 bytes crypt header + type + session + continue; } QReadLocker rl(&qrwlUsers); - PacketDataStream pds(buffer, len); - pds >> msgType >> uiSession; + quint64 key = (static_cast<unsigned long long>(from.sin_addr.s_addr) << 16) ^ from.sin_port; + PacketDataStream pds(buffer, len - 4); + + User *u = qhPeerUsers.value(key); + if (u) { + if (! checkDecrypt(u, encrypted, buffer, len)) + continue; + pds >> msgType >> uiSession; + if (u->uiSession != uiSession) + continue; + } else { + // Unknown peer + foreach(User *usr, qhHostUsers.value(from.sin_addr.s_addr)) { + pds.rewind(); + if (usr->csCrypt.isValid() && checkDecrypt(usr, encrypted, buffer, len)) { + pds >> msgType >> uiSession; + if (usr->uiSession == uiSession) { + // Every time we relock, reverify users' existance. + // The main thread might delete the user while the lock isn't held. + rl.unlock(); + qrwlUsers.lockForWrite(); + if (qhUsers.contains(uiSession)) { + u = usr; + qhHostUsers[from.sin_addr.s_addr].remove(u); + qhPeerUsers.insert(key, u); + u->saiUdpAddress.sin_port = from.sin_port; + qrwlUsers.unlock(); + rl.relock(); + if (! qhUsers.contains(uiSession)) + u = NULL; + } + } + + } + } + if (! u) + continue; + len -= 4; + } if ((msgType != Message::Speex) && (msgType != Message::Ping)) continue; if (! pds.isValid()) continue; - User *u = qhUsers.value(uiSession); - if (! u) - continue; - - if ((from.sin_addr.s_addr != u->saiUdpAddress.sin_addr.s_addr) || (from.sin_port != u->saiUdpAddress.sin_port)) { - if (u->saiUdpAddress.sin_port != 0) - continue; - if (htonl(u->peerAddress().toIPv4Address()) != from.sin_addr.s_addr) - continue; - u->saiUdpAddress.sin_addr.s_addr = from.sin_addr.s_addr; - u->saiUdpAddress.sin_port = from.sin_port; - } - - if (msgType == Message::Ping) - ::sendto(sUdpSocket, buffer, len, 0, reinterpret_cast<struct sockaddr *>(&from), sizeof(from)); - else { + if (msgType == Message::Ping) { + QByteArray qba; + sendMessage(u, buffer, len, qba); + } else { processMsg(pds, qhUsers.value(uiSession)); } } @@ -281,38 +311,26 @@ void Server::fakeUdpPacket(Message *msg, Connection *source) { processMsg(pds, source); } -#include <openssl/aes.h> +bool Server::checkDecrypt(User *u, const char *encrypted, char *plain, unsigned int len) { + if (u->csCrypt.isValid() && u->csCrypt.decrypt(reinterpret_cast<const unsigned char *>(encrypted), reinterpret_cast<unsigned char *>(plain), len)) + return true; -class AESEncrypt { - public: - AES_KEY enc; - unsigned char iv[AES_BLOCK_SIZE]; - AESEncrypt(); - void doEncrypt(const char *data, int len); -}; - -AESEncrypt aes; - -AESEncrypt::AESEncrypt() { - for(int i=0;i<AES_BLOCK_SIZE;i++) - iv[i]=i; - AES_set_encrypt_key(iv, 128, &enc); -} - -void AESEncrypt::doEncrypt(const char *data, int len) { - char buffer[len]; - iv[0]++; - iv[1]++; - iv[3]++; - iv[2]++; - int num = 0; - AES_ofb128_encrypt(reinterpret_cast<const unsigned char *>(data), reinterpret_cast<unsigned char *>(buffer), len, &enc, iv, &num); + qWarning("Crypt No Good. And %lld", u->csCrypt.tLastGood.elapsed()); + qWarning("with a hint of %lld", u->csCrypt.tLastRequest.elapsed()); + if (u->csCrypt.tLastGood.elapsed() > 5000000ULL) { + if (u->csCrypt.tLastRequest.elapsed() > 5000000ULL) { + u->csCrypt.tLastRequest.restart(); + emit reqSync(u->uiSession); + } + } + return false; } void Server::sendMessage(User *u, const char *data, int len, QByteArray &cache) { - if (u->saiUdpAddress.sin_port != 0) { -// aes.doEncrypt(data, len); - ::sendto(sUdpSocket, data, len, 0, reinterpret_cast<struct sockaddr *>(& u->saiUdpAddress), sizeof(u->saiUdpAddress)); + if ((u->saiUdpAddress.sin_port != 0) && u->csCrypt.isValid()) { + char buffer[len+4]; + u->csCrypt.encrypt(reinterpret_cast<const unsigned char *>(data), reinterpret_cast<unsigned char *>(buffer), len); + ::sendto(sUdpSocket, buffer, len+4, 0, reinterpret_cast<struct sockaddr *>(& u->saiUdpAddress), sizeof(u->saiUdpAddress)); } else { if (cache.isEmpty()) cache = QByteArray(data, len); @@ -447,6 +465,7 @@ void Server::newClient() { { QWriteLocker wl(&qrwlUsers); qhUsers.insert(u->uiSession, u); + qhHostUsers[htonl(sock->peerAddress().toIPv4Address())].insert(u); } connect(u, SIGNAL(connectionClosed(QString)), this, SLOT(connectionClosed(QString))); @@ -493,6 +512,10 @@ void Server::connectionClosed(QString reason) { QWriteLocker wl(&qrwlUsers); qhUsers.remove(u->uiSession); + qhHostUsers[u->saiUdpAddress.sin_addr.s_addr].remove(u); + quint64 key = (static_cast<unsigned long long>(u->saiUdpAddress.sin_addr.s_addr) << 16) ^ u->saiUdpAddress.sin_port; + qhPeerUsers.remove(key); + if (u->cChannel) u->cChannel->removePlayer(u); } @@ -550,6 +573,15 @@ void Server::tcpTransmitData(QByteArray a, unsigned int id) { } } +void Server::doSync(unsigned int id) { + User *u = qhUsers.value(id); + if (u) { + log(u, "Requesting crypt-nonce resync"); + MessageCryptSync mcs; + u->sendMessage(&mcs); + } +} + void Server::sendMessage(unsigned int id, Message *mMsg) { Connection *c = qhUsers.value(id); sendMessage(c, mMsg); diff --git a/src/murmur/Server.h b/src/murmur/Server.h index 691202d35..dbfa4cbba 100644 --- a/src/murmur/Server.h +++ b/src/murmur/Server.h @@ -137,7 +137,9 @@ class Server : public QThread, public MessageHandler { void message(QByteArray &, Connection *cCon = NULL); void checkTimeout(); void tcpTransmitData(QByteArray, unsigned int); + void doSync(unsigned int); signals: + void reqSync(unsigned int); void tcpTransmit(QByteArray, unsigned int id); public: int iServerNum; @@ -152,6 +154,8 @@ class Server : public QThread, public MessageHandler { #endif QHash<unsigned int, User *> qhUsers; + QHash<quint64, User *> qhPeerUsers; + QHash<unsigned int, QSet<User *> > qhHostUsers; QHash<unsigned int, Channel *> qhChannels; QReadWriteLock qrwlUsers; ChanACL::ACLCache acCache; @@ -169,6 +173,8 @@ class Server : public QThread, public MessageHandler { void fakeUdpPacket(Message *msg, Connection *source); void run(); + bool checkDecrypt(User *u, const char *encrypted, char *plain, unsigned int cryptlen); + bool hasPermission(Player *p, Channel *c, ChanACL::Perm perm); void clearACLCache(Player *p = NULL); @@ -236,7 +242,8 @@ class Server : public QThread, public MessageHandler { virtual void msgEditACL(Connection *, MessageEditACL *); virtual void msgQueryUsers(Connection *, MessageQueryUsers *); virtual void msgTexture(Connection *, MessageTexture *); - + virtual void msgCryptSetup(Connection *, MessageCryptSetup *); + virtual void msgCryptSync(Connection *, MessageCryptSync *); }; diff --git a/src/tests/Benchmark.cpp b/src/tests/Benchmark.cpp index fa3c7bc60..65b6adc27 100644 --- a/src/tests/Benchmark.cpp +++ b/src/tests/Benchmark.cpp @@ -59,11 +59,11 @@ Client::Client(QObject *p, QHostAddress qha, unsigned short prt, bool send, bool struct utsname uts; uname(&uts); - + static int ctr = 1; QString name = QString("%1.%2").arg(uts.nodename).arg(getpid() * 1000 + ctr); - + ctr++; unsigned char buffer[65535]; |