Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mumble-voip/mumble.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Connection.h4
-rw-r--r--src/CryptState.cpp137
-rw-r--r--src/CryptState.h6
-rw-r--r--src/Message.cpp47
-rw-r--r--src/Message.h53
-rw-r--r--src/PacketDataStream.h4
-rw-r--r--src/mumble/MainWindow.h3
-rw-r--r--src/mumble/Messages.cpp13
-rw-r--r--src/mumble/ServerHandler.cpp24
-rw-r--r--src/murmur/Messages.cpp26
-rw-r--r--src/murmur/Server.cpp132
-rw-r--r--src/murmur/Server.h9
-rw-r--r--src/tests/Benchmark.cpp4
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];