diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index 5c7e7af8..376aec89 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -779,26 +779,42 @@ namespace crypto void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, std::string_view info, uint8_t * out, size_t outLen) { - EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, nullptr); - EVP_PKEY_derive_init (pctx); - EVP_PKEY_CTX_set_hkdf_md (pctx, EVP_sha256()); + HKDFContext ctx; + ctx (salt, key, keyLen, info, out, outLen); + } + + HKDFContext::HKDFContext () + { + m_Ctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, nullptr); + } + + HKDFContext::~HKDFContext () + { + if (m_Ctx) + EVP_PKEY_CTX_free (m_Ctx); + } + + void HKDFContext::operator ()(const uint8_t * salt, const uint8_t * key, size_t keyLen, + std::string_view info, uint8_t * out, size_t outLen) + { + EVP_PKEY_derive_init (m_Ctx); + EVP_PKEY_CTX_set_hkdf_md (m_Ctx, EVP_sha256()); if (key && keyLen) { - EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32); - EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen); + EVP_PKEY_CTX_set1_hkdf_salt (m_Ctx, salt, 32); + EVP_PKEY_CTX_set1_hkdf_key (m_Ctx, key, keyLen); } else { // zerolen - EVP_PKEY_CTX_hkdf_mode (pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY); + EVP_PKEY_CTX_hkdf_mode (m_Ctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY); uint8_t tempKey[32]; unsigned int len; HMAC(EVP_sha256(), salt, 32, nullptr, 0, tempKey, &len); - EVP_PKEY_CTX_set1_hkdf_key (pctx, tempKey, len); + EVP_PKEY_CTX_set1_hkdf_key (m_Ctx, tempKey, len); } if (info.length () > 0) - EVP_PKEY_CTX_add1_hkdf_info (pctx, (const uint8_t *)info.data (), info.length ()); - EVP_PKEY_derive (pctx, out, &outLen); - EVP_PKEY_CTX_free (pctx); + EVP_PKEY_CTX_add1_hkdf_info (m_Ctx, (const uint8_t *)info.data (), info.length ()); + EVP_PKEY_derive (m_Ctx, out, &outLen); } // Noise diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index bef12a08..dc0b0727 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2026, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -46,7 +46,7 @@ namespace crypto EVP_PKEY * CreateDSA (BIGNUM * pubKey = nullptr, BIGNUM * privKey = nullptr); #else DSA * CreateDSA (); -#endif +#endif // RSA const BIGNUM * GetRSAE (); @@ -89,21 +89,21 @@ namespace crypto // AES typedef i2p::data::Tag<32> AESKey; - + class ECBEncryption { public: ECBEncryption (); ~ECBEncryption (); - + void SetKey (const uint8_t * key) { m_Key = key; }; void Encrypt(const uint8_t * in, uint8_t * out); private: AESKey m_Key; - EVP_CIPHER_CTX * m_Ctx; + EVP_CIPHER_CTX * m_Ctx; }; class ECBDecryption @@ -112,14 +112,14 @@ namespace crypto ECBDecryption (); ~ECBDecryption (); - + void SetKey (const uint8_t * key) { m_Key = key; }; void Decrypt (const uint8_t * in, uint8_t * out); - + private: - + AESKey m_Key; - EVP_CIPHER_CTX * m_Ctx; + EVP_CIPHER_CTX * m_Ctx; }; class CBCEncryption @@ -129,13 +129,13 @@ namespace crypto CBCEncryption (); ~CBCEncryption (); - void SetKey (const uint8_t * key) { m_Key = key; }; // 32 bytes + void SetKey (const uint8_t * key) { m_Key = key; }; // 32 bytes void Encrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out); - + private: AESKey m_Key; - EVP_CIPHER_CTX * m_Ctx; + EVP_CIPHER_CTX * m_Ctx; }; class CBCDecryption @@ -144,14 +144,14 @@ namespace crypto CBCDecryption (); ~CBCDecryption (); - + void SetKey (const uint8_t * key) { m_Key = key; }; // 32 bytes void Decrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out); private: AESKey m_Key; - EVP_CIPHER_CTX * m_Ctx; + EVP_CIPHER_CTX * m_Ctx; }; class TunnelEncryption // with double IV encryption @@ -203,11 +203,11 @@ namespace crypto const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); // msgLen is len without tag void Encrypt (const std::vector >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad - + private: - EVP_CIPHER_CTX * m_Ctx; - }; + EVP_CIPHER_CTX * m_Ctx; + }; class AEADChaCha20Poly1305Decryptor { @@ -218,35 +218,49 @@ namespace crypto bool Decrypt (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); // msgLen is len without tag - + private: - EVP_CIPHER_CTX * m_Ctx; - }; - + EVP_CIPHER_CTX * m_Ctx; + }; + 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 - + // ChaCha20 void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out); - class ChaCha20Context + class ChaCha20Context final { public: ChaCha20Context (); ~ChaCha20Context (); void operator ()(const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out); - + private: - EVP_CIPHER_CTX * m_Ctx; + EVP_CIPHER_CTX * m_Ctx; }; - + // HKDF void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, std::string_view info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32 + class HKDFContext final + { + public: + + HKDFContext (); + ~HKDFContext (); + + void operator ()(const uint8_t * salt, const uint8_t * key, size_t keyLen, std::string_view info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32 + + private: + + EVP_PKEY_CTX * m_Ctx; + }; + // Noise struct NoiseSymmetricState @@ -255,7 +269,7 @@ namespace crypto uint64_t m_N; void Init (const uint8_t * ck, const uint8_t * hh, const uint8_t * pub); - + void MixHash (const uint8_t * buf, size_t len); void MixHash (const std::vector >& bufs); void MixKey (const uint8_t * sharedSecret); @@ -268,7 +282,7 @@ namespace crypto void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (NTCP2) void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (SSU2) void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets) - + // init and terminate void InitCrypto (bool precomputation); void TerminateCrypto (); diff --git a/libi2pd/ECIESX25519AEADRatchetSession.cpp b/libi2pd/ECIESX25519AEADRatchetSession.cpp index 2c6eb0ba..c582f86f 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.cpp +++ b/libi2pd/ECIESX25519AEADRatchetSession.cpp @@ -44,7 +44,7 @@ namespace garlic m_NextIndex = 0; } - uint64_t RatchetTagSet::GetNextSessionTag () + uint64_t RatchetTagSet::GetNextSessionTag (i2p::crypto::HKDFContext& hkdfCtx) { m_NextIndex++; if (m_NextIndex >= 65535) @@ -52,7 +52,7 @@ namespace garlic LogPrint (eLogError, "Garlic: Tagset ", GetTagSetID (), " is empty"); return 0; } - i2p::crypto::HKDF (m_SessionTagKeyData, m_SessTagConstant, 32, "SessionTagKeyGen", m_SessionTagKeyData); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64) + hkdfCtx (m_SessionTagKeyData, m_SessTagConstant, 32, "SessionTagKeyGen", m_SessionTagKeyData); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64) return m_SessionTagKeyData.GetLL ()[4]; // tag = keydata[32:39] } @@ -648,7 +648,7 @@ namespace garlic // we are Bob m_NSRSendTagset = std::make_shared(); InitNewSessionTagset (m_NSRSendTagset); - uint64_t tag = m_NSRSendTagset->GetNextSessionTag (); + uint64_t tag = m_NSRSendTagset->GetNextSessionTag (GetOwner ()->GetHKDFContext ()); size_t offset = 0; memcpy (out + offset, &tag, 8); @@ -733,7 +733,7 @@ namespace garlic bool ECIESX25519AEADRatchetSession::NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) { // we are Bob and sent NSR already - uint64_t tag = m_NSRSendTagset->GetNextSessionTag (); // next tag + uint64_t tag = m_NSRSendTagset->GetNextSessionTag (GetOwner ()->GetHKDFContext ()); // next tag memcpy (out, &tag, 8); memcpy (out + 8, m_NSREncodedKey, 32); // recalculate h with new tag @@ -908,7 +908,7 @@ namespace garlic uint8_t nonce[12]; auto index = m_SendTagset->GetNextIndex (); CreateNonce (index, nonce); // tag's index - uint64_t tag = m_SendTagset->GetNextSessionTag (); + uint64_t tag = m_SendTagset->GetNextSessionTag (GetOwner ()->GetHKDFContext ()); if (!tag) { LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset"); diff --git a/libi2pd/ECIESX25519AEADRatchetSession.h b/libi2pd/ECIESX25519AEADRatchetSession.h index 3edf9c9f..933ce56d 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.h +++ b/libi2pd/ECIESX25519AEADRatchetSession.h @@ -54,7 +54,7 @@ namespace garlic void DHInitialize (const uint8_t * rootKey, const uint8_t * k); void NextSessionTagRatchet (); - uint64_t GetNextSessionTag (); + uint64_t GetNextSessionTag (i2p::crypto::HKDFContext& hkdfCtx); const uint8_t * GetNextRootKey () const { return m_NextRootKey; }; int GetNextIndex () const { return m_NextIndex; }; void GetSymmKey (int index, uint8_t * key); diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index 5cab3b6d..a0963c94 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -1067,7 +1067,7 @@ namespace garlic uint64_t GarlicDestination::AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset) { auto index = tagset->GetNextIndex (); - uint64_t tag = tagset->GetNextSessionTag (); + uint64_t tag = tagset->GetNextSessionTag (m_HKDFContext); if (tag) m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{index, tagset}); return tag; diff --git a/libi2pd/Garlic.h b/libi2pd/Garlic.h index 3d269599..8be13d1a 100644 --- a/libi2pd/Garlic.h +++ b/libi2pd/Garlic.h @@ -257,6 +257,7 @@ namespace garlic int GetNumTags () const { return m_NumTags; }; void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; }; int GetNumRatchetInboundTags () const { return m_NumRatchetInboundTags; }; + i2p::crypto::HKDFContext& GetHKDFContext () { return m_HKDFContext; } std::shared_ptr GetRoutingSession (std::shared_ptr destination, bool attachLeaseSet, bool requestNewIfNotFound = true); void CleanupExpiredTags (); @@ -333,6 +334,7 @@ namespace garlic // encryption i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor; i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor; + i2p::crypto::HKDFContext m_HKDFContext; public: