From 3907c17cf5529034a349a0519fe84baa083a6d77 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 2 Nov 2020 18:49:07 -0500 Subject: [PATCH] handle TunnelBuildMessage for ECIES router --- libi2pd/I2NPProtocol.cpp | 53 +++++++++++++++++++++++++++++++-------- libi2pd/I2NPProtocol.h | 1 + libi2pd/RouterContext.cpp | 35 ++++++++++++++++++++++++-- libi2pd/RouterContext.h | 8 ++++-- libi2pd/TunnelConfig.cpp | 11 +++++--- libi2pd/TunnelConfig.h | 2 ++ 6 files changed, 93 insertions(+), 17 deletions(-) diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index 36e7a763..7778b9e4 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -363,37 +363,70 @@ namespace i2p BN_CTX * ctx = BN_CTX_new (); i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText, ctx); BN_CTX_free (ctx); + uint8_t retCode = 0; + bool isECIES = i2p::context.IsECIES (); // replace record to reply if (i2p::context.AcceptsTunnels () && i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels && !i2p::transport::transports.IsBandwidthExceeded () && !i2p::transport::transports.IsTransitBandwidthExceeded ()) { - auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( + auto transitTunnel = isECIES ? + i2p::tunnel::CreateTransitTunnel ( + bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), + clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, + clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, + clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80, + clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) : + i2p::tunnel::CreateTransitTunnel ( bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80, - clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET ] & 0x40); + clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40); i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); - record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 0; } else - record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30) + retCode = 30; // always reject with bandwidth reason (30) - //TODO: fill filler - SHA256 (record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1, // + 1 byte of ret - record + BUILD_RESPONSE_RECORD_HASH_OFFSET); + if (isECIES) + { + memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options + record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; + } + else + { + record[BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; + SHA256 (record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1, // + 1 byte of ret + record + BUILD_RESPONSE_RECORD_HASH_OFFSET); + } // encrypt reply i2p::crypto::CBCEncryption encryption; for (int j = 0; j < num; j++) { - encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); - encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET); uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; - encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); + if (isECIES && j == i) + { + uint8_t nonce[12]; + memset (nonce, 0, 12); + auto noiseState = std::move (i2p::context.GetCurrentNoiseState ()); + if (!noiseState || !i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, + noiseState->m_H, 32, noiseState->m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt + { + LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed"); + return false; + } + } + else + { + encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); + encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET); + encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); + } } return true; } diff --git a/libi2pd/I2NPProtocol.h b/libi2pd/I2NPProtocol.h index 03f0f439..b8fbb303 100644 --- a/libi2pd/I2NPProtocol.h +++ b/libi2pd/I2NPProtocol.h @@ -98,6 +98,7 @@ namespace i2p const size_t ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 464; // ECIES BuildResponseRecord + const size_t ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET = 0; const size_t ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET = 511; enum I2NPMessageType diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 69e69d7c..da9b42fc 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -19,6 +19,7 @@ #include "version.h" #include "Log.h" #include "Family.h" +#include "TunnelConfig.h" #include "RouterContext.h" namespace i2p @@ -41,6 +42,13 @@ namespace i2p CreateNewRouter (); m_Decryptor = m_Keys.CreateDecryptor (nullptr); UpdateRouterInfo (); + if (IsECIES ()) + { + auto initState = new i2p::crypto::NoiseSymmetricState (); + i2p::tunnel::InitBuildRequestRecordNoiseState (*initState); + initState->MixHash (GetIdentity ()->GetEncryptionPublicKey (), 32); // h = SHA256(h || hepk) + m_InitialNoiseState.reset (initState); + } } void RouterContext::CreateNewRouter () @@ -673,9 +681,32 @@ namespace i2p return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, true) : false; } - bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const + bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) { - return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, false) : false; + if (!m_Decryptor) return false; + if (IsECIES ()) + { + if (!m_InitialNoiseState) return false; + // m_InitialNoiseState is h = SHA256(h || hepk) + m_CurrentNoiseState.reset (new i2p::crypto::NoiseSymmetricState (*m_InitialNoiseState)); + m_CurrentNoiseState->MixHash (encrypted, 32); // h = SHA256(h || sepk) + uint8_t sharedSecret[32]; + m_Decryptor->Decrypt (encrypted, sharedSecret, ctx, false); + m_CurrentNoiseState->MixKey (sharedSecret); + encrypted += 32; + uint8_t nonce[12]; + memset (nonce, 0, 12); + if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, TUNNEL_BUILD_RECORD_SIZE - 16, + m_CurrentNoiseState->m_H, 32, m_CurrentNoiseState->m_CK, nonce, data, TUNNEL_BUILD_RECORD_SIZE - 16, false)) // decrypt + { + LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed"); + return false; + } + m_CurrentNoiseState->MixHash (encrypted, TUNNEL_BUILD_RECORD_SIZE); // h = SHA256(h || ciphertext) + return true; + } + else + return m_Decryptor->Decrypt (encrypted, data, ctx, false); } i2p::crypto::X25519Keys& RouterContext::GetStaticKeys () diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index 37e1791d..402d3816 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -84,7 +84,7 @@ namespace i2p void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; }; int GetNetID () const { return m_NetID; }; void SetNetID (int netID) { m_NetID = netID; }; - bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const; + bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx); void UpdatePort (int port); // called from Daemon void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon @@ -109,7 +109,9 @@ namespace i2p bool SupportsV4 () const { return m_RouterInfo.IsV4 (); }; void SetSupportsV6 (bool supportsV6); void SetSupportsV4 (bool supportsV4); - + bool IsECIES () const { return GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET; }; + std::unique_ptr& GetCurrentNoiseState () { return m_CurrentNoiseState; }; + void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove void UpdateStats (); void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing @@ -160,6 +162,8 @@ namespace i2p std::mutex m_GarlicMutex; std::unique_ptr m_NTCP2Keys; std::unique_ptr m_StaticKeys; + // for ECIESx25519 + std::unique_ptr m_InitialNoiseState, m_CurrentNoiseState; }; extern RouterContext context; diff --git a/libi2pd/TunnelConfig.cpp b/libi2pd/TunnelConfig.cpp index 61e878d4..c0ebb766 100644 --- a/libi2pd/TunnelConfig.cpp +++ b/libi2pd/TunnelConfig.cpp @@ -127,9 +127,7 @@ namespace tunnel void TunnelHopConfig::EncryptECIES (std::shared_ptr& encryptor, const uint8_t * plainText, uint8_t * encrypted, BN_CTX * ctx) { - static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars - memcpy (m_CK, protocolName, 32); // ck = h = protocol_name || 0 - SHA256 (m_CK, 32, m_H); // h = SHA256(h); + InitBuildRequestRecordNoiseState (*this); uint8_t hepk[32]; encryptor->Encrypt (nullptr, hepk, nullptr, false); MixHash (hepk, 32); // h = SHA256(h || hepk) @@ -150,5 +148,12 @@ namespace tunnel } MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext) } + + void InitBuildRequestRecordNoiseState (i2p::crypto::NoiseSymmetricState& state) + { + static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars + memcpy (state.m_CK, protocolName, 32); // ck = h = protocol_name || 0 + SHA256 (state.m_CK, 32, state.m_H); // h = SHA256(h); + } } } \ No newline at end of file diff --git a/libi2pd/TunnelConfig.h b/libi2pd/TunnelConfig.h index 261394b4..518c1aad 100644 --- a/libi2pd/TunnelConfig.h +++ b/libi2pd/TunnelConfig.h @@ -45,6 +45,8 @@ namespace tunnel const uint8_t * clearText, uint8_t * encrypted, BN_CTX * ctx); }; + void InitBuildRequestRecordNoiseState (i2p::crypto::NoiseSymmetricState& state); + class TunnelConfig { public: