mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-01-22 21:37:17 +01:00
separate ratchet session for ECIES router
This commit is contained in:
parent
def9873a70
commit
065cfe3b9d
|
@ -227,7 +227,7 @@ namespace garlic
|
||||||
if (!GetOwner ()) return false;
|
if (!GetOwner ()) return false;
|
||||||
// we are Bob
|
// we are Bob
|
||||||
// KDF1
|
// KDF1
|
||||||
i2p::crypto::InitNoiseIKState (*this, GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
|
i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
|
||||||
|
|
||||||
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
|
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
|
||||||
{
|
{
|
||||||
|
@ -460,7 +460,7 @@ namespace garlic
|
||||||
offset += 32;
|
offset += 32;
|
||||||
|
|
||||||
// KDF1
|
// KDF1
|
||||||
i2p::crypto::InitNoiseIKState (*this, m_RemoteStaticKey); // bpk
|
i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // 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];
|
||||||
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk)
|
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk)
|
||||||
|
@ -520,7 +520,7 @@ namespace garlic
|
||||||
bool ECIESX25519AEADRatchetSession::NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
|
bool ECIESX25519AEADRatchetSession::NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
|
||||||
{
|
{
|
||||||
// we are Alice, router's bpk is m_RemoteStaticKey
|
// we are Alice, router's bpk is m_RemoteStaticKey
|
||||||
i2p::crypto::InitNoiseNState (*this, m_RemoteStaticKey);
|
i2p::crypto::InitNoiseNState (GetNoiseState (), m_RemoteStaticKey);
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
|
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
|
||||||
memcpy (out + offset, m_EphemeralKeys->GetPublicKey (), 32);
|
memcpy (out + offset, m_EphemeralKeys->GetPublicKey (), 32);
|
||||||
|
@ -656,7 +656,7 @@ namespace garlic
|
||||||
}
|
}
|
||||||
buf += 32; len -= 32;
|
buf += 32; len -= 32;
|
||||||
// KDF for Reply Key Section
|
// KDF for Reply Key Section
|
||||||
i2p::util::SaveStateHelper<i2p::crypto::NoiseSymmetricState> s(*this); // restore noise state on exit
|
i2p::util::SaveStateHelper<i2p::crypto::NoiseSymmetricState> s(GetNoiseState ()); // restore noise state on exit
|
||||||
MixHash (tag, 8); // h = SHA256(h || tag)
|
MixHash (tag, 8); // h = SHA256(h || tag)
|
||||||
MixHash (bepk, 32); // h = SHA256(h || bepk)
|
MixHash (bepk, 32); // h = SHA256(h || bepk)
|
||||||
uint8_t sharedSecret[32];
|
uint8_t sharedSecret[32];
|
||||||
|
@ -820,32 +820,6 @@ namespace garlic
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ECIESX25519AEADRatchetSession::HandleNextMessageForRouter (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
if (!GetOwner ()) return false;
|
|
||||||
// we are Bob
|
|
||||||
i2p::crypto::InitNoiseNState (*this, GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
|
|
||||||
MixHash (buf, 32);
|
|
||||||
uint8_t sharedSecret[32];
|
|
||||||
if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
MixKey (sharedSecret);
|
|
||||||
buf += 32; len -= 32;
|
|
||||||
uint8_t nonce[12];
|
|
||||||
CreateNonce (0, nonce);
|
|
||||||
std::vector<uint8_t> payload (len - 16);
|
|
||||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
HandlePayload (payload.data (), len - 16, nullptr, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
|
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
|
||||||
{
|
{
|
||||||
|
@ -1124,6 +1098,38 @@ namespace garlic
|
||||||
ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds
|
ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RouterIncomingRatchetSession::RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState):
|
||||||
|
ECIESX25519AEADRatchetSession (&i2p::context, false)
|
||||||
|
{
|
||||||
|
SetNoiseState (initState);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RouterIncomingRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
if (!GetOwner ()) return false;
|
||||||
|
i2p::crypto::NoiseSymmetricState state (GetNoiseState ());
|
||||||
|
// we are Bob
|
||||||
|
state.MixHash (buf, 32);
|
||||||
|
uint8_t sharedSecret[32];
|
||||||
|
if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
state.MixKey (sharedSecret);
|
||||||
|
buf += 32; len -= 32;
|
||||||
|
uint8_t nonce[12];
|
||||||
|
CreateNonce (0, nonce);
|
||||||
|
std::vector<uint8_t> payload (len - 16);
|
||||||
|
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, state.m_H, 32, state.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HandlePayload (payload.data (), len - 16, nullptr, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag)
|
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag)
|
||||||
{
|
{
|
||||||
auto m = NewI2NPMessage ();
|
auto m = NewI2NPMessage ();
|
||||||
|
|
|
@ -164,7 +164,6 @@ namespace garlic
|
||||||
~ECIESX25519AEADRatchetSession ();
|
~ECIESX25519AEADRatchetSession ();
|
||||||
|
|
||||||
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0);
|
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0);
|
||||||
bool HandleNextMessageForRouter (const uint8_t * buf, size_t len);
|
|
||||||
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
|
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
|
||||||
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg, bool isForRouter = false);
|
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg, bool isForRouter = false);
|
||||||
|
|
||||||
|
@ -186,16 +185,21 @@ namespace garlic
|
||||||
bool IsTerminated () const { return m_IsTerminated; }
|
bool IsTerminated () const { return m_IsTerminated; }
|
||||||
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
|
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
i2p::crypto::NoiseSymmetricState& GetNoiseState () { return *this; };
|
||||||
|
void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; };
|
||||||
|
void CreateNonce (uint64_t seqn, uint8_t * nonce);
|
||||||
|
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void CreateNonce (uint64_t seqn, uint8_t * nonce);
|
|
||||||
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
|
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
|
||||||
void InitNewSessionTagset (std::shared_ptr<RatchetTagSet> tagsetNsr) const;
|
void InitNewSessionTagset (std::shared_ptr<RatchetTagSet> tagsetNsr) const;
|
||||||
|
|
||||||
bool HandleNewIncomingSession (const uint8_t * buf, size_t len);
|
bool HandleNewIncomingSession (const uint8_t * buf, size_t len);
|
||||||
bool HandleNewOutgoingSessionReply (uint8_t * buf, size_t len);
|
bool HandleNewOutgoingSessionReply (uint8_t * buf, size_t len);
|
||||||
bool HandleExistingSessionMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index);
|
bool HandleExistingSessionMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index);
|
||||||
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
|
|
||||||
void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset);
|
void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset);
|
||||||
|
|
||||||
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true);
|
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true);
|
||||||
|
@ -237,6 +241,15 @@ namespace garlic
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// single session for all incoming messages
|
||||||
|
class RouterIncomingRatchetSession: public ECIESX25519AEADRatchetSession
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState);
|
||||||
|
bool HandleNextMessage (const uint8_t * buf, size_t len);
|
||||||
|
};
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag);
|
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ namespace i2p
|
||||||
auto initState = new i2p::crypto::NoiseSymmetricState ();
|
auto initState = new i2p::crypto::NoiseSymmetricState ();
|
||||||
i2p::crypto::InitNoiseNState (*initState, GetIdentity ()->GetEncryptionPublicKey ());
|
i2p::crypto::InitNoiseNState (*initState, GetIdentity ()->GetEncryptionPublicKey ());
|
||||||
m_InitialNoiseState.reset (initState);
|
m_InitialNoiseState.reset (initState);
|
||||||
|
m_ECIESSession = std::make_shared<i2p::garlic::RouterIncomingRatchetSession>(*initState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -739,8 +740,10 @@ namespace i2p
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
buf += 4;
|
buf += 4;
|
||||||
auto session = std::make_shared<i2p::garlic::ECIESX25519AEADRatchetSession>(this, false);
|
if (m_ECIESSession)
|
||||||
session->HandleNextMessageForRouter (buf, len);
|
m_ECIESSession->HandleNextMessage (buf, len);
|
||||||
|
else
|
||||||
|
LogPrint (eLogError, "Router: Session is not set for ECIES router");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
|
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
|
||||||
|
|
|
@ -21,6 +21,11 @@
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
|
namespace garlic
|
||||||
|
{
|
||||||
|
class RouterIncomingRatchetSession;
|
||||||
|
}
|
||||||
|
|
||||||
const char ROUTER_INFO[] = "router.info";
|
const char ROUTER_INFO[] = "router.info";
|
||||||
const char ROUTER_KEYS[] = "router.keys";
|
const char ROUTER_KEYS[] = "router.keys";
|
||||||
const char NTCP2_KEYS[] = "ntcp2.keys";
|
const char NTCP2_KEYS[] = "ntcp2.keys";
|
||||||
|
@ -42,7 +47,7 @@ namespace i2p
|
||||||
eRouterErrorOffline = 2,
|
eRouterErrorOffline = 2,
|
||||||
eRouterErrorSymmetricNAT = 3
|
eRouterErrorSymmetricNAT = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
class RouterContext: public i2p::garlic::GarlicDestination
|
class RouterContext: public i2p::garlic::GarlicDestination
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -155,6 +160,7 @@ namespace i2p
|
||||||
i2p::data::RouterInfo m_RouterInfo;
|
i2p::data::RouterInfo m_RouterInfo;
|
||||||
i2p::data::PrivateKeys m_Keys;
|
i2p::data::PrivateKeys m_Keys;
|
||||||
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor, m_TunnelDecryptor;
|
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor, m_TunnelDecryptor;
|
||||||
|
std::shared_ptr<i2p::garlic::RouterIncomingRatchetSession> m_ECIESSession;
|
||||||
uint64_t m_LastUpdateTime; // in seconds
|
uint64_t m_LastUpdateTime; // in seconds
|
||||||
bool m_AcceptsTunnels, m_IsFloodfill;
|
bool m_AcceptsTunnels, m_IsFloodfill;
|
||||||
std::chrono::time_point<std::chrono::steady_clock> m_StartupTime;
|
std::chrono::time_point<std::chrono::steady_clock> m_StartupTime;
|
||||||
|
|
Loading…
Reference in a new issue