diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 927e98a0..bf6047b2 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -559,9 +559,7 @@ namespace client m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ()); RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4); - auto msg = i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound); - if (floodfill->GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) // TODO: remove when implemented - msg = WrapMessage (floodfill, msg); + auto msg = WrapMessageForRouter (floodfill, i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound)); m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer, shared_from_this (), std::placeholders::_1)); @@ -756,10 +754,8 @@ namespace client else AddSessionKey (replyKey, replyTag); - auto msg = CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, - request->replyTunnel, replyKey, replyTag, isECIES); - if (nextFloodfill->GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) // TODO: remove when implemented - msg = WrapMessage (nextFloodfill, msg); + auto msg = WrapMessageForRouter (nextFloodfill, CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, + request->replyTunnel, replyKey, replyTag, isECIES)); request->outboundTunnel->SendTunnelDataMsg ( { i2p::tunnel::TunnelMessageBlock diff --git a/libi2pd/ECIESX25519AEADRatchetSession.cpp b/libi2pd/ECIESX25519AEADRatchetSession.cpp index 8b67960c..bd8d2fe6 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.cpp +++ b/libi2pd/ECIESX25519AEADRatchetSession.cpp @@ -446,7 +446,7 @@ namespace garlic 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 (); // we are Alice, bpk is m_RemoteStaticKey @@ -464,31 +464,47 @@ namespace garlic uint8_t sharedSecret[32]; m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk) MixKey (sharedSecret); - // encrypt static key section + // encrypt flags/static key section uint8_t nonce[12]; 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 + { + 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: Static section AEAD encryption failed "); + LogPrint (eLogWarning, "Garlic: Flags/static section AEAD encryption failed "); return false; } + MixHash (out + offset, 48); // h = SHA256(h || ciphertext) offset += 48; // KDF2 - GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519 (ask, bpk) - MixKey (sharedSecret); + if (isStatic) + { + 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 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"); return false; } - MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext) - + m_State = eSessionStateNewSessionSent; - if (GetOwner ()) - GenerateMoreReceiveTags (CreateNewSessionTagset (), ECIESX25519_NSR_NUM_GENERATED_TAGS); - + if (isStatic) + { + MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext) + if (GetOwner ()) + GenerateMoreReceiveTags (CreateNewSessionTagset (), ECIESX25519_NSR_NUM_GENERATED_TAGS); + } return true; } @@ -778,6 +794,11 @@ namespace garlic return nullptr; len += 72; break; + case eSessionStateOneTime: + if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen, false)) + return nullptr; + len += 96; + break; default: return nullptr; } @@ -788,6 +809,12 @@ namespace garlic return m; } + std::shared_ptr ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr msg) + { + m_State = eSessionStateOneTime; + return WrapSingleMessage (msg); + } + std::vector ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr msg, bool first) { uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); diff --git a/libi2pd/ECIESX25519AEADRatchetSession.h b/libi2pd/ECIESX25519AEADRatchetSession.h index 1c377b28..755531a3 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.h +++ b/libi2pd/ECIESX25519AEADRatchetSession.h @@ -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_INCOMING_TAGS_EXPIRATION_TIMEOUT = 600; // in seconds 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_MAX_NUM_GENERATED_TAGS = 160; 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_REQUEST_REVERSE_KEY_FLAG = 0x04; - class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, public std::enable_shared_from_this + class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, + private i2p::crypto::NoiseSymmetricState, + public std::enable_shared_from_this { enum SessionState { @@ -137,7 +139,8 @@ namespace garlic eSessionStateNewSessionReceived, eSessionStateNewSessionSent, eSessionStateNewSessionReplySent, - eSessionStateEstablished + eSessionStateEstablished, + eSessionStateOneTime }; struct DHRatchet @@ -155,7 +158,8 @@ namespace garlic bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr receiveTagset, int index = 0); std::shared_ptr WrapSingleMessage (std::shared_ptr msg); - + std::shared_ptr WrapOneTimeMessage (std::shared_ptr msg); + const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } @@ -175,7 +179,6 @@ namespace garlic private: void ResetKeys (); - void MixHash (const uint8_t * buf, size_t len); void CreateNonce (uint64_t seqn, uint8_t * nonce); bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes std::shared_ptr CreateNewSessionTagset (); @@ -186,7 +189,7 @@ namespace garlic void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr& receiveTagset, int index); void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr& 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 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); @@ -200,7 +203,7 @@ namespace garlic 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_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only std::shared_ptr m_EphemeralKeys; diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index 5f76bca1..d8d034a1 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -709,11 +709,20 @@ namespace garlic } } - std::shared_ptr GarlicDestination::WrapMessage (std::shared_ptr destination, - std::shared_ptr msg, bool attachLeaseSet) + std::shared_ptr GarlicDestination::WrapMessageForRouter (std::shared_ptr router, + std::shared_ptr msg) { - auto session = GetRoutingSession (destination, attachLeaseSet); - return session->WrapSingleMessage (msg); + if (router->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET) + { + auto session = std::make_shared(this, false); + session->SetRemoteStaticKey (router->GetIdentity ()->GetEncryptionPublicKey ()); + return session->WrapOneTimeMessage (msg); + } + else + { + auto session = GetRoutingSession (router, false); + return session->WrapSingleMessage (msg); + } } std::shared_ptr GarlicDestination::GetRoutingSession ( diff --git a/libi2pd/Garlic.h b/libi2pd/Garlic.h index 3c5d5de6..8df830c7 100644 --- a/libi2pd/Garlic.h +++ b/libi2pd/Garlic.h @@ -238,8 +238,8 @@ namespace garlic std::shared_ptr GetRoutingSession (std::shared_ptr destination, bool attachLeaseSet); void CleanupExpiredTags (); void RemoveDeliveryStatusSession (uint32_t msgID); - std::shared_ptr WrapMessage (std::shared_ptr destination, - std::shared_ptr msg, bool attachLeaseSet = false); + std::shared_ptr WrapMessageForRouter (std::shared_ptr router, + std::shared_ptr msg); void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag