Browse Source

use pre-calculated x25519 ephemeral keys for ratchets

pull/1542/head
orignal 4 years ago
parent
commit
5f1e66d64b
  1. 4
      libi2pd/Crypto.h
  2. 59
      libi2pd/ECIESX25519AEADRatchetSession.cpp
  3. 4
      libi2pd/ECIESX25519AEADRatchetSession.h

4
libi2pd/Crypto.h

@ -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

59
libi2pd/ECIESX25519AEADRatchetSession.cpp

@ -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
} }
} }

4
libi2pd/ECIESX25519AEADRatchetSession.h

@ -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…
Cancel
Save