From 5447259e1a2cc7b29c4fc4993a85be3bb145b1fd Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 13 Jun 2018 16:16:23 -0400 Subject: [PATCH] AEAD/ChaCha20/Poly1305 decryption and SessionCreate prcessing --- libi2pd/Crypto.cpp | 23 ++++++--- libi2pd/Crypto.h | 2 +- libi2pd/NTCP2.cpp | 73 ++++++++++++++++++++++++----- libi2pd/NTCP2.h | 7 ++- tests/test-aeadchacha20poly1305.cpp | 9 +++- 5 files changed, 90 insertions(+), 24 deletions(-) diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index 0073faee..bdcf5acd 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -1062,9 +1062,9 @@ namespace crypto // AEAD/ChaCha20/Poly1305 - size_t AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) + bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt) { - if (msgLen + 16 < len) return 0; + if (encrypt && msgLen + 16 < len) return 0; // generate one time poly key uint8_t polyKey[64]; memset(polyKey, 0, sizeof(polyKey)); @@ -1072,6 +1072,7 @@ namespace crypto // encrypt data memcpy (buf, msg, msgLen); chacha20 (buf, msgLen, nonce, key, 1); + // create Poly1305 message std::vector polyMsg(adLen + msgLen + 3*16); size_t offset = 0; @@ -1084,7 +1085,7 @@ namespace crypto rem = 16 - rem; memcpy (polyMsg.data () + offset, padding, rem); offset += rem; } - memcpy (polyMsg.data () + offset, buf, msgLen); offset += msgLen; // encrypted data + memcpy (polyMsg.data () + offset, encrypt ? buf : msg, msgLen); offset += msgLen; // encrypted data rem = msgLen & 0x0F; // %16 if (rem) { @@ -1095,9 +1096,19 @@ namespace crypto htole64buf (polyMsg.data () + offset, adLen); offset += 8; htole64buf (polyMsg.data () + offset, msgLen); offset += 8; - // calculate Poly1305 tag and write in after encrypted data - Poly1305HMAC ((uint32_t *)(buf + msgLen), (uint32_t *)polyKey, polyMsg.data (), offset); - return msgLen + 16; + if (encrypt) + { + // calculate Poly1305 tag and write in after encrypted data + Poly1305HMAC ((uint32_t *)(buf + msgLen), (uint32_t *)polyKey, polyMsg.data (), offset); + } + else + { + uint32_t tag[8]; + // calculate Poly1305 tag + Poly1305HMAC (tag, (uint32_t *)polyKey, polyMsg.data (), offset); + if (memcmp (tag, msg + msgLen, 16)) return false; // compare with provided + } + return true; } // init and terminate diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index c124a200..af4ec1f8 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -255,7 +255,7 @@ namespace crypto }; // AEAD/ChaCha20/Poly1305 - size_t AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); + bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag // init and terminate void InitCrypto (bool precomputation); diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 474ad67d..64636433 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "Log.h" #include "I2PEndian.h" #include "Crypto.h" @@ -50,11 +51,11 @@ namespace transport m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); } - bool NTCP2Session::KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * pub, uint8_t * derived) + void NTCP2Session::KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * pub, uint8_t * derived) { static const char protocolName[] = "Noise_XK_25519_ChaChaPoly_SHA256"; // 32 bytes - uint8_t h[64], ck[33]; - memcpy (ck, protocolName, 32); + uint8_t h[64]; + memcpy (m_CK, protocolName, 32); SHA256 ((const uint8_t *)protocolName, 32, h); // h = SHA256(h || rs) memcpy (h + 32, rs, 32); @@ -69,14 +70,43 @@ namespace transport BN_CTX_free (ctx); // temp_key = HMAC-SHA256(ck, input_key_material) uint8_t tempKey[32]; unsigned int len; - HMAC(EVP_sha256(), ck, 32, inputKeyMaterial, 32, tempKey, &len); - // ck = HMAC-SHA256(temp_key, byte(0x01)) + HMAC(EVP_sha256(), m_CK, 32, inputKeyMaterial, 32, tempKey, &len); + // ck = HMAC-SHA256(temp_key, byte(0x01)) inputKeyMaterial[0] = 1; - HMAC(EVP_sha256(), tempKey, 32, inputKeyMaterial, 1, ck, &len); + HMAC(EVP_sha256(), tempKey, 32, inputKeyMaterial, 1, m_CK, &len); // derived = HMAC-SHA256(temp_key, ck || byte(0x02)) - ck[32] = 2; - HMAC(EVP_sha256(), tempKey, 32, ck, 33, derived, &len); - return true; + m_CK[32] = 2; + HMAC(EVP_sha256(), tempKey, 32, m_CK, 33, derived, &len); + } + + void NTCP2Session::KeyDerivationFunction2 (const uint8_t * pub, const uint8_t * sessionRequest, size_t sessionRequestLen, uint8_t * derived) + { + uint8_t h[64]; + memcpy (h, m_H, 32); + memcpy (h + 32, sessionRequest + 32, 32); // encrypted payload + SHA256 (h, 64, m_H); + int paddingLength = sessionRequestLen - 64; + if (paddingLength > 0) + { + std::vector h1(paddingLength + 32); + memcpy (h1.data (), m_H, 32); + memcpy (h1.data () + 32, sessionRequest + 64, paddingLength); + SHA256 (h1.data (), paddingLength + 32, m_H); + } + // x25519 between remote pub and priv + uint8_t inputKeyMaterial[32]; + BN_CTX * ctx = BN_CTX_new (); + i2p::crypto::GetEd25519 ()->ScalarMul (pub, m_ExpandedPrivateKey, inputKeyMaterial, ctx); + BN_CTX_free (ctx); + // temp_key = HMAC-SHA256(ck, input_key_material) + uint8_t tempKey[32]; unsigned int len; + HMAC(EVP_sha256(), m_CK, 32, inputKeyMaterial, 32, tempKey, &len); + // ck = HMAC-SHA256(temp_key, byte(0x01)) + inputKeyMaterial[0] = 1; + HMAC(EVP_sha256(), tempKey, 32, inputKeyMaterial, 1, m_CK, &len); + // derived = HMAC-SHA256(temp_key, ck || byte(0x02)) + m_CK[32] = 2; + HMAC(EVP_sha256(), tempKey, 32, m_CK, 33, derived, &len); } void NTCP2Session::CreateEphemeralKey (uint8_t * pub) @@ -93,7 +123,8 @@ namespace transport { // create buffer and fill padding auto paddingLength = rand () % (287 - 64); // message length doesn't exceed 287 bytes - m_SessionRequestBuffer = new uint8_t[paddingLength + 64]; + m_SessionRequestBufferLen = paddingLength + 64; + m_SessionRequestBuffer = new uint8_t[m_SessionRequestBufferLen]; RAND_bytes (m_SessionRequestBuffer + 64, paddingLength); // generate key pair (X) uint8_t x[32]; @@ -119,9 +150,9 @@ namespace transport // sign and encrypt options, use m_H as AD uint8_t nonce[12]; memset (nonce, 0, 12); // set nonce to zero - i2p::crypto::AEADChaCha20Poly1305Encrypt (options, 16, m_H, 32, key, nonce, m_SessionRequestBuffer + 32, 32); + i2p::crypto::AEADChaCha20Poly1305 (options, 16, m_H, 32, key, nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt // send message - boost::asio::async_write (m_Socket, boost::asio::buffer (m_SessionRequestBuffer, paddingLength + 64), boost::asio::transfer_all (), + boost::asio::async_write (m_Socket, boost::asio::buffer (m_SessionRequestBuffer, m_SessionRequestBufferLen), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionRequestSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -151,13 +182,29 @@ namespace transport } else { - LogPrint (eLogWarning, "NTCP2: SessionCreated received ", bytes_transferred); + LogPrint (eLogInfo, "NTCP2: SessionCreated received ", bytes_transferred); uint8_t y[32]; // decrypt Y i2p::crypto::CBCDecryption decryption; decryption.SetKey (GetRemoteIdentity ()->GetIdentHash ()); decryption.SetIV (m_IV); decryption.Decrypt (m_SessionCreatedBuffer, 32, y); + // decryption key for next block + uint8_t key[32]; + KeyDerivationFunction2 (y, m_SessionRequestBuffer, m_SessionRequestBufferLen, key); + // decrypt and verify MAC + uint8_t payload[8]; + uint8_t nonce[12]; + memset (nonce, 0, 12); // set nonce to zero + if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 8, m_H, 32, key, nonce, payload, 8, false)) // decrypt + { + // TODO: + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionCreated MAC verification failed "); + Terminate (); + } } } diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index 456f9d3e..b7e83f26 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -29,7 +29,9 @@ namespace transport private: - bool KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * pub, uint8_t * derived); // for SessionRequest + void KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * pub, uint8_t * derived); // for SessionRequest + void KeyDerivationFunction2 (const uint8_t * pub, const uint8_t * sessionRequest, size_t sessionRequestLen, uint8_t * derived); // for SessionCreate + void CreateEphemeralKey (uint8_t * pub); void SendSessionRequest (); @@ -43,8 +45,9 @@ namespace transport bool m_IsEstablished, m_IsTerminated; uint8_t m_ExpandedPrivateKey[64]; // x25519 ephemeral key - uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32]; + uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/; uint8_t * m_SessionRequestBuffer, * m_SessionCreatedBuffer; + size_t m_SessionRequestBufferLen; }; class NTCP2Server diff --git a/tests/test-aeadchacha20poly1305.cpp b/tests/test-aeadchacha20poly1305.cpp index cb9ed5c3..dcd4b4d6 100644 --- a/tests/test-aeadchacha20poly1305.cpp +++ b/tests/test-aeadchacha20poly1305.cpp @@ -43,7 +43,12 @@ uint8_t encrypted[114] = int main () { uint8_t buf[114+16]; - i2p::crypto::AEADChaCha20Poly1305Encrypt ((uint8_t *)text, 114, ad, 12, key, nonce, buf, 114 + 16); + // test encryption + i2p::crypto::AEADChaCha20Poly1305 ((uint8_t *)text, 114, ad, 12, key, nonce, buf, 114 + 16, true); assert (memcmp (buf, encrypted, 114) == 0); - assert(memcmp (buf + 114, tag, 16) == 0); + assert (memcmp (buf + 114, tag, 16) == 0); + // test decryption + uint8_t buf1[114]; + assert (i2p::crypto::AEADChaCha20Poly1305 (buf, 114, ad, 12, key, nonce, buf1, 114, false)); + assert (memcmp (buf1, text, 114) == 0); }