/ libi2pd / ECIESX25519AEADRatchetSession.cpp
ECIESX25519AEADRatchetSession.cpp
   1  /*
   2  * Copyright (c) 2013-2026, The PurpleI2P Project
   3  *
   4  * This file is part of Purple i2pd project and licensed under BSD3
   5  *
   6  * See full license text in LICENSE file at top of project tree
   7  */
   8  
   9  #include <string.h>
  10  #include "Log.h"
  11  #include "util.h"
  12  #include "Crypto.h"
  13  #include "PostQuantum.h"
  14  #include "Elligator.h"
  15  #include "Tag.h"
  16  #include "I2PEndian.h"
  17  #include "Timestamp.h"
  18  #include "Tunnel.h"
  19  #include "TunnelPool.h"
  20  #include "Transports.h"
  21  #include "ECIESX25519AEADRatchetSession.h"
  22  
  23  namespace i2p
  24  {
  25  namespace garlic
  26  {
  27  
  28  	void RatchetTagSet::DHInitialize (const uint8_t * rootKey, const uint8_t * k)
  29  	{
  30  		// DH_INITIALIZE(rootKey, k)
  31  		uint8_t keydata[64];
  32  		i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64)
  33  		memcpy (m_NextRootKey, keydata, 32); // nextRootKey = keydata[0:31]
  34  		i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_SessionTagKeyData);
  35  		// [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64)
  36  		memcpy (m_SymmKeyCK, (const uint8_t *)m_SessionTagKeyData + 32, 32);
  37  		m_NextSymmKeyIndex = 0;
  38  	}
  39  
  40  	void RatchetTagSet::NextSessionTagRatchet ()
  41  	{
  42  		i2p::crypto::HKDF (m_SessionTagKeyData, nullptr, 0, "STInitialization", m_SessionTagKeyData); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64)
  43  		memcpy (m_SessTagConstant, (const uint8_t *)m_SessionTagKeyData + 32, 32); // SESSTAG_CONSTANT = keydata[32:63]
  44  		m_NextIndex = 0;
  45  	}
  46  
  47  	uint64_t RatchetTagSet::GetNextSessionTag ()
  48  	{
  49  		m_NextIndex++;
  50  		if (m_NextIndex >= 65535)
  51  		{
  52  			LogPrint (eLogError, "Garlic: Tagset ", GetTagSetID (), " is empty");
  53  			return 0;
  54  		}
  55  		i2p::crypto::HKDF (m_SessionTagKeyData, m_SessTagConstant, 32, "SessionTagKeyGen", m_SessionTagKeyData); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
  56  		return m_SessionTagKeyData.GetLL ()[4]; // tag = keydata[32:39]
  57  	}
  58  
  59  	void RatchetTagSet::GetSymmKey (int index, uint8_t * key)
  60  	{
  61  		if (index >= m_NextSymmKeyIndex)
  62  		{
  63  			auto num = index + 1 - m_NextSymmKeyIndex;
  64  			if (!m_NextSymmKeyIndex)
  65  			{
  66  				i2p::crypto::HKDF (m_SymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK); // keydata_0 = HKDF(symmKey_ck, SYMMKEY_CONSTANT, "SymmetricRatchet", 64)
  67  				m_NextSymmKeyIndex = 1;
  68  				num--;
  69  			}
  70  			for (int i = 0; i < num; i++)
  71  			{
  72  				i2p::crypto::HKDF (m_CurrentSymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK);
  73  				if (i < num - 1)
  74  					m_ItermediateSymmKeys.emplace (m_NextSymmKeyIndex + i, m_CurrentSymmKeyCK + 32);
  75  			}
  76  			m_NextSymmKeyIndex += num;
  77  			memcpy (key, m_CurrentSymmKeyCK + 32, 32);
  78  		}
  79  		else
  80  		{
  81  			auto it = m_ItermediateSymmKeys.find (index);
  82  			if (it != m_ItermediateSymmKeys.end ())
  83  			{
  84  				memcpy (key, it->second, 32);
  85  				m_ItermediateSymmKeys.erase (it);
  86  			}
  87  			else
  88  				LogPrint (eLogError, "Garlic: Missing symmetric key for index ", index);
  89  		}
  90  	}
  91  
  92  	void RatchetTagSet::DeleteSymmKey (int index)
  93  	{
  94  		m_ItermediateSymmKeys.erase (index);
  95  	}
  96  
  97  	ReceiveRatchetTagSet::ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS):
  98  		m_Session (session), m_IsNS (isNS)
  99  	{
 100  	}
 101  
 102  	ReceiveRatchetTagSet::~ReceiveRatchetTagSet ()
 103  	{
 104  		if (m_IsNS && m_Session)
 105  			m_Session->CleanupReceiveNSRKeys ();
 106  	}
 107  
 108  	void ReceiveRatchetTagSet::Expire ()
 109  	{
 110  		if (!m_ExpirationTimestamp)
 111  			m_ExpirationTimestamp = i2p::util::GetSecondsSinceEpoch () + ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT;
 112  	}
 113  
 114  	bool ReceiveRatchetTagSet::IsExpired (uint64_t ts) const
 115  	{
 116  		return m_ExpirationTimestamp && ts > m_ExpirationTimestamp;
 117  	}
 118  
 119  	bool ReceiveRatchetTagSet::IsIndexExpired (int index) const
 120  	{
 121  		return index < m_TrimBehindIndex;
 122  	}
 123  
 124  	bool ReceiveRatchetTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index)
 125  	{
 126  		auto session = GetSession ();
 127  		if (!session) return false;
 128  		return session->HandleNextMessage (buf, len, shared_from_this (), index);
 129  	}
 130  
 131  	bool ReceiveRatchetTagSet::IsSessionTerminated () const
 132  	{
 133  		return !m_Session || m_Session->IsTerminated ();
 134  	}
 135  
 136  
 137  	SymmetricKeyTagSet::SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key):
 138  		ReceiveRatchetTagSet (nullptr), m_Destination (destination)
 139  	{
 140  		memcpy (m_Key, key, 32);
 141  		Expire ();
 142  	}
 143  
 144  	bool SymmetricKeyTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index)
 145  	{
 146  		if (len < 24) return false;
 147  		uint8_t nonce[12];
 148  		memset (nonce, 0, 12); // n = 0
 149  		size_t offset = 8; // first 8 bytes is reply tag used as AD
 150  		len -= 16; // poly1305
 151  		if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len - offset, buf, 8, m_Key, nonce, buf + offset, len - offset, false)) // decrypt
 152  		{
 153  			LogPrint (eLogWarning, "Garlic: Symmetric key tagset AEAD decryption failed");
 154  			return false;
 155  		}
 156  		// we assume 1 I2NP block with delivery type local
 157  		if (offset + 3 > len)
 158  		{
 159  			LogPrint (eLogWarning, "Garlic: Symmetric key tagset is too short ", len);
 160  			return false;
 161  		}
 162  		if (buf[offset] != eECIESx25519BlkGalicClove)
 163  		{
 164  			LogPrint (eLogWarning, "Garlic: Symmetric key tagset unexpected block ", (int)buf[offset]);
 165  			return false;
 166  		}
 167  		offset++;
 168  		auto size = bufbe16toh (buf + offset);
 169  		offset += 2;
 170  		if (offset + size > len)
 171  		{
 172  			LogPrint (eLogWarning, "Garlic: Symmetric key tagset block is too long ", size);
 173  			return false;
 174  		}
 175  		if (m_Destination)
 176  			m_Destination->HandleECIESx25519GarlicClove (buf + offset, size, nullptr);
 177  		return true;
 178  	}
 179  
 180  	ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS):
 181  		GarlicRoutingSession (owner, true), m_RemoteStaticKeyType (0)
 182  	{
 183  		if (!attachLeaseSetNS) SetLeaseSetUpdateStatus (eLeaseSetUpToDate);
 184  	}
 185  
 186  	ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession ()
 187  	{
 188  	}
 189  
 190  	void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce)
 191  	{
 192  		memset (nonce, 0, 4);
 193  		htole64buf (nonce + 4, seqn);
 194  	}
 195  
 196  	bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf)
 197  	{
 198  		bool ineligible = false;
 199  		while (!ineligible)
 200  		{
 201  			m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
 202  			ineligible = m_EphemeralKeys->IsElligatorIneligible ();
 203  			if (!ineligible) // we haven't tried it yet
 204  			{
 205  				if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys->GetPublicKey (), buf))
 206  					return true; // success
 207  				// otherwise return back
 208  				m_EphemeralKeys->SetElligatorIneligible ();
 209  				i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys);
 210  			}
 211  			else
 212  				i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys);
 213  		}
 214  		// we still didn't find elligator eligible pair
 215  		for (int i = 0; i < 25; i++)
 216  		{
 217  			// create new
 218  			m_EphemeralKeys = std::make_shared<i2p::crypto::X25519Keys>();
 219  			m_EphemeralKeys->GenerateKeys ();
 220  			if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys->GetPublicKey (), buf))
 221  				return true; // success
 222  			else
 223  			{
 224  				// let NTCP2 use it
 225  				m_EphemeralKeys->SetElligatorIneligible ();
 226  				i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys);
 227  			}
 228  		}
 229  		LogPrint (eLogError, "Garlic: Can't generate elligator eligible x25519 keys");
 230  		return false;
 231  	}
 232  
 233  	void ECIESX25519AEADRatchetSession::InitNewSessionTagset (std::shared_ptr<RatchetTagSet> tagsetNsr) const
 234  	{
 235  		uint8_t tagsetKey[32];
 236  		i2p::crypto::HKDF (m_CK, nullptr, 0, "SessionReplyTags", tagsetKey, 32); // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32)
 237  		// Session Tag Ratchet
 238  		tagsetNsr->DHInitialize (m_CK, tagsetKey); // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey)
 239  		tagsetNsr->NextSessionTagRatchet ();
 240  	}
 241  
 242  	bool ECIESX25519AEADRatchetSession::MessageConfirmed (uint32_t msgID)
 243  	{
 244  		auto ret = GarlicRoutingSession::MessageConfirmed (msgID); // LeaseSet
 245  		if (m_AckRequestMsgID && m_AckRequestMsgID == msgID)
 246  		{
 247  			m_AckRequestMsgID = 0;
 248  			m_AckRequestNumAttempts = 0;
 249  			ret = true;
 250  		}
 251  		return ret;
 252  	}
 253  
 254  	bool ECIESX25519AEADRatchetSession::CleanupUnconfirmedTags ()
 255  	{
 256  		if (m_AckRequestMsgID && m_AckRequestNumAttempts > ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS)
 257  		{
 258  			m_AckRequestMsgID = 0;
 259  			m_AckRequestNumAttempts = 0;
 260  			return true;
 261  		}
 262  		return false;
 263  	}
 264  
 265  	void ECIESX25519AEADRatchetSession::CleanupReceiveNSRKeys ()
 266  	{
 267  		m_EphemeralKeys = nullptr;
 268  #if OPENSSL_PQ
 269  		m_PQKeys = nullptr;
 270  #endif
 271  	}
 272  
 273  	bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len)
 274  	{
 275  		if (!GetOwner ()) return false;
 276  		// we are Bob
 277  		// KDF1
 278  
 279  		if (len < 32 || !i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
 280  		{
 281  			LogPrint (eLogError, "Garlic: Can't decode elligator");
 282  			return false;
 283  		}
 284  		buf += 32; len -= 32;
 285  
 286  		uint8_t sharedSecret[32];
 287  		bool decrypted = false;
 288  		auto cryptoType = GetOwner ()->GetRatchetsHighestCryptoType ();
 289  #if OPENSSL_PQ
 290  		if (cryptoType > i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // we support post quantum
 291  		{
 292  			i2p::crypto::InitNoiseIKStateMLKEM (GetNoiseState (), cryptoType, GetOwner ()->GetEncryptionPublicKey (cryptoType)); // bpk
 293  			MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
 294  
 295  			if (GetOwner ()->Decrypt (m_Aepk, sharedSecret, cryptoType)) // x25519(bsk, aepk)
 296  			{
 297  				MixKey (sharedSecret);
 298  
 299  				auto keyLen = i2p::crypto::GetMLKEMPublicKeyLen (cryptoType);
 300  				if (keyLen + 16 < len && keyLen)
 301  				{
 302  					std::vector<uint8_t> encapsKey(keyLen);
 303  					if (Decrypt (buf, encapsKey.data (), keyLen))
 304  					{
 305  						decrypted = true; // encaps section has right hash
 306  						MixHash (buf, keyLen + 16);
 307  						buf += keyLen + 16;
 308  						len -= keyLen + 16;
 309  
 310  						m_PQKeys = i2p::crypto::CreateMLKEMKeys (cryptoType);
 311  						m_PQKeys->SetPublicKey (encapsKey.data ());
 312  					}
 313  				}
 314  				// if PQ failed it's valid situation meaning regular ECIES_X25519(type 4)
 315  			}
 316  		}
 317  #endif
 318  		if (!decrypted)
 319  		{
 320  			if (cryptoType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ||
 321  			    GetOwner ()->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
 322  			{
 323  				cryptoType = i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD;
 324  				i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
 325  				MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
 326  
 327  				if (!GetOwner ()->Decrypt (m_Aepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
 328  				{
 329  					LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key");
 330  					return false;
 331  				}
 332  				MixKey (sharedSecret);
 333  			}
 334  			else
 335  			{
 336  				LogPrint (eLogWarning, "Garlic: No supported encryption type");
 337  				return false;
 338  			}
 339  		}
 340  
 341  		// decrypt flags/static
 342  		if (len < 48)
 343  		{
 344  			LogPrint (eLogWarning, "Garlic: Static key section is too short ", len);
 345  			return false;
 346  		}
 347  		uint8_t fs[32];
 348  		if (!Decrypt (buf, fs, 32))
 349  		{
 350  			LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed ");
 351  			return false;
 352  		}
 353  		MixHash (buf, 48); // h = SHA256(h || ciphertext)
 354  		buf += 48; len -= 48; // 32 data + 16 poly
 355  
 356  		// KDF2 for payload
 357  		bool isStatic = !i2p::data::Tag<32> (fs).IsZero ();
 358  		if (isStatic)
 359  		{
 360  			// static key, fs is apk
 361  			SetRemoteStaticKey (cryptoType, fs);
 362  			if (!GetOwner ()->Decrypt (fs, sharedSecret, m_RemoteStaticKeyType)) // x25519(bsk, apk)
 363  			{
 364  				LogPrint (eLogWarning, "Garlic: Incorrect Alice static key");
 365  				return false;
 366  			}
 367  			MixKey (sharedSecret);
 368  		}
 369  
 370  		// decrypt payload
 371  		if (len < 16)
 372  		{
 373  			LogPrint (eLogWarning, "Garlic: Payload section is too short ", len);
 374  			return false;
 375  		}
 376  		std::vector<uint8_t> payload (len - 16); // we must save original ciphertext
 377  		if (!Decrypt (buf, payload.data (), len - 16))
 378  		{
 379  			LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed");
 380  			return false;
 381  		}
 382  
 383  		m_State = eSessionStateNewSessionReceived;
 384  		if (isStatic)
 385  		{
 386  			MixHash (buf, len); // h = SHA256(h || ciphertext)
 387  			GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
 388  		}
 389  		HandlePayload (payload.data (), len - 16, nullptr, 0);
 390  
 391  		return true;
 392  	}
 393  
 394  	void ECIESX25519AEADRatchetSession::HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index)
 395  	{
 396  		size_t offset = 0;
 397  		while (offset < len)
 398  		{
 399  			uint8_t blk = buf[offset];
 400  			offset++;
 401  			auto size = bufbe16toh (buf + offset);
 402  			offset += 2;
 403  			LogPrint (eLogDebug, "Garlic: Block type ", (int)blk, " of size ", size);
 404  			if (size > len)
 405  			{
 406  				LogPrint (eLogError, "Garlic: Unexpected block length ", size);
 407  				break;
 408  			}
 409  			switch (blk)
 410  			{
 411  				case eECIESx25519BlkGalicClove:
 412  					if (GetOwner ())
 413  						GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size, this);
 414  				break;
 415  				case eECIESx25519BlkNextKey:
 416  					LogPrint (eLogDebug, "Garlic: Next key");
 417  					if (receiveTagset)
 418  						HandleNextKey (buf + offset, size, receiveTagset);
 419  					else
 420  						LogPrint (eLogError, "Garlic: Unexpected next key block");
 421  				break;
 422  				case eECIESx25519BlkAck:
 423  				{
 424  					LogPrint (eLogDebug, "Garlic: Ack");
 425  					int numAcks = size >> 2; // /4
 426  					auto offset1 = offset;
 427  					for (auto i = 0; i < numAcks; i++)
 428  					{
 429  						uint32_t tagsetid = bufbe16toh (buf + offset1); offset1 += 2; // tagsetid
 430  						uint16_t n = bufbe16toh (buf + offset1); offset1 += 2; // N
 431  						MessageConfirmed ((tagsetid << 16) + n); // msgid = (tagsetid << 16) + N
 432  					}
 433  					break;
 434  				}
 435  				case eECIESx25519BlkAckRequest:
 436  				{
 437  					LogPrint (eLogDebug, "Garlic: Ack request");
 438  					if (receiveTagset)
 439  						m_AckRequests.push_back ({receiveTagset->GetTagSetID (), index});
 440  					break;
 441  				}
 442  				case eECIESx25519BlkTermination:
 443  					LogPrint (eLogDebug, "Garlic: Termination");
 444  					if (GetOwner ())
 445  						GetOwner ()->RemoveECIESx25519Session (m_RemoteStaticKey);
 446  					if (receiveTagset) receiveTagset->Expire ();
 447  				break;
 448  				case eECIESx25519BlkDateTime:
 449  					LogPrint (eLogDebug, "Garlic: Datetime");
 450  				break;
 451  				case eECIESx25519BlkOptions:
 452  					LogPrint (eLogDebug, "Garlic: Options");
 453  				break;
 454  				case eECIESx25519BlkPadding:
 455  					LogPrint (eLogDebug, "Garlic: Padding");
 456  				break;
 457  				default:
 458  					LogPrint (eLogWarning, "Garlic: Unknown block type ", (int)blk);
 459  			}
 460  			offset += size;
 461  		}
 462  		if (IsResponseRequired () && GetOwner ())
 463  			GetOwner ()->ScheduleSessionResponseTimer (shared_from_this());
 464  	}
 465  
 466  	void ECIESX25519AEADRatchetSession::HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset)
 467  	{
 468  		uint8_t flag = buf[0]; buf++; // flag
 469  		if (flag & ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG)
 470  		{
 471  			if (!m_SendForwardKey || !m_NextSendRatchet) return;
 472  			uint16_t keyID = bufbe16toh (buf); buf += 2; // keyID
 473  			if (((!m_NextSendRatchet->newKey || !m_NextSendRatchet->keyID) && keyID == m_NextSendRatchet->keyID) ||
 474  				(m_NextSendRatchet->newKey && keyID == m_NextSendRatchet->keyID -1))
 475  			{
 476  				if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG)
 477  					memcpy (m_NextSendRatchet->remote, buf, 32);
 478  				uint8_t sharedSecret[32], tagsetKey[32];
 479  				m_NextSendRatchet->key->Agree (m_NextSendRatchet->remote, sharedSecret);
 480  				i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32)
 481  				auto newTagset = std::make_shared<RatchetTagSet> ();
 482  				newTagset->SetTagSetID (1 + m_NextSendRatchet->keyID + keyID);
 483  				newTagset->DHInitialize (m_SendTagset->GetNextRootKey (), tagsetKey);
 484  				newTagset->NextSessionTagRatchet ();
 485  				m_SendTagset = newTagset;
 486  				m_SendForwardKey = false;
 487  				LogPrint (eLogDebug, "Garlic: Next send tagset ", newTagset->GetTagSetID (), " created");
 488  			}
 489  			else
 490  				LogPrint (eLogDebug, "Garlic: Unexpected next key ", keyID);
 491  		}
 492  		else
 493  		{
 494  			uint16_t keyID = bufbe16toh (buf); buf += 2; // keyID
 495  			bool newKey = flag & ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG;
 496  			if (!m_NextReceiveRatchet)
 497  				m_NextReceiveRatchet.reset (new DHRatchet ());
 498  			else
 499  			{
 500  				if (keyID == m_NextReceiveRatchet->keyID && newKey == m_NextReceiveRatchet->newKey)
 501  				{
 502  					LogPrint (eLogDebug, "Garlic: Duplicate ", newKey ? "new" : "old", " key ", keyID, " received");
 503  					return;
 504  				}
 505  				m_NextReceiveRatchet->keyID = keyID;
 506  			}
 507  			if (newKey)
 508  			{
 509  				m_NextReceiveRatchet->key = i2p::transport::transports.GetNextX25519KeysPair ();
 510  				m_NextReceiveRatchet->newKey = true;
 511  			}
 512  			else
 513  				m_NextReceiveRatchet->newKey = false;
 514  			auto tagsetID = m_NextReceiveRatchet->GetReceiveTagSetID ();
 515  			if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG)
 516  				memcpy (m_NextReceiveRatchet->remote, buf, 32);
 517  
 518  			uint8_t sharedSecret[32], tagsetKey[32];
 519  			m_NextReceiveRatchet->key->Agree (m_NextReceiveRatchet->remote, sharedSecret);
 520  			i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32)
 521  			auto newTagset = std::make_shared<ReceiveRatchetTagSet>(shared_from_this ());
 522  			newTagset->SetTagSetID (tagsetID);
 523  			newTagset->DHInitialize (receiveTagset->GetNextRootKey (), tagsetKey);
 524  			newTagset->NextSessionTagRatchet ();
 525  			GenerateMoreReceiveTags (newTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ?
 526  				GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MAX_NUM_GENERATED_TAGS);
 527  			receiveTagset->Expire ();
 528  
 529  			LogPrint (eLogDebug, "Garlic: Next receive tagset ", tagsetID, " created");
 530  			m_SendReverseKey = true;
 531  		}
 532  	}
 533  
 534  	void ECIESX25519AEADRatchetSession::NewNextSendRatchet ()
 535  	{
 536  		if (m_NextSendRatchet)
 537  		{
 538  			if (!m_NextSendRatchet->newKey || !m_NextSendRatchet->keyID)
 539  			{
 540  				m_NextSendRatchet->keyID++;
 541  				m_NextSendRatchet->newKey = true;
 542  			}
 543  			else
 544  				m_NextSendRatchet->newKey = false;
 545  		}
 546  		else
 547  			m_NextSendRatchet.reset (new DHRatchet ());
 548  		if (m_NextSendRatchet->newKey)
 549  			m_NextSendRatchet->key = i2p::transport::transports.GetNextX25519KeysPair ();
 550  
 551  		m_SendForwardKey = true;
 552  		LogPrint (eLogDebug, "Garlic: New send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", m_NextSendRatchet->keyID, " created");
 553  	}
 554  
 555  	bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic)
 556  	{
 557  		// we are Alice, bpk is m_RemoteStaticKey
 558  		size_t offset = 0;
 559  		if (!GenerateEphemeralKeysAndEncode (out + offset))
 560  		{
 561  			LogPrint (eLogError, "Garlic: Can't encode elligator");
 562  			return false;
 563  		}
 564  		offset += 32;
 565  
 566  		// KDF1
 567  #if OPENSSL_PQ
 568  		if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
 569  		{
 570  			i2p::crypto::InitNoiseIKStateMLKEM (GetNoiseState (), m_RemoteStaticKeyType, m_RemoteStaticKey); // bpk
 571  			m_PQKeys = i2p::crypto::CreateMLKEMKeys (m_RemoteStaticKeyType);
 572  			m_PQKeys->GenerateKeys ();
 573  		}
 574  		else
 575  #endif
 576  			i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // bpk
 577  		MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
 578  		uint8_t sharedSecret[32];
 579  		if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk)
 580  		{
 581  			LogPrint (eLogWarning, "Garlic: Incorrect Bob static key");
 582  			return false;
 583  		}
 584  		MixKey (sharedSecret);
 585  #if OPENSSL_PQ
 586  		if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
 587  		{
 588  			auto keyLen = i2p::crypto::GetMLKEMPublicKeyLen (m_RemoteStaticKeyType);
 589  			std::vector<uint8_t> encapsKey(keyLen);
 590  			m_PQKeys->GetPublicKey (encapsKey.data ());
 591  			// encrypt encapsKey
 592  			if (!Encrypt (encapsKey.data (), out + offset, keyLen))
 593  			{
 594  				LogPrint (eLogWarning, "Garlic: ML-KEM encap_key section AEAD encryption failed ");
 595  				return false;
 596  			}
 597  			MixHash (out + offset, keyLen + 16); // h = SHA256(h || ciphertext)
 598  			offset += keyLen + 16;
 599  		}
 600  #endif
 601  		// encrypt flags/static key section
 602  		const uint8_t * fs;
 603  		if (isStatic)
 604  			fs = GetOwner ()->GetEncryptionPublicKey (m_RemoteStaticKeyType);
 605  		else
 606  		{
 607  			memset (out + offset, 0, 32); // all zeros flags section
 608  			fs = out + offset;
 609  		}
 610  		if (!Encrypt (fs, out + offset, 32))
 611  		{
 612  			LogPrint (eLogWarning, "Garlic: Flags/static section AEAD encryption failed ");
 613  			return false;
 614  		}
 615  
 616  		MixHash (out + offset, 48); // h = SHA256(h || ciphertext)
 617  		offset += 48;
 618  		// KDF2
 619  		if (isStatic)
 620  		{
 621  			GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, m_RemoteStaticKeyType); // x25519 (ask, bpk)
 622  			MixKey (sharedSecret);
 623  		}
 624  		// encrypt payload
 625  		if (!Encrypt (payload, out + offset, len))
 626  		{
 627  			LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
 628  			return false;
 629  		}
 630  
 631  		m_State = eSessionStateNewSessionSent;
 632  		if (isStatic)
 633  		{
 634  			MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext)
 635  			if (GetOwner ())
 636  			{
 637  				auto tagsetNsr = std::make_shared<ReceiveRatchetTagSet>(shared_from_this (), true);
 638  				InitNewSessionTagset (tagsetNsr);
 639  				tagsetNsr->Expire (); // let non-replied session expire
 640  				GenerateMoreReceiveTags (tagsetNsr, ECIESX25519_NSR_NUM_GENERATED_TAGS);
 641  			}
 642  		}
 643  		return true;
 644  	}
 645  
 646  	bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
 647  	{
 648  		// we are Bob
 649  		m_NSRSendTagset = std::make_shared<RatchetTagSet>();
 650  		InitNewSessionTagset (m_NSRSendTagset);
 651  		uint64_t tag = m_NSRSendTagset->GetNextSessionTag ();
 652  
 653  		size_t offset = 0;
 654  		memcpy (out + offset, &tag, 8);
 655  		offset += 8;
 656  		if (!GenerateEphemeralKeysAndEncode (out + offset)) // bepk
 657  		{
 658  			LogPrint (eLogError, "Garlic: Can't encode elligator");
 659  			return false;
 660  		}
 661  		memcpy (m_NSREncodedKey, out + offset, 32); // for possible next NSR
 662  		memcpy (m_NSRH, m_H, 32);
 663  		offset += 32;
 664  		// KDF for Reply Key Section
 665  		MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
 666  		MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk)
 667  		uint8_t sharedSecret[32];
 668  		if (!m_EphemeralKeys->Agree (m_Aepk, sharedSecret)) // sharedSecret = x25519(besk, aepk)
 669  		{
 670  			LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key");
 671  			return false;
 672  		}
 673  		MixKey (sharedSecret);
 674  #if OPENSSL_PQ
 675  		if (m_PQKeys)
 676  		{
 677  			size_t cipherTextLen = i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType);
 678  			std::vector<uint8_t> kemCiphertext(cipherTextLen);
 679  			m_PQKeys->Encaps (kemCiphertext.data (), sharedSecret);
 680  
 681  			if (!Encrypt (kemCiphertext.data (), out + offset, cipherTextLen))
 682  			{
 683  				LogPrint (eLogWarning, "Garlic: NSR ML-KEM ciphertext section AEAD encryption failed");
 684  				return false;
 685  			}
 686  			m_NSREncodedPQKey = std::make_unique<std::vector<uint8_t> > (cipherTextLen + 16);
 687  			memcpy (m_NSREncodedPQKey->data (), out + offset, cipherTextLen + 16);
 688  			MixHash (out + offset, cipherTextLen + 16);
 689  			MixKey (sharedSecret);
 690  			offset += cipherTextLen + 16;
 691  		}
 692  #endif
 693  		if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // sharedSecret = x25519(besk, apk)
 694  		{
 695  			LogPrint (eLogWarning, "Garlic: Incorrect Alice static key");
 696  			return false;
 697  		}
 698  		MixKey (sharedSecret);
 699  		// calculate hash for zero length
 700  		if (!Encrypt (sharedSecret /* can be anything */, out + offset, 0)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
 701  		{
 702  			LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed");
 703  			return false;
 704  		}
 705  		MixHash (out + offset, 16); // h = SHA256(h || ciphertext)
 706  		offset += 16;
 707  		// KDF for payload
 708  		uint8_t keydata[64];
 709  		i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64)
 710  		// k_ab = keydata[0:31], k_ba = keydata[32:63]
 711  		auto receiveTagset = std::make_shared<ReceiveRatchetTagSet>(shared_from_this());
 712  		receiveTagset->DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
 713  		receiveTagset->NextSessionTagRatchet ();
 714  		m_SendTagset = std::make_shared<RatchetTagSet>();
 715  		m_SendTagset->DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
 716  		m_SendTagset->NextSessionTagRatchet ();
 717  		GenerateMoreReceiveTags (receiveTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ?
 718  			GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MIN_NUM_GENERATED_TAGS);
 719  		i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
 720  		// encrypt payload
 721  		uint8_t nonce[12]; memset (nonce, 0, 12); // seqn = 0
 722  		if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt
 723  		{
 724  			LogPrint (eLogWarning, "Garlic: NSR payload section AEAD encryption failed");
 725  			return false;
 726  		}
 727  		m_State = eSessionStateNewSessionReplySent;
 728  		m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch ();
 729  
 730  		return true;
 731  	}
 732  
 733  	bool ECIESX25519AEADRatchetSession::NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
 734  	{
 735  		// we are Bob and sent NSR already
 736  		uint64_t tag = m_NSRSendTagset->GetNextSessionTag (); // next tag
 737  		memcpy (out, &tag, 8);
 738  		memcpy (out + 8, m_NSREncodedKey, 32);
 739  		// recalculate h with new tag
 740  		memcpy (m_H, m_NSRH, 32);
 741  		MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
 742  		MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk)
 743  		m_N = 0;
 744  		size_t offset = 40;
 745  #if OPENSSL_PQ
 746  		if (m_PQKeys)
 747  		{
 748  			if (m_NSREncodedPQKey)
 749  			{
 750  				size_t cipherTextLen = i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType);
 751  				memcpy (out + offset, m_NSREncodedPQKey->data (), cipherTextLen + 16);
 752  				MixHash (out + offset, cipherTextLen + 16);
 753  				offset += cipherTextLen + 16;
 754  			}
 755  			else
 756  			{
 757  				LogPrint (eLogWarning, "Garlic: No stored ML-KEM keys");
 758  				return false;
 759  			}
 760  		}
 761  #endif
 762  		if (!Encrypt (m_NSRH /* can be anything */, out + offset, 0)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
 763  		{
 764  			LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed");
 765  			return false;
 766  		}
 767  		MixHash (out + offset, 16); // h = SHA256(h || ciphertext)
 768  		// encrypt payload
 769  		uint8_t nonce[12]; memset (nonce, 0, 12);
 770  		if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset + 16, len + 16, true)) // encrypt
 771  		{
 772  			LogPrint (eLogWarning, "Garlic: Next NSR payload section AEAD encryption failed");
 773  			return false;
 774  		}
 775  		return true;
 776  	}
 777  
 778  	bool ECIESX25519AEADRatchetSession::HandleNewOutgoingSessionReply (uint8_t * buf, size_t len)
 779  	{
 780  		// we are Alice
 781  		LogPrint (eLogDebug, "Garlic: Reply received");
 782  		const uint8_t * tag = buf;
 783  		buf += 8; len -= 8; // tag
 784  		uint8_t bepk[32]; // Bob's ephemeral key
 785  		if (len < 32 || !i2p::crypto::GetElligator ()->Decode (buf, bepk))
 786  		{
 787  			LogPrint (eLogError, "Garlic: Can't decode elligator");
 788  			return false;
 789  		}
 790  		buf += 32; len -= 32;
 791  		// KDF for Reply Key Section
 792  		i2p::util::SaveStateHelper<i2p::crypto::NoiseSymmetricState> s(GetNoiseState ()); // restore noise state on exit
 793  		MixHash (tag, 8); // h = SHA256(h || tag)
 794  		MixHash (bepk, 32); // h = SHA256(h || bepk)
 795  		uint8_t sharedSecret[32];
 796  		if (!m_EphemeralKeys->Agree (bepk, sharedSecret)) // sharedSecret = x25519(aesk, bepk)
 797  		{
 798  			LogPrint (eLogWarning, "Garlic: Incorrect Bob ephemeral key");
 799  			return false;
 800  		}
 801  		MixKey (sharedSecret);
 802  #if OPENSSL_PQ
 803  		if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
 804  		{
 805  			// decrypt kem_ciphertext section
 806  			size_t cipherTextLen = i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType);
 807  			if (cipherTextLen + 16 > len || !cipherTextLen)
 808  			{
 809  				LogPrint (eLogWarning, "Garlic: ML-KEM cipher test section is too short ", len, ". Expected ", cipherTextLen + 16);
 810  				return false;
 811  			}
 812  			std::vector<uint8_t> kemCiphertext(cipherTextLen);
 813  			if (!Decrypt (buf, kemCiphertext.data (), cipherTextLen))
 814  			{
 815  				LogPrint (eLogWarning, "Garlic: Reply ML-KEM ciphertext section AEAD decryption failed");
 816  				return false;
 817  			}
 818  			MixHash (buf, cipherTextLen + 16);
 819  			buf += cipherTextLen + 16;
 820  			len -= cipherTextLen + 16;
 821  			// decaps
 822  			m_PQKeys->Decaps (kemCiphertext.data (), sharedSecret);
 823  			MixKey (sharedSecret);
 824  		}
 825  #endif
 826  		GetOwner ()->Decrypt (bepk, sharedSecret, m_RemoteStaticKeyType); // x25519 (ask, bepk)
 827  		MixKey (sharedSecret);
 828  
 829  		// calculate hash for zero length
 830  		if (len < 16)
 831  		{
 832  			LogPrint (eLogWarning, "Garlic: Zero length section is too short ", len);
 833  			return false;
 834  		}
 835  		if (!Decrypt (buf, sharedSecret/* can be anything */, 0)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only
 836  		{
 837  			LogPrint (eLogWarning, "Garlic: Reply key section AEAD decryption failed");
 838  			return false;
 839  		}
 840  		MixHash (buf, 16); // h = SHA256(h || ciphertext)
 841  		buf += 16; len -= 16;
 842  		// KDF for payload
 843  		uint8_t keydata[64];
 844  		i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64)
 845  		if (m_State == eSessionStateNewSessionSent)
 846  		{
 847  			// only first time, then we keep using existing tagsets
 848  			// k_ab = keydata[0:31], k_ba = keydata[32:63]
 849  			m_SendTagset = std::make_shared<RatchetTagSet>();
 850  			m_SendTagset->DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
 851  			m_SendTagset->NextSessionTagRatchet ();
 852  			auto receiveTagset = std::make_shared<ReceiveRatchetTagSet>(shared_from_this ());
 853  			receiveTagset->DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
 854  			receiveTagset->NextSessionTagRatchet ();
 855  			GenerateMoreReceiveTags (receiveTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ?
 856  				GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MIN_NUM_GENERATED_TAGS);
 857  		}
 858  		i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
 859  		// decrypt payload
 860  		if (len < 16)
 861  		{
 862  			LogPrint (eLogWarning, "Garlic: Payload section is too short ", len);
 863  			return false;
 864  		}
 865  		uint8_t nonce[12]; memset (nonce, 0, 12); // seqn = 0
 866  		if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, buf, len - 16, false)) // decrypt
 867  		{
 868  			LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
 869  			return false;
 870  		}
 871  
 872  		if (m_State == eSessionStateNewSessionSent)
 873  		{
 874  			m_State = eSessionStateEstablished;
 875  			// don't delete m_EpehemralKey and m_PQKeys because delayed NSR's might come
 876  			// done in CleanupReceiveNSRKeys called from NSR tagset destructor
 877  			m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch ();
 878  			GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
 879  		}
 880  		HandlePayload (buf, len - 16, nullptr, 0);
 881  
 882  		// we have received reply to NS with LeaseSet in it
 883  		SetLeaseSetUpdateStatus (eLeaseSetUpToDate);
 884  		SetLeaseSetUpdateMsgID (0);
 885  
 886  		return true;
 887  	}
 888  
 889  	bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
 890  	{
 891  		auto owner = GetOwner ();
 892  		if (!owner) return false;
 893  		uint8_t nonce[12];
 894  		auto index = m_SendTagset->GetNextIndex ();
 895  		CreateNonce (index, nonce); // tag's index
 896  		uint64_t tag = m_SendTagset->GetNextSessionTag ();
 897  		if (!tag)
 898  		{
 899  			LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset");
 900  			owner->RemoveECIESx25519Session (m_RemoteStaticKey);
 901  			return false;
 902  		}
 903  		memcpy (out, &tag, 8);
 904  		// ad = The session tag, 8 bytes
 905  		// ciphertext = ENCRYPT(k, n, payload, ad)
 906  		uint8_t key[32];
 907  		m_SendTagset->GetSymmKey (index, key);
 908  		if (!owner->AEADChaCha20Poly1305Encrypt (payload, len, out, 8, key, nonce, out + 8, outLen - 8))
 909  		{
 910  			LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
 911  			return false;
 912  		}
 913  		if (index >= ECIESX25519_TAGSET_MAX_NUM_TAGS && !m_SendForwardKey)
 914  			NewNextSendRatchet ();
 915  		return true;
 916  	}
 917  
 918  	bool ECIESX25519AEADRatchetSession::HandleExistingSessionMessage (uint8_t * buf, size_t len,
 919  		std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index)
 920  	{
 921  		uint8_t nonce[12];
 922  		CreateNonce (index, nonce); // tag's index
 923  		len -= 8; // tag
 924  		uint8_t * payload = buf + 8;
 925  		uint8_t key[32];
 926  		receiveTagset->GetSymmKey (index, key);
 927  		auto owner = GetOwner ();
 928  		if (!owner) return true; // drop message
 929  
 930  		if (len < 16 || !owner->AEADChaCha20Poly1305Decrypt (payload, len - 16, buf, 8, key, nonce, payload, len - 16))
 931  		{
 932  			LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
 933  			return false;
 934  		}
 935  		HandlePayload (payload, len - 16, receiveTagset, index);
 936  
 937  		int moreTags = 0;
 938  		if (owner->GetNumRatchetInboundTags () > 0) // override in settings?
 939  		{
 940  			if (receiveTagset->GetNextIndex () - index < owner->GetNumRatchetInboundTags ()/2)
 941  				moreTags = owner->GetNumRatchetInboundTags ();
 942  			index -= owner->GetNumRatchetInboundTags (); // trim behind
 943  		}
 944  		else
 945  		{
 946  			moreTags = (receiveTagset->GetTagSetID () > 0) ? ECIESX25519_MAX_NUM_GENERATED_TAGS : // for non first tagset
 947  				(ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 1)); // N/2
 948  			if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS;
 949  			moreTags -= (receiveTagset->GetNextIndex () - index);
 950  			index -= ECIESX25519_MAX_NUM_GENERATED_TAGS; // trim behind
 951  		}
 952  		if (moreTags > 0)
 953  			GenerateMoreReceiveTags (receiveTagset, moreTags);
 954  		if (index > 0)
 955  			receiveTagset->SetTrimBehind (index);
 956  		return true;
 957  	}
 958  
 959  	bool ECIESX25519AEADRatchetSession::HandleNextMessage (uint8_t * buf, size_t len,
 960  		std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index)
 961  	{
 962  		m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
 963  		switch (m_State)
 964  		{
 965  			case eSessionStateNewSessionReplySent:
 966  				m_State = eSessionStateEstablished;
 967  				m_NSRSendTagset = nullptr;
 968  				m_EphemeralKeys = nullptr;
 969  #if OPENSSL_PQ
 970  				m_PQKeys = nullptr;
 971  				m_NSREncodedPQKey = nullptr;
 972  #endif
 973  				[[fallthrough]];
 974  			case eSessionStateEstablished:
 975  				if (m_SendReverseKey && receiveTagset->GetTagSetID () == m_NextReceiveRatchet->GetReceiveTagSetID ())
 976  					m_SendReverseKey = false; // tag received on new tagset
 977  				if (receiveTagset->IsNS ())
 978  				{
 979  					// our of sequence NSR
 980  					LogPrint (eLogDebug, "Garlic: Check for out of order NSR with index ", index);
 981  					if (receiveTagset->GetNextIndex () - index < ECIESX25519_NSR_NUM_GENERATED_TAGS/2)
 982  						GenerateMoreReceiveTags (receiveTagset, ECIESX25519_NSR_NUM_GENERATED_TAGS);
 983  					return HandleNewOutgoingSessionReply (buf, len);
 984  				}
 985  				else
 986  					return HandleExistingSessionMessage (buf, len, receiveTagset, index);
 987  			case eSessionStateNew:
 988  				return HandleNewIncomingSession (buf, len);
 989  			case eSessionStateNewSessionSent:
 990  				return HandleNewOutgoingSessionReply (buf, len);
 991  			default:
 992  				return false;
 993  		}
 994  		return true;
 995  	}
 996  
 997  	std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
 998  	{
 999  		uint8_t * payload = GetOwner ()->GetPayloadBuffer ();
1000  		if (!payload) return nullptr;
1001  		size_t len = CreatePayload (msg, m_State != eSessionStateEstablished, payload);
1002  		if (!len) return nullptr;
1003  		return WrapPayload (payload, len);
1004  	}
1005  
1006  	std::vector<std::shared_ptr<I2NPMessage> > ECIESX25519AEADRatchetSession::WrapMultipleMessages (const std::vector<std::shared_ptr<const I2NPMessage> >& msgs)
1007  	{
1008  		if (m_State != eSessionStateEstablished)
1009  			return GarlicRoutingSession::WrapMultipleMessages (msgs);
1010  
1011  		std::vector<std::shared_ptr<I2NPMessage> > ret;
1012  		if (!msgs.empty ())
1013  		{
1014  			ret.push_back (WrapSingleMessage (msgs[0]));
1015  			if (msgs.size () > 1)
1016  			{
1017  				uint8_t * payload = GetOwner ()->GetPayloadBuffer ();
1018  				size_t len = 0;
1019  				auto it = msgs.begin (); it++;
1020  				while (it != msgs.end ())
1021  				{
1022  					if (*it != nullptr)
1023  					{
1024  						if ((*it)->GetPayloadLength () + 13 + len > ECIESX25519_OPTIMAL_PAYLOAD_SIZE)
1025  						{
1026  							auto paddingSize = GetNextPaddingSize (len);
1027  							if (paddingSize)
1028  								len += CreatePaddingClove (paddingSize, payload + len, I2NP_MAX_MESSAGE_SIZE - len);
1029  							ret.push_back (WrapPayload (payload, len));
1030  							len = 0;
1031  						}
1032  						len += CreateGarlicClove (*it, payload + len, I2NP_MAX_MESSAGE_SIZE - len, true);
1033  					}
1034  					it++;
1035  				}
1036  				if (len > 0)
1037  				{
1038  					auto paddingSize = GetNextPaddingSize (len);
1039  					if (paddingSize)
1040  						len += CreatePaddingClove (paddingSize, payload + len, I2NP_MAX_MESSAGE_SIZE - len);
1041  					ret.push_back (WrapPayload (payload, len));
1042  				}
1043  			}
1044  		}
1045  		return ret;
1046  	}
1047  
1048  	std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapPayload (const uint8_t * payload, size_t len)
1049  	{
1050  #if OPENSSL_PQ
1051  		auto m = NewI2NPMessage (len + (m_State == eSessionStateEstablished ? 28 :
1052  			i2p::crypto::GetMLKEMPublicKeyLen (m_RemoteStaticKeyType) + 116));
1053  #else
1054  		auto m = NewI2NPMessage (len + 100); // 96 + 4
1055  #endif
1056  		m->Align (12); // in order to get buf aligned to 16 (12 + 4)
1057  		uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
1058  
1059  		switch (m_State)
1060  		{
1061  			case eSessionStateEstablished:
1062  				if (!NewExistingSessionMessage (payload, len, buf, m->maxLen))
1063  					return nullptr;
1064  				len += 24;
1065  			break;
1066  			case eSessionStateNew:
1067  				if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen))
1068  					return nullptr;
1069  				len += 96;
1070  #if OPENSSL_PQ
1071  				if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
1072  					len += i2p::crypto::GetMLKEMPublicKeyLen (m_RemoteStaticKeyType) + 16;
1073  #endif
1074  			break;
1075  			case eSessionStateNewSessionReceived:
1076  				if (!NewSessionReplyMessage (payload, len, buf, m->maxLen))
1077  					return nullptr;
1078  				len += 72;
1079  #if OPENSSL_PQ
1080  				if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
1081  					len += i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType) + 16;
1082  #endif
1083  			break;
1084  			case eSessionStateNewSessionReplySent:
1085  				if (!NextNewSessionReplyMessage (payload, len, buf, m->maxLen))
1086  					return nullptr;
1087  				len += 72;
1088  #if OPENSSL_PQ
1089  				if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
1090  					len += i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType) + 16;
1091  #endif
1092  			break;
1093  			case eSessionStateOneTime:
1094  				if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false))
1095  					return nullptr;
1096  				len += 96;
1097  			break;
1098  			default:
1099  				return nullptr;
1100  		}
1101  
1102  		htobe32buf (m->GetPayload (), len);
1103  		m->len += len + 4;
1104  		m->FillI2NPMessageHeader (eI2NPGarlic);
1105  		return m;
1106  	}
1107  
1108  	std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg)
1109  	{
1110  		m_State = eSessionStateOneTime;
1111  		return WrapSingleMessage (msg);
1112  	}
1113  
1114  	size_t ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first, uint8_t * payload)
1115  	{
1116  		uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
1117  		size_t payloadLen = 0;
1118  		bool sendAckRequest = false;
1119  		if (first) payloadLen += 7;// datatime
1120  		if (msg)
1121  		{
1122  			payloadLen += msg->GetPayloadLength () + 13;
1123  			if (m_Destination) payloadLen += 32;
1124  		}
1125  		if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASESET_CONFIRMATION_TIMEOUT)
1126  		{
1127  			// resubmit non-confirmed LeaseSet
1128  			SetLeaseSetUpdateStatus (eLeaseSetUpdated);
1129  			SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed
1130  		}
1131  		auto leaseSet = (GetLeaseSetUpdateStatus () == eLeaseSetUpdated) ? GetOwner ()->GetLeaseSet () : nullptr;
1132  		if (leaseSet)
1133  		{
1134  			payloadLen += leaseSet->GetBufferLen () + DATABASE_STORE_HEADER_SIZE + 13;
1135  			if (!first)
1136  			{
1137  				// ack request for LeaseSet
1138  				m_AckRequestMsgID = m_SendTagset->GetMsgID ();
1139  				sendAckRequest = true;
1140  				// update LeaseSet status
1141  				SetLeaseSetUpdateStatus (eLeaseSetSubmitted);
1142  				SetLeaseSetUpdateMsgID (m_AckRequestMsgID);
1143  				SetLeaseSetSubmissionTime (ts);
1144  			}
1145  		}
1146  		if (!sendAckRequest && !first &&
1147  		    ((!m_AckRequestMsgID && ts > m_LastAckRequestSendTime + m_AckRequestInterval) || // regular request
1148  		     (m_AckRequestMsgID && ts > m_LastAckRequestSendTime + LEASESET_CONFIRMATION_TIMEOUT))) // previous request failed. try again
1149  		{
1150  			// not LeaseSet
1151  			m_AckRequestMsgID = m_SendTagset->GetMsgID ();
1152  			if (m_AckRequestMsgID)
1153  			{
1154  				m_AckRequestNumAttempts++;
1155  				sendAckRequest = true;
1156  			}
1157  		}
1158  		if (sendAckRequest) payloadLen += 4;
1159  		if (m_AckRequests.size () > 0)
1160  			payloadLen += m_AckRequests.size ()*4 + 3;
1161  		if (m_SendReverseKey)
1162  		{
1163  			payloadLen += 6;
1164  			if (m_NextReceiveRatchet->newKey) payloadLen += 32;
1165  		}
1166  		if (m_SendForwardKey)
1167  		{
1168  			payloadLen += 6;
1169  			if (m_NextSendRatchet->newKey) payloadLen += 32;
1170  		}
1171  		uint8_t paddingSize = 0;
1172  		if (payloadLen || ts > m_LastSentTimestamp + ECIESX25519_SEND_INACTIVITY_TIMEOUT)
1173  		{
1174  			paddingSize = GetNextPaddingSize (payloadLen);
1175  			if (paddingSize)
1176  				payloadLen += paddingSize + 3;
1177  		}
1178  		if (payloadLen)
1179  		{
1180  			if (payloadLen > I2NP_MAX_MESSAGE_SIZE)
1181  			{
1182  				LogPrint (eLogError, "Garlic: Payload length ", payloadLen, " is too long");
1183  				return 0;
1184  			}
1185  			m_LastSentTimestamp = ts;
1186  			size_t offset = 0;
1187  			// DateTime
1188  			if (first)
1189  			{
1190  				payload[offset] = eECIESx25519BlkDateTime; offset++;
1191  				htobe16buf (payload + offset, 4); offset += 2;
1192  				htobe32buf (payload + offset, ts/1000); offset += 4; // in seconds
1193  			}
1194  			// LeaseSet
1195  			if (leaseSet)
1196  				offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset);
1197  			// ack request
1198  			if (sendAckRequest)
1199  			{
1200  				payload[offset] = eECIESx25519BlkAckRequest; offset++;
1201  				htobe16buf (payload + offset, 1); offset += 2;
1202  				payload[offset] = 0; offset++; // flags
1203  				m_LastAckRequestSendTime = ts;
1204  			}
1205  			// msg
1206  			if (msg)
1207  				offset += CreateGarlicClove (msg, payload + offset, payloadLen - offset);
1208  			// ack
1209  			if (m_AckRequests.size () > 0)
1210  			{
1211  				payload[offset] = eECIESx25519BlkAck; offset++;
1212  				htobe16buf (payload + offset, m_AckRequests.size () * 4); offset += 2;
1213  				for (auto& it: m_AckRequests)
1214  				{
1215  					htobe16buf (payload + offset, it.first); offset += 2;
1216  					htobe16buf (payload + offset, it.second); offset += 2;
1217  				}
1218  				m_AckRequests.clear ();
1219  			}
1220  			// next keys
1221  			if (m_SendReverseKey)
1222  			{
1223  				payload[offset] = eECIESx25519BlkNextKey; offset++;
1224  				htobe16buf (payload + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2;
1225  				payload[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG;
1226  				int keyID = m_NextReceiveRatchet->keyID - 1;
1227  				if (m_NextReceiveRatchet->newKey)
1228  				{
1229  					payload[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG;
1230  					keyID++;
1231  				}
1232  				offset++; // flag
1233  				htobe16buf (payload + offset, keyID); offset += 2; // keyid
1234  				if (m_NextReceiveRatchet->newKey)
1235  				{
1236  					memcpy (payload + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32);
1237  					offset += 32; // public key
1238  				}
1239  			}
1240  			if (m_SendForwardKey)
1241  			{
1242  				payload[offset] = eECIESx25519BlkNextKey; offset++;
1243  				htobe16buf (payload + offset, m_NextSendRatchet->newKey ? 35 : 3); offset += 2;
1244  				payload[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG;
1245  				if (!m_NextSendRatchet->keyID) payload[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only
1246  				offset++; // flag
1247  				htobe16buf (payload + offset, m_NextSendRatchet->keyID); offset += 2; // keyid
1248  				if (m_NextSendRatchet->newKey)
1249  				{
1250  					memcpy (payload + offset, m_NextSendRatchet->key->GetPublicKey (), 32);
1251  					offset += 32; // public key
1252  				}
1253  			}
1254  			// padding
1255  			if (paddingSize)
1256  				offset += CreatePaddingClove (paddingSize, payload + offset, payloadLen - offset);
1257  		}
1258  		return payloadLen;
1259  	}
1260  
1261  	uint8_t ECIESX25519AEADRatchetSession::GetNextPaddingSize (size_t payloadLen)
1262  	{
1263  		uint8_t paddingSize = 0;
1264  		int delta = (int)ECIESX25519_OPTIMAL_PAYLOAD_SIZE - (int)payloadLen;
1265  		if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size
1266  		{
1267  			paddingSize = GetOwner ()->GetRng ()() % 16; // 0 - 15
1268  			if (delta > 3)
1269  			{
1270  				delta -= 3;
1271  				if (paddingSize >= delta) paddingSize %= delta;
1272  			}
1273  			paddingSize++;
1274  		}
1275  		return paddingSize;
1276  	}
1277  
1278  	size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg,
1279  		uint8_t * buf, size_t len, bool alwaysLocal)
1280  	{
1281  		if (!msg) return 0;
1282  		uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1;
1283  		if (m_Destination) cloveSize += 32;
1284  		if ((int)len < cloveSize + 3) return 0;
1285  		buf[0] = eECIESx25519BlkGalicClove; // clove type
1286  		htobe16buf (buf + 1, cloveSize); // size
1287  		buf += 3;
1288  		if (!alwaysLocal && m_Destination)
1289  		{
1290  			*buf = (eGarlicDeliveryTypeDestination << 5);
1291  			memcpy (buf + 1, *m_Destination, 32); buf += 32;
1292  		}
1293  		else
1294  			*buf = 0;
1295  		buf++;	// flag and delivery instructions
1296  		*buf = msg->GetTypeID (); // I2NP msg type
1297  		htobe32buf (buf + 1, msg->GetMsgID ()); // msgID
1298  		htobe32buf (buf + 5, msg->GetExpiration () / 1000); // expiration in seconds
1299  		memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ());
1300  		return cloveSize + 3;
1301  	}
1302  
1303  	size_t ECIESX25519AEADRatchetSession::CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len)
1304  	{
1305  		if (!ls || ls->GetStoreType () != i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2)
1306  		{
1307  			LogPrint (eLogError, "Garlic: Incorrect LeasetSet type to send");
1308  			return 0;
1309  		}
1310  		uint16_t cloveSize = 1 + 9 + DATABASE_STORE_HEADER_SIZE + ls->GetBufferLen (); // to local
1311  		if ((int)len < cloveSize + 3) return 0;
1312  		buf[0] = eECIESx25519BlkGalicClove; // clove type
1313  		htobe16buf (buf + 1, cloveSize); // size
1314  		buf += 3;
1315  		*buf = 0; buf++; // flag and delivery instructions
1316  		*buf = eI2NPDatabaseStore; buf++; // I2NP msg type
1317  		RAND_bytes (buf, 4); buf += 4; // msgID
1318  		htobe32buf (buf, (ts + I2NP_MESSAGE_EXPIRATION_TIMEOUT)/1000); buf += 4; // expiration
1319  		// payload
1320  		memcpy (buf + DATABASE_STORE_KEY_OFFSET, ls->GetStoreHash (), 32);
1321  		buf[DATABASE_STORE_TYPE_OFFSET] = i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2;
1322  		memset (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0, 4); // replyToken = 0
1323  		buf += DATABASE_STORE_HEADER_SIZE;
1324  		memcpy (buf, ls->GetBuffer (), ls->GetBufferLen ());
1325  
1326  		return cloveSize + 3;
1327  	}
1328  
1329  	size_t ECIESX25519AEADRatchetSession::CreatePaddingClove (uint8_t paddingSize, uint8_t * buf, size_t len)
1330  	{
1331  		buf[0] = eECIESx25519BlkPadding;
1332  		htobe16buf (buf + 1, paddingSize);
1333  		memset (buf + 3, 0, paddingSize);
1334  		return paddingSize + 3;
1335  	}
1336  
1337  	void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags (std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int numTags)
1338  	{
1339  		if (GetOwner ())
1340  		{
1341  			for (int i = 0; i < numTags; i++)
1342  			{
1343  				auto tag = GetOwner ()->AddECIESx25519SessionNextTag (receiveTagset);
1344  				if (!tag)
1345  				{
1346  					LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for receive tagset");
1347  					break;
1348  				}
1349  			}
1350  		}
1351  	}
1352  
1353  	bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts)
1354  	{
1355  		CleanupUnconfirmedLeaseSet (ts);
1356  		if (!m_Destination && ts > m_LastActivityTimestamp + ECIESX25519_SESSION_CREATE_TIMEOUT) return true; // m_LastActivityTimestamp is NS receive time
1357  		if (m_State != eSessionStateEstablished && m_SessionCreatedTimestamp && ts > m_SessionCreatedTimestamp + ECIESX25519_SESSION_ESTABLISH_TIMEOUT) return true;
1358  		return ts > m_LastActivityTimestamp + ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT && // seconds
1359  			ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds
1360  	}
1361  
1362  	RouterIncomingRatchetSession::RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState):
1363  		ECIESX25519AEADRatchetSession (&i2p::context, false)
1364  	{
1365  		SetLeaseSetUpdateStatus (eLeaseSetDoNotSend);
1366  		SetNoiseState (initState);
1367  	}
1368  
1369  	bool RouterIncomingRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len)
1370  	{
1371  		if (!GetOwner ()) return false;
1372  		m_CurrentNoiseState = GetNoiseState ();
1373  		// we are Bob
1374  		m_CurrentNoiseState.MixHash (buf, 32);
1375  		uint8_t sharedSecret[32];
1376  		if (!GetOwner ()->Decrypt (buf, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
1377  		{
1378  			LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key");
1379  			return false;
1380  		}
1381  		m_CurrentNoiseState.MixKey (sharedSecret);
1382  		buf += 32; len -= 32;
1383  		uint8_t nonce[12];
1384  		CreateNonce (0, nonce);
1385  		std::vector<uint8_t> payload (len - 16);
1386  		if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_CurrentNoiseState.m_H, 32,
1387  			m_CurrentNoiseState.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
1388  		{
1389  			LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed");
1390  			return false;
1391  		}
1392  		HandlePayload (payload.data (), len - 16, nullptr, 0);
1393  		return true;
1394  	}
1395  
1396  	static size_t CreateGarlicPayload (std::shared_ptr<const I2NPMessage> msg, uint8_t * payload,
1397  		bool datetime, size_t optimalSize)
1398  	{
1399  		size_t len = 0;
1400  		if (datetime)
1401  		{
1402  			// DateTime
1403  			payload[0] = eECIESx25519BlkDateTime;
1404  			htobe16buf (payload + 1, 4);
1405  			htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ());
1406  			len = 7;
1407  		}
1408  		// I2NP
1409  		payload += len;
1410  		uint16_t cloveSize = msg->GetPayloadLength () + 10;
1411  		payload[0] = eECIESx25519BlkGalicClove; // clove type
1412  		htobe16buf (payload + 1, cloveSize); // size
1413  		payload += 3;
1414  		payload[0] = 0; // flag and delivery instructions
1415  		payload[1] = msg->GetTypeID (); // I2NP msg type
1416  		htobe32buf (payload + 2, msg->GetMsgID ()); // msgID
1417  		htobe32buf (payload + 6, msg->GetExpiration () / 1000); // expiration in seconds
1418  		memcpy (payload + 10, msg->GetPayload (), msg->GetPayloadLength ());
1419  		len += cloveSize + 3;
1420  		payload += cloveSize;
1421  		// padding
1422  		int delta = (int)optimalSize - (int)len;
1423  		if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size
1424  		{
1425  			uint8_t paddingSize = rand () & 0x0F; // 0 - 15
1426  			if (delta > 3)
1427  			{
1428  				delta -= 3;
1429  				if (paddingSize > delta) paddingSize %= delta;
1430  			}
1431  			payload[0] = eECIESx25519BlkPadding;
1432  			htobe16buf (payload + 1, paddingSize);
1433  			if (paddingSize) memset (payload + 3, 0, paddingSize);
1434  			len += paddingSize + 3;
1435  		}
1436  		return len;
1437  	}
1438  
1439  	std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<I2NPMessage> msg, const uint8_t * key, uint64_t tag)
1440  	{
1441  		auto m = NewI2NPMessage ((msg ? msg->GetPayloadLength () : 0) + 128);
1442  		m->Align (12); // in order to get buf aligned to 16 (12 + 4)
1443  		uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
1444  		size_t offset = 0;
1445  		memcpy (buf + offset, &tag, 8); offset += 8;
1446  		auto payload = buf + offset;
1447  		size_t len = CreateGarlicPayload (msg, payload, false, 956); // 1003 - 8 tag - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 3 local tunnel delivery
1448  		uint8_t nonce[12];
1449  		memset (nonce, 0, 12); // n = 0
1450  		if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, buf, 8, key, nonce, payload, len + 16, true)) // encrypt
1451  		{
1452  			LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
1453  			return nullptr;
1454  		}
1455  		offset += len + 16;
1456  		htobe32buf (m->GetPayload (), offset);
1457  		m->len += offset + 4;
1458  		m->FillI2NPMessageHeader (eI2NPGarlic);
1459  		if (msg->onDrop)
1460  		{
1461  			// move onDrop to the wrapping I2NP messages
1462  			m->onDrop = msg->onDrop;
1463  			msg->onDrop = nullptr;
1464  		}
1465  		return m;
1466  	}
1467  
1468  	std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<I2NPMessage> msg, const uint8_t * routerPublicKey)
1469  	{
1470  		// Noise_N, we are Alice, routerPublicKey is Bob's
1471  		i2p::crypto::NoiseSymmetricState noiseState;
1472  		i2p::crypto::InitNoiseNState (noiseState, routerPublicKey);
1473  		auto m = NewI2NPMessage ((msg ? msg->GetPayloadLength () : 0) + 128);
1474  		m->Align (12); // in order to get buf aligned to 16 (12 + 4)
1475  		uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
1476  		size_t offset = 0;
1477  		auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
1478  		memcpy (buf + offset, ephemeralKeys->GetPublicKey (), 32);
1479  		noiseState.MixHash (buf + offset, 32); // h = SHA256(h || aepk)
1480  		offset += 32;
1481  		uint8_t sharedSecret[32];
1482  		if (!ephemeralKeys->Agree (routerPublicKey, sharedSecret)) // x25519(aesk, bpk)
1483  		{
1484  			LogPrint (eLogWarning, "Garlic: Incorrect Bob static key");
1485  			return nullptr;
1486  		}
1487  		noiseState.MixKey (sharedSecret);
1488  		auto payload = buf + offset;
1489  		size_t len = CreateGarlicPayload (msg, payload, true, 900); // 1003 - 32 eph key - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 35 router tunnel delivery
1490  		uint8_t nonce[12];
1491  		memset (nonce, 0, 12);
1492  		// encrypt payload
1493  		if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, noiseState.m_H, 32, noiseState.m_CK + 32, nonce, payload, len + 16, true)) // encrypt
1494  		{
1495  			LogPrint (eLogWarning, "Garlic: Payload for router AEAD encryption failed");
1496  			return nullptr;
1497  		}
1498  		offset += len + 16;
1499  		htobe32buf (m->GetPayload (), offset);
1500  		m->len += offset + 4;
1501  		m->FillI2NPMessageHeader (eI2NPGarlic);
1502  		if (msg->onDrop)
1503  		{
1504  			// move onDrop to the wrapping I2NP messages
1505  			m->onDrop = msg->onDrop;
1506  			msg->onDrop = nullptr;
1507  		}
1508  		return m;
1509  	}
1510  }
1511  }