mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-01-22 21:37:17 +01:00
one time garlic encryption for ECIES routers
This commit is contained in:
parent
6362a7bba5
commit
4ba1be2dc0
|
@ -559,9 +559,7 @@ namespace client
|
||||||
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
|
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
|
||||||
LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
|
LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
|
||||||
RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
|
RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
|
||||||
auto msg = i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound);
|
auto msg = WrapMessageForRouter (floodfill, i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound));
|
||||||
if (floodfill->GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) // TODO: remove when implemented
|
|
||||||
msg = WrapMessage (floodfill, msg);
|
|
||||||
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
|
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
|
||||||
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
|
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
|
||||||
shared_from_this (), std::placeholders::_1));
|
shared_from_this (), std::placeholders::_1));
|
||||||
|
@ -756,10 +754,8 @@ namespace client
|
||||||
else
|
else
|
||||||
AddSessionKey (replyKey, replyTag);
|
AddSessionKey (replyKey, replyTag);
|
||||||
|
|
||||||
auto msg = CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
|
auto msg = WrapMessageForRouter (nextFloodfill, CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
|
||||||
request->replyTunnel, replyKey, replyTag, isECIES);
|
request->replyTunnel, replyKey, replyTag, isECIES));
|
||||||
if (nextFloodfill->GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) // TODO: remove when implemented
|
|
||||||
msg = WrapMessage (nextFloodfill, msg);
|
|
||||||
request->outboundTunnel->SendTunnelDataMsg (
|
request->outboundTunnel->SendTunnelDataMsg (
|
||||||
{
|
{
|
||||||
i2p::tunnel::TunnelMessageBlock
|
i2p::tunnel::TunnelMessageBlock
|
||||||
|
|
|
@ -446,7 +446,7 @@ namespace garlic
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
|
bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic)
|
||||||
{
|
{
|
||||||
ResetKeys ();
|
ResetKeys ();
|
||||||
// we are Alice, bpk is m_RemoteStaticKey
|
// we are Alice, bpk is m_RemoteStaticKey
|
||||||
|
@ -464,31 +464,47 @@ namespace garlic
|
||||||
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)
|
||||||
MixKey (sharedSecret);
|
MixKey (sharedSecret);
|
||||||
// encrypt static key section
|
// encrypt flags/static key section
|
||||||
uint8_t nonce[12];
|
uint8_t nonce[12];
|
||||||
CreateNonce (0, nonce);
|
CreateNonce (0, nonce);
|
||||||
if (!i2p::crypto::AEADChaCha20Poly1305 (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET), 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt
|
const uint8_t * fs;
|
||||||
|
if (isStatic)
|
||||||
|
fs = GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Static section AEAD encryption failed ");
|
memset (out + offset, 0, 32); // all zeros flags section
|
||||||
|
fs = out + offset;
|
||||||
|
}
|
||||||
|
if (!i2p::crypto::AEADChaCha20Poly1305 (fs, 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD encryption failed ");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
MixHash (out + offset, 48); // h = SHA256(h || ciphertext)
|
MixHash (out + offset, 48); // h = SHA256(h || ciphertext)
|
||||||
offset += 48;
|
offset += 48;
|
||||||
// KDF2
|
// KDF2
|
||||||
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519 (ask, bpk)
|
if (isStatic)
|
||||||
MixKey (sharedSecret);
|
{
|
||||||
|
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519 (ask, bpk)
|
||||||
|
MixKey (sharedSecret);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
CreateNonce (1, nonce);
|
||||||
// encrypt payload
|
// encrypt payload
|
||||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt
|
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
|
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext)
|
|
||||||
|
|
||||||
m_State = eSessionStateNewSessionSent;
|
m_State = eSessionStateNewSessionSent;
|
||||||
if (GetOwner ())
|
if (isStatic)
|
||||||
GenerateMoreReceiveTags (CreateNewSessionTagset (), ECIESX25519_NSR_NUM_GENERATED_TAGS);
|
{
|
||||||
|
MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext)
|
||||||
|
if (GetOwner ())
|
||||||
|
GenerateMoreReceiveTags (CreateNewSessionTagset (), ECIESX25519_NSR_NUM_GENERATED_TAGS);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -778,6 +794,11 @@ namespace garlic
|
||||||
return nullptr;
|
return nullptr;
|
||||||
len += 72;
|
len += 72;
|
||||||
break;
|
break;
|
||||||
|
case eSessionStateOneTime:
|
||||||
|
if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen, false))
|
||||||
|
return nullptr;
|
||||||
|
len += 96;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -788,6 +809,12 @@ namespace garlic
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg)
|
||||||
|
{
|
||||||
|
m_State = eSessionStateOneTime;
|
||||||
|
return WrapSingleMessage (msg);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first)
|
std::vector<uint8_t> ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first)
|
||||||
{
|
{
|
||||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace garlic
|
||||||
const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after
|
const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after
|
||||||
const int ECIESX25519_INCOMING_TAGS_EXPIRATION_TIMEOUT = 600; // in seconds
|
const int ECIESX25519_INCOMING_TAGS_EXPIRATION_TIMEOUT = 600; // in seconds
|
||||||
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180
|
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180
|
||||||
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 4096; // number of tags we request new tagset after
|
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after
|
||||||
const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24;
|
const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24;
|
||||||
const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 160;
|
const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 160;
|
||||||
const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12;
|
const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12;
|
||||||
|
@ -129,7 +129,9 @@ namespace garlic
|
||||||
const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02;
|
const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02;
|
||||||
const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04;
|
const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04;
|
||||||
|
|
||||||
class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, public std::enable_shared_from_this<ECIESX25519AEADRatchetSession>
|
class ECIESX25519AEADRatchetSession: public GarlicRoutingSession,
|
||||||
|
private i2p::crypto::NoiseSymmetricState,
|
||||||
|
public std::enable_shared_from_this<ECIESX25519AEADRatchetSession>
|
||||||
{
|
{
|
||||||
enum SessionState
|
enum SessionState
|
||||||
{
|
{
|
||||||
|
@ -137,7 +139,8 @@ namespace garlic
|
||||||
eSessionStateNewSessionReceived,
|
eSessionStateNewSessionReceived,
|
||||||
eSessionStateNewSessionSent,
|
eSessionStateNewSessionSent,
|
||||||
eSessionStateNewSessionReplySent,
|
eSessionStateNewSessionReplySent,
|
||||||
eSessionStateEstablished
|
eSessionStateEstablished,
|
||||||
|
eSessionStateOneTime
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DHRatchet
|
struct DHRatchet
|
||||||
|
@ -155,7 +158,8 @@ namespace garlic
|
||||||
|
|
||||||
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<RatchetTagSet> receiveTagset, int index = 0);
|
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<RatchetTagSet> receiveTagset, int index = 0);
|
||||||
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);
|
||||||
|
|
||||||
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
|
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
|
||||||
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
|
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
|
||||||
|
|
||||||
|
@ -175,7 +179,6 @@ namespace garlic
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void ResetKeys ();
|
void ResetKeys ();
|
||||||
void MixHash (const uint8_t * buf, size_t len);
|
|
||||||
void CreateNonce (uint64_t seqn, uint8_t * nonce);
|
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
|
||||||
std::shared_ptr<RatchetTagSet> CreateNewSessionTagset ();
|
std::shared_ptr<RatchetTagSet> CreateNewSessionTagset ();
|
||||||
|
@ -186,7 +189,7 @@ namespace garlic
|
||||||
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<RatchetTagSet>& receiveTagset, int index);
|
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<RatchetTagSet>& receiveTagset, int index);
|
||||||
void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<RatchetTagSet>& receiveTagset);
|
void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<RatchetTagSet>& receiveTagset);
|
||||||
|
|
||||||
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
|
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true);
|
||||||
bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
|
bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
|
||||||
bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
|
bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
|
||||||
bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
|
bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
|
||||||
|
@ -200,7 +203,7 @@ namespace garlic
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32];
|
uint8_t 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
|
||||||
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
||||||
|
|
|
@ -709,11 +709,20 @@ namespace garlic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> GarlicDestination::WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
std::shared_ptr<I2NPMessage> GarlicDestination::WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||||
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet)
|
std::shared_ptr<I2NPMessage> msg)
|
||||||
{
|
{
|
||||||
auto session = GetRoutingSession (destination, attachLeaseSet);
|
if (router->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)
|
||||||
return session->WrapSingleMessage (msg);
|
{
|
||||||
|
auto session = std::make_shared<ECIESX25519AEADRatchetSession>(this, false);
|
||||||
|
session->SetRemoteStaticKey (router->GetIdentity ()->GetEncryptionPublicKey ());
|
||||||
|
return session->WrapOneTimeMessage (msg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto session = GetRoutingSession (router, false);
|
||||||
|
return session->WrapSingleMessage (msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
|
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
|
||||||
|
|
|
@ -238,8 +238,8 @@ namespace garlic
|
||||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
|
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
|
||||||
void CleanupExpiredTags ();
|
void CleanupExpiredTags ();
|
||||||
void RemoveDeliveryStatusSession (uint32_t msgID);
|
void RemoveDeliveryStatusSession (uint32_t msgID);
|
||||||
std::shared_ptr<I2NPMessage> WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||||
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet = false);
|
std::shared_ptr<I2NPMessage> msg);
|
||||||
|
|
||||||
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
|
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
|
||||||
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag
|
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag
|
||||||
|
|
Loading…
Reference in a new issue