common HKDF context for ratchets tags

This commit is contained in:
orignal
2026-08-08 16:52:28 -04:00
parent cfcc88029c
commit e32e694a83
6 changed files with 77 additions and 45 deletions
+26 -10
View File
@@ -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
+42 -28
View File
@@ -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<std::pair<uint8_t *, size_t> >& 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<std::pair<uint8_t *, size_t> >& 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 ();
+5 -5
View File
@@ -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<RatchetTagSet>();
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");
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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;
+2
View File
@@ -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<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> 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: