mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-01-22 21:37:17 +01:00
use pre-calculated x25519 ephemeral keys for ratchets
This commit is contained in:
parent
1f31fdc257
commit
5f1e66d64b
|
@ -91,6 +91,9 @@ namespace crypto
|
||||||
void SetPrivateKey (const uint8_t * priv, bool calculatePublic = false);
|
void SetPrivateKey (const uint8_t * priv, bool calculatePublic = false);
|
||||||
void Agree (const uint8_t * pub, uint8_t * shared);
|
void Agree (const uint8_t * pub, uint8_t * shared);
|
||||||
|
|
||||||
|
bool IsElligatorIneligible () const { return m_IsElligatorIneligible; }
|
||||||
|
void SetElligatorIneligible () { m_IsElligatorIneligible = true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
uint8_t m_PublicKey[32];
|
uint8_t m_PublicKey[32];
|
||||||
|
@ -101,6 +104,7 @@ namespace crypto
|
||||||
BN_CTX * m_Ctx;
|
BN_CTX * m_Ctx;
|
||||||
uint8_t m_PrivateKey[32];
|
uint8_t m_PrivateKey[32];
|
||||||
#endif
|
#endif
|
||||||
|
bool m_IsElligatorIneligible = false; // true if definitly ineligible
|
||||||
};
|
};
|
||||||
|
|
||||||
// ElGamal
|
// ElGamal
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "Timestamp.h"
|
#include "Timestamp.h"
|
||||||
#include "Tunnel.h"
|
#include "Tunnel.h"
|
||||||
#include "TunnelPool.h"
|
#include "TunnelPool.h"
|
||||||
|
#include "Transports.h"
|
||||||
#include "ECIESX25519AEADRatchetSession.h"
|
#include "ECIESX25519AEADRatchetSession.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
|
@ -137,12 +138,38 @@ namespace garlic
|
||||||
|
|
||||||
bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf)
|
bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf)
|
||||||
{
|
{
|
||||||
|
bool ineligible = false;
|
||||||
|
while (!ineligible)
|
||||||
|
{
|
||||||
|
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
|
||||||
|
ineligible = m_EphemeralKeys->IsElligatorIneligible ();
|
||||||
|
if (!ineligible) // we haven't tried it yet
|
||||||
|
{
|
||||||
|
if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys->GetPublicKey (), buf))
|
||||||
|
return true; // success
|
||||||
|
// otherwise return back
|
||||||
|
m_EphemeralKeys->SetElligatorIneligible ();
|
||||||
|
i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys);
|
||||||
|
}
|
||||||
|
// we still didn't find elligator eligible pair
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
m_EphemeralKeys.GenerateKeys ();
|
// create new
|
||||||
if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys.GetPublicKey (), buf))
|
m_EphemeralKeys = std::make_shared<i2p::crypto::X25519Keys>();
|
||||||
|
m_EphemeralKeys->GenerateKeys ();
|
||||||
|
if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys->GetPublicKey (), buf))
|
||||||
return true; // success
|
return true; // success
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// let NTCP2 use it
|
||||||
|
m_EphemeralKeys->SetElligatorIneligible ();
|
||||||
|
i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
LogPrint (eLogError, "Garlic: Can't generate elligator eligible x25519 keys");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,7 +321,7 @@ namespace garlic
|
||||||
if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG)
|
if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG)
|
||||||
memcpy (m_NextSendRatchet->remote, buf, 32);
|
memcpy (m_NextSendRatchet->remote, buf, 32);
|
||||||
uint8_t sharedSecret[32], tagsetKey[32];
|
uint8_t sharedSecret[32], tagsetKey[32];
|
||||||
m_NextSendRatchet->key.Agree (m_NextSendRatchet->remote, sharedSecret);
|
m_NextSendRatchet->key->Agree (m_NextSendRatchet->remote, sharedSecret);
|
||||||
i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32)
|
i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32)
|
||||||
auto newTagset = std::make_shared<RatchetTagSet> (shared_from_this ());
|
auto newTagset = std::make_shared<RatchetTagSet> (shared_from_this ());
|
||||||
newTagset->SetTagSetID (1 + m_NextSendRatchet->keyID + keyID);
|
newTagset->SetTagSetID (1 + m_NextSendRatchet->keyID + keyID);
|
||||||
|
@ -326,7 +353,7 @@ namespace garlic
|
||||||
int tagsetID = 2*keyID;
|
int tagsetID = 2*keyID;
|
||||||
if (newKey)
|
if (newKey)
|
||||||
{
|
{
|
||||||
m_NextReceiveRatchet->key.GenerateKeys ();
|
m_NextReceiveRatchet->key = i2p::transport::transports.GetNextX25519KeysPair ();
|
||||||
m_NextReceiveRatchet->newKey = true;
|
m_NextReceiveRatchet->newKey = true;
|
||||||
tagsetID++;
|
tagsetID++;
|
||||||
}
|
}
|
||||||
|
@ -336,7 +363,7 @@ namespace garlic
|
||||||
memcpy (m_NextReceiveRatchet->remote, buf, 32);
|
memcpy (m_NextReceiveRatchet->remote, buf, 32);
|
||||||
|
|
||||||
uint8_t sharedSecret[32], tagsetKey[32];
|
uint8_t sharedSecret[32], tagsetKey[32];
|
||||||
m_NextReceiveRatchet->key.Agree (m_NextReceiveRatchet->remote, sharedSecret);
|
m_NextReceiveRatchet->key->Agree (m_NextReceiveRatchet->remote, sharedSecret);
|
||||||
i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32)
|
i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32)
|
||||||
auto newTagset = std::make_shared<RatchetTagSet>(shared_from_this ());
|
auto newTagset = std::make_shared<RatchetTagSet>(shared_from_this ());
|
||||||
newTagset->SetTagSetID (tagsetID);
|
newTagset->SetTagSetID (tagsetID);
|
||||||
|
@ -364,7 +391,7 @@ namespace garlic
|
||||||
else
|
else
|
||||||
m_NextSendRatchet.reset (new DHRatchet ());
|
m_NextSendRatchet.reset (new DHRatchet ());
|
||||||
if (m_NextSendRatchet->newKey)
|
if (m_NextSendRatchet->newKey)
|
||||||
m_NextSendRatchet->key.GenerateKeys ();
|
m_NextSendRatchet->key->GenerateKeys ();
|
||||||
|
|
||||||
m_SendForwardKey = true;
|
m_SendForwardKey = true;
|
||||||
LogPrint (eLogDebug, "Garlic: new send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", m_NextSendRatchet->keyID, " created");
|
LogPrint (eLogDebug, "Garlic: new send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", m_NextSendRatchet->keyID, " created");
|
||||||
|
@ -384,9 +411,9 @@ namespace garlic
|
||||||
|
|
||||||
// KDF1
|
// KDF1
|
||||||
MixHash (m_RemoteStaticKey, 32); // h = SHA256(h || bpk)
|
MixHash (m_RemoteStaticKey, 32); // h = SHA256(h || bpk)
|
||||||
MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || aepk)
|
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
|
||||||
uint8_t sharedSecret[32];
|
uint8_t sharedSecret[32];
|
||||||
m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk)
|
m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk)
|
||||||
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
|
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
|
||||||
// encrypt static key section
|
// encrypt static key section
|
||||||
uint8_t nonce[12];
|
uint8_t nonce[12];
|
||||||
|
@ -435,11 +462,11 @@ namespace garlic
|
||||||
offset += 32;
|
offset += 32;
|
||||||
// KDF for Reply Key Section
|
// KDF for Reply Key Section
|
||||||
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
|
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
|
||||||
MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || bepk)
|
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk)
|
||||||
uint8_t sharedSecret[32];
|
uint8_t sharedSecret[32];
|
||||||
m_EphemeralKeys.Agree (m_Aepk, sharedSecret); // sharedSecret = x25519(besk, aepk)
|
m_EphemeralKeys->Agree (m_Aepk, sharedSecret); // sharedSecret = x25519(besk, aepk)
|
||||||
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
|
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
|
||||||
m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // sharedSecret = x25519(besk, apk)
|
m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // sharedSecret = x25519(besk, apk)
|
||||||
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
|
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
|
||||||
uint8_t nonce[12];
|
uint8_t nonce[12];
|
||||||
CreateNonce (0, nonce);
|
CreateNonce (0, nonce);
|
||||||
|
@ -485,7 +512,7 @@ namespace garlic
|
||||||
// recalculate h with new tag
|
// recalculate h with new tag
|
||||||
memcpy (m_H, m_NSRH, 32);
|
memcpy (m_H, m_NSRH, 32);
|
||||||
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
|
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
|
||||||
MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || bepk)
|
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk)
|
||||||
uint8_t nonce[12];
|
uint8_t nonce[12];
|
||||||
CreateNonce (0, nonce);
|
CreateNonce (0, nonce);
|
||||||
if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + 40, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
|
if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + 40, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
|
||||||
|
@ -524,7 +551,7 @@ namespace garlic
|
||||||
if (m_State == eSessionStateNewSessionSent)
|
if (m_State == eSessionStateNewSessionSent)
|
||||||
{
|
{
|
||||||
// only fist time, we assume ephemeral keys the same
|
// only fist time, we assume ephemeral keys the same
|
||||||
m_EphemeralKeys.Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk)
|
m_EphemeralKeys->Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk)
|
||||||
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
|
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
|
||||||
GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519 (ask, bepk)
|
GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519 (ask, bepk)
|
||||||
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
|
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
|
||||||
|
@ -565,6 +592,7 @@ namespace garlic
|
||||||
if (m_State == eSessionStateNewSessionSent)
|
if (m_State == eSessionStateNewSessionSent)
|
||||||
{
|
{
|
||||||
m_State = eSessionStateEstablished;
|
m_State = eSessionStateEstablished;
|
||||||
|
m_EphemeralKeys = nullptr;
|
||||||
m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||||
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
|
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
|
||||||
}
|
}
|
||||||
|
@ -643,6 +671,7 @@ namespace garlic
|
||||||
case eSessionStateNewSessionReplySent:
|
case eSessionStateNewSessionReplySent:
|
||||||
m_State = eSessionStateEstablished;
|
m_State = eSessionStateEstablished;
|
||||||
m_NSRSendTagset = nullptr;
|
m_NSRSendTagset = nullptr;
|
||||||
|
m_EphemeralKeys = nullptr;
|
||||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
#endif
|
#endif
|
||||||
|
@ -813,7 +842,7 @@ namespace garlic
|
||||||
htobe16buf (v.data () + offset, keyID); offset += 2; // keyid
|
htobe16buf (v.data () + offset, keyID); offset += 2; // keyid
|
||||||
if (m_NextReceiveRatchet->newKey)
|
if (m_NextReceiveRatchet->newKey)
|
||||||
{
|
{
|
||||||
memcpy (v.data () + offset, m_NextReceiveRatchet->key.GetPublicKey (), 32);
|
memcpy (v.data () + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32);
|
||||||
offset += 32; // public key
|
offset += 32; // public key
|
||||||
}
|
}
|
||||||
m_SendReverseKey = false;
|
m_SendReverseKey = false;
|
||||||
|
@ -828,7 +857,7 @@ namespace garlic
|
||||||
htobe16buf (v.data () + offset, m_NextSendRatchet->keyID); offset += 2; // keyid
|
htobe16buf (v.data () + offset, m_NextSendRatchet->keyID); offset += 2; // keyid
|
||||||
if (m_NextSendRatchet->newKey)
|
if (m_NextSendRatchet->newKey)
|
||||||
{
|
{
|
||||||
memcpy (v.data () + offset, m_NextSendRatchet->key.GetPublicKey (), 32);
|
memcpy (v.data () + offset, m_NextSendRatchet->key->GetPublicKey (), 32);
|
||||||
offset += 32; // public key
|
offset += 32; // public key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,7 @@ namespace garlic
|
||||||
struct DHRatchet
|
struct DHRatchet
|
||||||
{
|
{
|
||||||
int keyID = 0;
|
int keyID = 0;
|
||||||
i2p::crypto::X25519Keys key;
|
std::shared_ptr<i2p::crypto::X25519Keys> key;
|
||||||
uint8_t remote[32]; // last remote public key
|
uint8_t remote[32]; // last remote public key
|
||||||
bool newKey = true;
|
bool newKey = true;
|
||||||
};
|
};
|
||||||
|
@ -180,7 +180,7 @@ namespace garlic
|
||||||
uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32];
|
uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32];
|
||||||
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
|
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
|
||||||
uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only
|
uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only
|
||||||
i2p::crypto::X25519Keys m_EphemeralKeys;
|
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
||||||
SessionState m_State = eSessionStateNew;
|
SessionState m_State = eSessionStateNew;
|
||||||
uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0; // incoming
|
uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0; // incoming
|
||||||
std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRSendTagset;
|
std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRSendTagset;
|
||||||
|
|
Loading…
Reference in a new issue