From 9000b3df4edcbe7f2c8afd0e1e30609746311ace Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 5 Jul 2021 14:31:07 -0400 Subject: [PATCH] KDF for short tunnel build messages --- libi2pd/I2NPProtocol.cpp | 28 +++++++++++++++++++--------- libi2pd/TunnelConfig.cpp | 22 +++++++++++++++++----- libi2pd/TunnelConfig.h | 2 +- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index a040e25b..6034e5f4 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -622,17 +622,28 @@ namespace i2p return; } auto& noiseState = i2p::context.GetCurrentNoiseState (); - uint8_t layerKeys[64]; // (layer key, iv key) - i2p::crypto::HKDF (noiseState.m_CK + 32, nullptr, 0, "LayerAndIVKeys", layerKeys); // TODO: correct domain + uint8_t replyKey[32], layerKey[32], ivKey[32]; + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK); + memcpy (replyKey, noiseState.m_CK + 32, 32); + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK); + memcpy (layerKey, noiseState.m_CK + 32, 32); + bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; + if (isEndpoint) + { + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK); + memcpy (ivKey, noiseState.m_CK + 32, 32); + } + else + memcpy (ivKey, noiseState.m_CK , 32); auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - layerKeys, layerKeys + 32, + layerKey, ivKey, clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); - if (clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) + if (isEndpoint) { // we are endpoint, create OutboundTunnelBuildReply auto otbrm = NewI2NPShortMessage (); @@ -658,14 +669,13 @@ namespace i2p } otbrm->len += (payload - otbrm->GetPayload ()); otbrm->FillI2NPMessageHeader (eI2NPOutboundTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); - uint8_t replyKeys[64]; // (reply key, tag) - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "ReplyKeyAndTag", replyKeys); // TODO: correct domain + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK); uint64_t tag; - memcpy (&tag, replyKeys + 32, 8); + memcpy (&tag, noiseState.m_CK, 8); // send garlic to reply tunnel transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - i2p::garlic::WrapECIESX25519AEADRatchetMessage (otbrm, replyKeys, tag))); + i2p::garlic::WrapECIESX25519AEADRatchetMessage (otbrm, noiseState.m_CK + 32, tag))); } else { @@ -680,7 +690,7 @@ namespace i2p { // TODO: fill reply if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16, - noiseState.m_H, 32, noiseState.m_CK, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt + noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt { LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed"); return; diff --git a/libi2pd/TunnelConfig.cpp b/libi2pd/TunnelConfig.cpp index 505f66cc..cc612c00 100644 --- a/libi2pd/TunnelConfig.cpp +++ b/libi2pd/TunnelConfig.cpp @@ -137,11 +137,11 @@ namespace tunnel MixHash (encrypted, len + 16); // h = SHA256(h || ciphertext) } - bool ECIESTunnelHopConfig::DecryptECIES (const uint8_t * encrypted, size_t len, uint8_t * clearText) + bool ECIESTunnelHopConfig::DecryptECIES (const uint8_t * key, const uint8_t * encrypted, size_t len, uint8_t * clearText) { uint8_t nonce[12]; memset (nonce, 0, 12); - return i2p::crypto::AEADChaCha20Poly1305 (encrypted, len - 16, m_H, 32, m_CK, nonce, clearText, len - 16, false); // decrypt + return i2p::crypto::AEADChaCha20Poly1305 (encrypted, len - 16, m_H, 32, key, nonce, clearText, len - 16, false); // decrypt } void LongECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx) @@ -171,7 +171,7 @@ namespace tunnel bool LongECIESTunnelHopConfig::DecryptBuildResponseRecord (const uint8_t * encrypted, uint8_t * clearText) { - if (!DecryptECIES (encrypted, TUNNEL_BUILD_RECORD_SIZE, clearText)) + if (!DecryptECIES (m_CK, encrypted, TUNNEL_BUILD_RECORD_SIZE, clearText)) { LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed"); return false; @@ -196,15 +196,27 @@ namespace tunnel htobe32buf (clearText + SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET , 600); // +10 minutes htobe32buf (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); memset (clearText + SHORT_REQUEST_RECORD_PADDING_OFFSET, 0, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE - SHORT_REQUEST_RECORD_PADDING_OFFSET); - // TODO: derive layer and reply keys // encrypt EncryptECIES (clearText, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET); + // derive reply and layer key + i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelReplyKey", m_CK); + memcpy (replyKey, m_CK + 32, 32); + i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelLayerKey", m_CK); + memcpy (layerKey, m_CK + 32, 32); + if (isEndpoint) + { + i2p::crypto::HKDF (m_CK, nullptr, 0, "TunnelLayerIVKey", m_CK); + memcpy (ivKey, m_CK + 32, 32); + i2p::crypto::HKDF (m_CK, nullptr, 0, "RGarlicKeyAndTag", m_CK); // OTBRM garlic key m_CK + 32, tag first 8 bytes of m_CK + } + else + memcpy (ivKey, m_CK, 32); // last HKDF memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); } bool ShortECIESTunnelHopConfig::DecryptBuildResponseRecord (const uint8_t * encrypted, uint8_t * clearText) { - if (!DecryptECIES (encrypted, SHORT_TUNNEL_BUILD_RECORD_SIZE, clearText)) + if (!DecryptECIES (replyKey, encrypted, SHORT_TUNNEL_BUILD_RECORD_SIZE, clearText)) { LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed"); return false; diff --git a/libi2pd/TunnelConfig.h b/libi2pd/TunnelConfig.h index 7d6456fd..d4c85558 100644 --- a/libi2pd/TunnelConfig.h +++ b/libi2pd/TunnelConfig.h @@ -59,7 +59,7 @@ namespace tunnel TunnelHopConfig (r) {}; bool IsECIES () const { return true; }; void EncryptECIES (const uint8_t * clearText, size_t len, uint8_t * encrypted); - bool DecryptECIES (const uint8_t * encrypted, size_t len, uint8_t * clearText); + bool DecryptECIES (const uint8_t * key, const uint8_t * encrypted, size_t len, uint8_t * clearText); }; struct LongECIESTunnelHopConfig: public ECIESTunnelHopConfig