1 /* 2 * Copyright (c) 2013-2021, 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 <openssl/sha.h> 11 #include "Log.h" 12 #include "util.h" 13 #include "Crypto.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 DHInitialize(const uint8_t * rootKey,const uint8_t * k)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 NextSessionTagRatchet()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 GetNextSessionTag()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 GetSymmKey(int index,uint8_t * key)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 DeleteSymmKey(int index)92 void RatchetTagSet::DeleteSymmKey (int index) 93 { 94 m_ItermediateSymmKeys.erase (index); 95 } 96 Expire()97 void ReceiveRatchetTagSet::Expire () 98 { 99 if (!m_ExpirationTimestamp) 100 m_ExpirationTimestamp = i2p::util::GetSecondsSinceEpoch () + ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT; 101 } 102 IsExpired(uint64_t ts) const103 bool ReceiveRatchetTagSet::IsExpired (uint64_t ts) const 104 { 105 return m_ExpirationTimestamp && ts > m_ExpirationTimestamp; 106 } 107 IsIndexExpired(int index) const108 bool ReceiveRatchetTagSet::IsIndexExpired (int index) const 109 { 110 return index < m_TrimBehindIndex; 111 } 112 HandleNextMessage(uint8_t * buf,size_t len,int index)113 bool ReceiveRatchetTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index) 114 { 115 auto session = GetSession (); 116 if (!session) return false; 117 return session->HandleNextMessage (buf, len, shared_from_this (), index); 118 } 119 SymmetricKeyTagSet(GarlicDestination * destination,const uint8_t * key)120 SymmetricKeyTagSet::SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key): 121 ReceiveRatchetTagSet (nullptr), m_Destination (destination) 122 { 123 memcpy (m_Key, key, 32); 124 Expire (); 125 } 126 HandleNextMessage(uint8_t * buf,size_t len,int index)127 bool SymmetricKeyTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index) 128 { 129 if (len < 24) return false; 130 uint8_t nonce[12]; 131 memset (nonce, 0, 12); // n = 0 132 size_t offset = 8; // first 8 bytes is reply tag used as AD 133 len -= 16; // poly1305 134 if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len - offset, buf, 8, m_Key, nonce, buf + offset, len - offset, false)) // decrypt 135 { 136 LogPrint (eLogWarning, "Garlic: Symmetric key tagset AEAD decryption failed"); 137 return false; 138 } 139 // we assume 1 I2NP block with delivery type local 140 if (offset + 3 > len) 141 { 142 LogPrint (eLogWarning, "Garlic: Symmetric key tagset is too short ", len); 143 return false; 144 } 145 if (buf[offset] != eECIESx25519BlkGalicClove) 146 { 147 LogPrint (eLogWarning, "Garlic: Symmetric key tagset unexpected block ", (int)buf[offset]); 148 return false; 149 } 150 offset++; 151 auto size = bufbe16toh (buf + offset); 152 offset += 2; 153 if (offset + size > len) 154 { 155 LogPrint (eLogWarning, "Garlic: Symmetric key tagset block is too long ", size); 156 return false; 157 } 158 if (m_Destination) 159 m_Destination->HandleECIESx25519GarlicClove (buf + offset, size); 160 return true; 161 } 162 ECIESX25519AEADRatchetSession(GarlicDestination * owner,bool attachLeaseSetNS)163 ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS): 164 GarlicRoutingSession (owner, true) 165 { 166 if (!attachLeaseSetNS) SetLeaseSetUpdateStatus (eLeaseSetUpToDate); 167 RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0; 168 } 169 ~ECIESX25519AEADRatchetSession()170 ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession () 171 { 172 } 173 CreateNonce(uint64_t seqn,uint8_t * nonce)174 void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce) 175 { 176 memset (nonce, 0, 4); 177 htole64buf (nonce + 4, seqn); 178 } 179 GenerateEphemeralKeysAndEncode(uint8_t * buf)180 bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf) 181 { 182 bool ineligible = false; 183 while (!ineligible) 184 { 185 m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); 186 ineligible = m_EphemeralKeys->IsElligatorIneligible (); 187 if (!ineligible) // we haven't tried it yet 188 { 189 if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys->GetPublicKey (), buf)) 190 return true; // success 191 // otherwise return back 192 m_EphemeralKeys->SetElligatorIneligible (); 193 i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys); 194 } 195 else 196 i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys); 197 } 198 // we still didn't find elligator eligible pair 199 for (int i = 0; i < 25; i++) 200 { 201 // create new 202 m_EphemeralKeys = std::make_shared<i2p::crypto::X25519Keys>(); 203 m_EphemeralKeys->GenerateKeys (); 204 if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys->GetPublicKey (), buf)) 205 return true; // success 206 else 207 { 208 // let NTCP2 use it 209 m_EphemeralKeys->SetElligatorIneligible (); 210 i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys); 211 } 212 } 213 LogPrint (eLogError, "Garlic: Can't generate elligator eligible x25519 keys"); 214 return false; 215 } 216 InitNewSessionTagset(std::shared_ptr<RatchetTagSet> tagsetNsr) const217 void ECIESX25519AEADRatchetSession::InitNewSessionTagset (std::shared_ptr<RatchetTagSet> tagsetNsr) const 218 { 219 uint8_t tagsetKey[32]; 220 i2p::crypto::HKDF (m_CK, nullptr, 0, "SessionReplyTags", tagsetKey, 32); // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32) 221 // Session Tag Ratchet 222 tagsetNsr->DHInitialize (m_CK, tagsetKey); // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey) 223 tagsetNsr->NextSessionTagRatchet (); 224 } 225 HandleNewIncomingSession(const uint8_t * buf,size_t len)226 bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len) 227 { 228 if (!GetOwner ()) return false; 229 // we are Bob 230 // KDF1 231 i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk 232 233 if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk)) 234 { 235 LogPrint (eLogError, "Garlic: Can't decode elligator"); 236 return false; 237 } 238 buf += 32; len -= 32; 239 MixHash (m_Aepk, 32); // h = SHA256(h || aepk) 240 241 uint8_t sharedSecret[32]; 242 if (!GetOwner ()->Decrypt (m_Aepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) 243 { 244 LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key"); 245 return false; 246 } 247 MixKey (sharedSecret); 248 249 // decrypt flags/static 250 uint8_t nonce[12], fs[32]; 251 CreateNonce (0, nonce); 252 if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, m_CK + 32, nonce, fs, 32, false)) // decrypt 253 { 254 LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed "); 255 return false; 256 } 257 MixHash (buf, 48); // h = SHA256(h || ciphertext) 258 buf += 48; len -= 48; // 32 data + 16 poly 259 260 // KDF2 for payload 261 bool isStatic = !i2p::data::Tag<32> (fs).IsZero (); 262 if (isStatic) 263 { 264 // static key, fs is apk 265 memcpy (m_RemoteStaticKey, fs, 32); 266 if (!GetOwner ()->Decrypt (fs, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, apk) 267 { 268 LogPrint (eLogWarning, "Garlic: Incorrect Alice static key"); 269 return false; 270 } 271 MixKey (sharedSecret); 272 } 273 else // all zeros flags 274 CreateNonce (1, nonce); 275 276 // decrypt payload 277 std::vector<uint8_t> payload (len - 16); // we must save original ciphertext 278 if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt 279 { 280 LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed"); 281 return false; 282 } 283 284 m_State = eSessionStateNewSessionReceived; 285 if (isStatic) 286 { 287 MixHash (buf, len); // h = SHA256(h || ciphertext) 288 GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); 289 } 290 HandlePayload (payload.data (), len - 16, nullptr, 0); 291 292 return true; 293 } 294 HandlePayload(const uint8_t * buf,size_t len,const std::shared_ptr<ReceiveRatchetTagSet> & receiveTagset,int index)295 void ECIESX25519AEADRatchetSession::HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index) 296 { 297 size_t offset = 0; 298 while (offset < len) 299 { 300 uint8_t blk = buf[offset]; 301 offset++; 302 auto size = bufbe16toh (buf + offset); 303 offset += 2; 304 LogPrint (eLogDebug, "Garlic: Block type ", (int)blk, " of size ", size); 305 if (size > len) 306 { 307 LogPrint (eLogError, "Garlic: Unexpected block length ", size); 308 break; 309 } 310 switch (blk) 311 { 312 case eECIESx25519BlkGalicClove: 313 if (GetOwner ()) 314 GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size); 315 break; 316 case eECIESx25519BlkNextKey: 317 LogPrint (eLogDebug, "Garlic: Next key"); 318 if (receiveTagset) 319 HandleNextKey (buf + offset, size, receiveTagset); 320 else 321 LogPrint (eLogError, "Garlic: Unexpected next key block"); 322 break; 323 case eECIESx25519BlkAck: 324 { 325 LogPrint (eLogDebug, "Garlic: Ack"); 326 int numAcks = size >> 2; // /4 327 auto offset1 = offset; 328 for (auto i = 0; i < numAcks; i++) 329 { 330 offset1 += 2; // tagsetid 331 MessageConfirmed (bufbe16toh (buf + offset1)); offset1 += 2; // N 332 } 333 break; 334 } 335 case eECIESx25519BlkAckRequest: 336 { 337 LogPrint (eLogDebug, "Garlic: Ack request"); 338 m_AckRequests.push_back ({receiveTagset->GetTagSetID (), index}); 339 break; 340 } 341 case eECIESx25519BlkTermination: 342 LogPrint (eLogDebug, "Garlic: Termination"); 343 if (GetOwner ()) 344 GetOwner ()->RemoveECIESx25519Session (m_RemoteStaticKey); 345 if (receiveTagset) receiveTagset->Expire (); 346 break; 347 case eECIESx25519BlkDateTime: 348 LogPrint (eLogDebug, "Garlic: Datetime"); 349 break; 350 case eECIESx25519BlkOptions: 351 LogPrint (eLogDebug, "Garlic: Options"); 352 break; 353 case eECIESx25519BlkPadding: 354 LogPrint (eLogDebug, "Garlic: Padding"); 355 break; 356 default: 357 LogPrint (eLogWarning, "Garlic: Unknown block type ", (int)blk); 358 } 359 offset += size; 360 } 361 } 362 HandleNextKey(const uint8_t * buf,size_t len,const std::shared_ptr<ReceiveRatchetTagSet> & receiveTagset)363 void ECIESX25519AEADRatchetSession::HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset) 364 { 365 uint8_t flag = buf[0]; buf++; // flag 366 if (flag & ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG) 367 { 368 if (!m_SendForwardKey || !m_NextSendRatchet) return; 369 uint16_t keyID = bufbe16toh (buf); buf += 2; // keyID 370 if (((!m_NextSendRatchet->newKey || !m_NextSendRatchet->keyID) && keyID == m_NextSendRatchet->keyID) || 371 (m_NextSendRatchet->newKey && keyID == m_NextSendRatchet->keyID -1)) 372 { 373 if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG) 374 memcpy (m_NextSendRatchet->remote, buf, 32); 375 uint8_t sharedSecret[32], tagsetKey[32]; 376 m_NextSendRatchet->key->Agree (m_NextSendRatchet->remote, sharedSecret); 377 i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32) 378 auto newTagset = std::make_shared<RatchetTagSet> (); 379 newTagset->SetTagSetID (1 + m_NextSendRatchet->keyID + keyID); 380 newTagset->DHInitialize (m_SendTagset->GetNextRootKey (), tagsetKey); 381 newTagset->NextSessionTagRatchet (); 382 m_SendTagset = newTagset; 383 m_SendForwardKey = false; 384 LogPrint (eLogDebug, "Garlic: Next send tagset ", newTagset->GetTagSetID (), " created"); 385 } 386 else 387 LogPrint (eLogDebug, "Garlic: Unexpected next key ", keyID); 388 } 389 else 390 { 391 uint16_t keyID = bufbe16toh (buf); buf += 2; // keyID 392 bool newKey = flag & ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; 393 m_SendReverseKey = true; 394 if (!m_NextReceiveRatchet) 395 m_NextReceiveRatchet.reset (new DHRatchet ()); 396 else 397 { 398 if (keyID == m_NextReceiveRatchet->keyID && newKey == m_NextReceiveRatchet->newKey) 399 { 400 LogPrint (eLogDebug, "Garlic: Duplicate ", newKey ? "new" : "old", " key ", keyID, " received"); 401 return; 402 } 403 m_NextReceiveRatchet->keyID = keyID; 404 } 405 int tagsetID = 2*keyID; 406 if (newKey) 407 { 408 m_NextReceiveRatchet->key = i2p::transport::transports.GetNextX25519KeysPair (); 409 m_NextReceiveRatchet->newKey = true; 410 tagsetID++; 411 } 412 else 413 m_NextReceiveRatchet->newKey = false; 414 if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG) 415 memcpy (m_NextReceiveRatchet->remote, buf, 32); 416 417 uint8_t sharedSecret[32], tagsetKey[32]; 418 m_NextReceiveRatchet->key->Agree (m_NextReceiveRatchet->remote, sharedSecret); 419 i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32) 420 auto newTagset = std::make_shared<ReceiveRatchetTagSet>(shared_from_this ()); 421 newTagset->SetTagSetID (tagsetID); 422 newTagset->DHInitialize (receiveTagset->GetNextRootKey (), tagsetKey); 423 newTagset->NextSessionTagRatchet (); 424 GenerateMoreReceiveTags (newTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ? 425 GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MAX_NUM_GENERATED_TAGS); 426 receiveTagset->Expire (); 427 LogPrint (eLogDebug, "Garlic: Next receive tagset ", tagsetID, " created"); 428 } 429 } 430 NewNextSendRatchet()431 void ECIESX25519AEADRatchetSession::NewNextSendRatchet () 432 { 433 if (m_NextSendRatchet) 434 { 435 if (!m_NextSendRatchet->newKey || !m_NextSendRatchet->keyID) 436 { 437 m_NextSendRatchet->keyID++; 438 m_NextSendRatchet->newKey = true; 439 } 440 else 441 m_NextSendRatchet->newKey = false; 442 } 443 else 444 m_NextSendRatchet.reset (new DHRatchet ()); 445 if (m_NextSendRatchet->newKey) 446 m_NextSendRatchet->key = i2p::transport::transports.GetNextX25519KeysPair (); 447 448 m_SendForwardKey = true; 449 LogPrint (eLogDebug, "Garlic: New send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", m_NextSendRatchet->keyID, " created"); 450 } 451 NewOutgoingSessionMessage(const uint8_t * payload,size_t len,uint8_t * out,size_t outLen,bool isStatic)452 bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic) 453 { 454 // we are Alice, bpk is m_RemoteStaticKey 455 size_t offset = 0; 456 if (!GenerateEphemeralKeysAndEncode (out + offset)) 457 { 458 LogPrint (eLogError, "Garlic: Can't encode elligator"); 459 return false; 460 } 461 offset += 32; 462 463 // KDF1 464 i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // bpk 465 MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk) 466 uint8_t sharedSecret[32]; 467 if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk) 468 { 469 LogPrint (eLogWarning, "Garlic: Incorrect Bob static key"); 470 return false; 471 } 472 MixKey (sharedSecret); 473 // encrypt flags/static key section 474 uint8_t nonce[12]; 475 CreateNonce (0, nonce); 476 const uint8_t * fs; 477 if (isStatic) 478 fs = GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); 479 else 480 { 481 memset (out + offset, 0, 32); // all zeros flags section 482 fs = out + offset; 483 } 484 if (!i2p::crypto::AEADChaCha20Poly1305 (fs, 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt 485 { 486 LogPrint (eLogWarning, "Garlic: Flags/static section AEAD encryption failed "); 487 return false; 488 } 489 490 MixHash (out + offset, 48); // h = SHA256(h || ciphertext) 491 offset += 48; 492 // KDF2 493 if (isStatic) 494 { 495 GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk) 496 MixKey (sharedSecret); 497 } 498 else 499 CreateNonce (1, nonce); 500 // encrypt payload 501 if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt 502 { 503 LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); 504 return false; 505 } 506 507 m_State = eSessionStateNewSessionSent; 508 if (isStatic) 509 { 510 MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext) 511 if (GetOwner ()) 512 { 513 auto tagsetNsr = std::make_shared<ReceiveRatchetTagSet>(shared_from_this (), true); 514 InitNewSessionTagset (tagsetNsr); 515 tagsetNsr->Expire (); // let non-replied session expire 516 GenerateMoreReceiveTags (tagsetNsr, ECIESX25519_NSR_NUM_GENERATED_TAGS); 517 } 518 } 519 return true; 520 } 521 NewSessionReplyMessage(const uint8_t * payload,size_t len,uint8_t * out,size_t outLen)522 bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) 523 { 524 // we are Bob 525 m_NSRSendTagset = std::make_shared<RatchetTagSet>(); 526 InitNewSessionTagset (m_NSRSendTagset); 527 uint64_t tag = m_NSRSendTagset->GetNextSessionTag (); 528 529 size_t offset = 0; 530 memcpy (out + offset, &tag, 8); 531 offset += 8; 532 if (!GenerateEphemeralKeysAndEncode (out + offset)) // bepk 533 { 534 LogPrint (eLogError, "Garlic: Can't encode elligator"); 535 return false; 536 } 537 memcpy (m_NSREncodedKey, out + offset, 32); // for possible next NSR 538 memcpy (m_NSRH, m_H, 32); 539 offset += 32; 540 // KDF for Reply Key Section 541 MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag) 542 MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk) 543 uint8_t sharedSecret[32]; 544 if (!m_EphemeralKeys->Agree (m_Aepk, sharedSecret)) // sharedSecret = x25519(besk, aepk) 545 { 546 LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key"); 547 return false; 548 } 549 MixKey (sharedSecret); 550 if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // sharedSecret = x25519(besk, apk) 551 { 552 LogPrint (eLogWarning, "Garlic: Incorrect Alice static key"); 553 return false; 554 } 555 MixKey (sharedSecret); 556 uint8_t nonce[12]; 557 CreateNonce (0, nonce); 558 // calculate hash for zero length 559 if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + offset, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) 560 { 561 LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed"); 562 return false; 563 } 564 MixHash (out + offset, 16); // h = SHA256(h || ciphertext) 565 offset += 16; 566 // KDF for payload 567 uint8_t keydata[64]; 568 i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) 569 // k_ab = keydata[0:31], k_ba = keydata[32:63] 570 auto receiveTagset = std::make_shared<ReceiveRatchetTagSet>(shared_from_this()); 571 receiveTagset->DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) 572 receiveTagset->NextSessionTagRatchet (); 573 m_SendTagset = std::make_shared<RatchetTagSet>(); 574 m_SendTagset->DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) 575 m_SendTagset->NextSessionTagRatchet (); 576 GenerateMoreReceiveTags (receiveTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ? 577 GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MIN_NUM_GENERATED_TAGS); 578 i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) 579 // encrypt payload 580 if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt 581 { 582 LogPrint (eLogWarning, "Garlic: NSR payload section AEAD encryption failed"); 583 return false; 584 } 585 m_State = eSessionStateNewSessionReplySent; 586 m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch (); 587 588 return true; 589 } 590 NextNewSessionReplyMessage(const uint8_t * payload,size_t len,uint8_t * out,size_t outLen)591 bool ECIESX25519AEADRatchetSession::NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) 592 { 593 // we are Bob and sent NSR already 594 uint64_t tag = m_NSRSendTagset->GetNextSessionTag (); // next tag 595 memcpy (out, &tag, 8); 596 memcpy (out + 8, m_NSREncodedKey, 32); 597 // recalculate h with new tag 598 memcpy (m_H, m_NSRH, 32); 599 MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag) 600 MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk) 601 uint8_t nonce[12]; 602 CreateNonce (0, nonce); 603 if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + 40, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) 604 { 605 LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed"); 606 return false; 607 } 608 MixHash (out + 40, 16); // h = SHA256(h || ciphertext) 609 // encrypt payload 610 if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + 56, len + 16, true)) // encrypt 611 { 612 LogPrint (eLogWarning, "Garlic: Next NSR payload section AEAD encryption failed"); 613 return false; 614 } 615 return true; 616 } 617 HandleNewOutgoingSessionReply(uint8_t * buf,size_t len)618 bool ECIESX25519AEADRatchetSession::HandleNewOutgoingSessionReply (uint8_t * buf, size_t len) 619 { 620 // we are Alice 621 LogPrint (eLogDebug, "Garlic: Reply received"); 622 const uint8_t * tag = buf; 623 buf += 8; len -= 8; // tag 624 uint8_t bepk[32]; // Bob's ephemeral key 625 if (!i2p::crypto::GetElligator ()->Decode (buf, bepk)) 626 { 627 LogPrint (eLogError, "Garlic: Can't decode elligator"); 628 return false; 629 } 630 buf += 32; len -= 32; 631 // KDF for Reply Key Section 632 i2p::util::SaveStateHelper<i2p::crypto::NoiseSymmetricState> s(GetNoiseState ()); // restore noise state on exit 633 MixHash (tag, 8); // h = SHA256(h || tag) 634 MixHash (bepk, 32); // h = SHA256(h || bepk) 635 uint8_t sharedSecret[32]; 636 if (!m_EphemeralKeys->Agree (bepk, sharedSecret)) // sharedSecret = x25519(aesk, bepk) 637 { 638 LogPrint (eLogWarning, "Garlic: Incorrect Bob ephemeral key"); 639 return false; 640 } 641 MixKey (sharedSecret); 642 GetOwner ()->Decrypt (bepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk) 643 MixKey (sharedSecret); 644 645 uint8_t nonce[12]; 646 CreateNonce (0, nonce); 647 // calculate hash for zero length 648 if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anything */, 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only 649 { 650 LogPrint (eLogWarning, "Garlic: Reply key section AEAD decryption failed"); 651 return false; 652 } 653 MixHash (buf, 16); // h = SHA256(h || ciphertext) 654 buf += 16; len -= 16; 655 // KDF for payload 656 uint8_t keydata[64]; 657 i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) 658 if (m_State == eSessionStateNewSessionSent) 659 { 660 // only first time, then we keep using existing tagsets 661 // k_ab = keydata[0:31], k_ba = keydata[32:63] 662 m_SendTagset = std::make_shared<RatchetTagSet>(); 663 m_SendTagset->DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) 664 m_SendTagset->NextSessionTagRatchet (); 665 auto receiveTagset = std::make_shared<ReceiveRatchetTagSet>(shared_from_this ()); 666 receiveTagset->DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) 667 receiveTagset->NextSessionTagRatchet (); 668 GenerateMoreReceiveTags (receiveTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ? 669 GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MIN_NUM_GENERATED_TAGS); 670 } 671 i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) 672 // decrypt payload 673 if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, buf, len - 16, false)) // decrypt 674 { 675 LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed"); 676 return false; 677 } 678 679 if (m_State == eSessionStateNewSessionSent) 680 { 681 m_State = eSessionStateEstablished; 682 //m_EphemeralKeys = nullptr; // TODO: delete after a while 683 m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch (); 684 GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); 685 } 686 HandlePayload (buf, len - 16, nullptr, 0); 687 688 // we have received reply to NS with LeaseSet in it 689 SetLeaseSetUpdateStatus (eLeaseSetUpToDate); 690 SetLeaseSetUpdateMsgID (0); 691 692 return true; 693 } 694 NewExistingSessionMessage(const uint8_t * payload,size_t len,uint8_t * out,size_t outLen)695 bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) 696 { 697 uint8_t nonce[12]; 698 auto index = m_SendTagset->GetNextIndex (); 699 CreateNonce (index, nonce); // tag's index 700 uint64_t tag = m_SendTagset->GetNextSessionTag (); 701 if (!tag) 702 { 703 LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset"); 704 if (GetOwner ()) 705 GetOwner ()->RemoveECIESx25519Session (m_RemoteStaticKey); 706 return false; 707 } 708 memcpy (out, &tag, 8); 709 // ad = The session tag, 8 bytes 710 // ciphertext = ENCRYPT(k, n, payload, ad) 711 uint8_t key[32]; 712 m_SendTagset->GetSymmKey (index, key); 713 if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, out, 8, key, nonce, out + 8, outLen - 8, true)) // encrypt 714 { 715 LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); 716 return false; 717 } 718 if (index >= ECIESX25519_TAGSET_MAX_NUM_TAGS && !m_SendForwardKey) 719 NewNextSendRatchet (); 720 return true; 721 } 722 HandleExistingSessionMessage(uint8_t * buf,size_t len,std::shared_ptr<ReceiveRatchetTagSet> receiveTagset,int index)723 bool ECIESX25519AEADRatchetSession::HandleExistingSessionMessage (uint8_t * buf, size_t len, 724 std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index) 725 { 726 uint8_t nonce[12]; 727 CreateNonce (index, nonce); // tag's index 728 len -= 8; // tag 729 uint8_t * payload = buf + 8; 730 uint8_t key[32]; 731 receiveTagset->GetSymmKey (index, key); 732 if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 16, buf, 8, key, nonce, payload, len - 16, false)) // decrypt 733 { 734 LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed"); 735 return false; 736 } 737 HandlePayload (payload, len - 16, receiveTagset, index); 738 if (GetOwner ()) 739 { 740 int moreTags = 0; 741 if (GetOwner ()->GetNumRatchetInboundTags () > 0) // override in settings? 742 { 743 if (receiveTagset->GetNextIndex () - index < GetOwner ()->GetNumRatchetInboundTags ()/2) 744 moreTags = GetOwner ()->GetNumRatchetInboundTags (); 745 index -= GetOwner ()->GetNumRatchetInboundTags (); // trim behind 746 } 747 else 748 { 749 moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4 750 if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS; 751 moreTags -= (receiveTagset->GetNextIndex () - index); 752 index -= ECIESX25519_MAX_NUM_GENERATED_TAGS; // trim behind 753 } 754 if (moreTags > 0) 755 GenerateMoreReceiveTags (receiveTagset, moreTags); 756 if (index > 0) 757 receiveTagset->SetTrimBehind (index); 758 } 759 return true; 760 } 761 HandleNextMessage(uint8_t * buf,size_t len,std::shared_ptr<ReceiveRatchetTagSet> receiveTagset,int index)762 bool ECIESX25519AEADRatchetSession::HandleNextMessage (uint8_t * buf, size_t len, 763 std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index) 764 { 765 m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); 766 switch (m_State) 767 { 768 case eSessionStateNewSessionReplySent: 769 m_State = eSessionStateEstablished; 770 m_NSRSendTagset = nullptr; 771 m_EphemeralKeys = nullptr; 772 #if (__cplusplus >= 201703L) // C++ 17 or higher 773 [[fallthrough]]; 774 #endif 775 case eSessionStateEstablished: 776 if (receiveTagset->IsNS ()) 777 { 778 // our of sequence NSR 779 LogPrint (eLogDebug, "Garlic: Check for out of order NSR with index ", index); 780 if (receiveTagset->GetNextIndex () - index < ECIESX25519_NSR_NUM_GENERATED_TAGS/2) 781 GenerateMoreReceiveTags (receiveTagset, ECIESX25519_NSR_NUM_GENERATED_TAGS); 782 return HandleNewOutgoingSessionReply (buf, len); 783 } 784 else 785 return HandleExistingSessionMessage (buf, len, receiveTagset, index); 786 case eSessionStateNew: 787 return HandleNewIncomingSession (buf, len); 788 case eSessionStateNewSessionSent: 789 return HandleNewOutgoingSessionReply (buf, len); 790 default: 791 return false; 792 } 793 return true; 794 } 795 WrapSingleMessage(std::shared_ptr<const I2NPMessage> msg)796 std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) 797 { 798 uint8_t * payload = GetOwner ()->GetPayloadBuffer (); 799 if (!payload) return nullptr; 800 size_t len = CreatePayload (msg, m_State != eSessionStateEstablished, payload); 801 if (!len) return nullptr; 802 auto m = NewI2NPMessage (len + 100); // 96 + 4 803 m->Align (12); // in order to get buf aligned to 16 (12 + 4) 804 uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length 805 806 switch (m_State) 807 { 808 case eSessionStateEstablished: 809 if (!NewExistingSessionMessage (payload, len, buf, m->maxLen)) 810 return nullptr; 811 len += 24; 812 break; 813 case eSessionStateNew: 814 if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen)) 815 return nullptr; 816 len += 96; 817 break; 818 case eSessionStateNewSessionReceived: 819 if (!NewSessionReplyMessage (payload, len, buf, m->maxLen)) 820 return nullptr; 821 len += 72; 822 break; 823 case eSessionStateNewSessionReplySent: 824 if (!NextNewSessionReplyMessage (payload, len, buf, m->maxLen)) 825 return nullptr; 826 len += 72; 827 break; 828 case eSessionStateOneTime: 829 if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false)) 830 return nullptr; 831 len += 96; 832 break; 833 default: 834 return nullptr; 835 } 836 837 htobe32buf (m->GetPayload (), len); 838 m->len += len + 4; 839 m->FillI2NPMessageHeader (eI2NPGarlic); 840 return m; 841 } 842 WrapOneTimeMessage(std::shared_ptr<const I2NPMessage> msg)843 std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg) 844 { 845 m_State = eSessionStateOneTime; 846 return WrapSingleMessage (msg); 847 } 848 CreatePayload(std::shared_ptr<const I2NPMessage> msg,bool first,uint8_t * payload)849 size_t ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first, uint8_t * payload) 850 { 851 uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); 852 size_t payloadLen = 0; 853 if (first) payloadLen += 7;// datatime 854 if (msg) 855 { 856 payloadLen += msg->GetPayloadLength () + 13; 857 if (m_Destination) payloadLen += 32; 858 } 859 if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT) 860 { 861 // resubmit non-confirmed LeaseSet 862 SetLeaseSetUpdateStatus (eLeaseSetUpdated); 863 SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed 864 } 865 auto leaseSet = (GetLeaseSetUpdateStatus () == eLeaseSetUpdated) ? GetOwner ()->GetLeaseSet () : nullptr; 866 if (leaseSet) 867 { 868 payloadLen += leaseSet->GetBufferLen () + DATABASE_STORE_HEADER_SIZE + 13; 869 if (!first) 870 { 871 // ack request 872 SetLeaseSetUpdateStatus (eLeaseSetSubmitted); 873 SetLeaseSetUpdateMsgID (m_SendTagset->GetNextIndex ()); 874 SetLeaseSetSubmissionTime (ts); 875 payloadLen += 4; 876 } 877 } 878 if (m_AckRequests.size () > 0) 879 payloadLen += m_AckRequests.size ()*4 + 3; 880 if (m_SendReverseKey) 881 { 882 payloadLen += 6; 883 if (m_NextReceiveRatchet->newKey) payloadLen += 32; 884 } 885 if (m_SendForwardKey) 886 { 887 payloadLen += 6; 888 if (m_NextSendRatchet->newKey) payloadLen += 32; 889 } 890 uint8_t paddingSize = 0; 891 if (payloadLen || ts > m_LastSentTimestamp + ECIESX25519_SEND_INACTIVITY_TIMEOUT) 892 { 893 int delta = (int)ECIESX25519_OPTIMAL_PAYLOAD_SIZE - (int)payloadLen; 894 if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size 895 { 896 paddingSize = m_PaddingSizes[m_NextPaddingSize++] & 0x0F; // 0 - 15 897 if (m_NextPaddingSize >= 32) 898 { 899 RAND_bytes (m_PaddingSizes, 32); 900 m_NextPaddingSize = 0; 901 } 902 if (delta > 3) 903 { 904 delta -= 3; 905 if (paddingSize >= delta) paddingSize %= delta; 906 } 907 paddingSize++; 908 payloadLen += paddingSize + 3; 909 } 910 } 911 if (payloadLen) 912 { 913 if (payloadLen > I2NP_MAX_MESSAGE_SIZE) 914 { 915 LogPrint (eLogError, "Garlic: Payload length ", payloadLen, " is too long"); 916 return 0; 917 } 918 m_LastSentTimestamp = ts; 919 size_t offset = 0; 920 // DateTime 921 if (first) 922 { 923 payload[offset] = eECIESx25519BlkDateTime; offset++; 924 htobe16buf (payload + offset, 4); offset += 2; 925 htobe32buf (payload + offset, ts/1000); offset += 4; // in seconds 926 } 927 // LeaseSet 928 if (leaseSet) 929 { 930 offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset); 931 if (!first) 932 { 933 // ack request 934 payload[offset] = eECIESx25519BlkAckRequest; offset++; 935 htobe16buf (payload + offset, 1); offset += 2; 936 payload[offset] = 0; offset++; // flags 937 } 938 } 939 // msg 940 if (msg) 941 offset += CreateGarlicClove (msg, payload + offset, payloadLen - offset); 942 // ack 943 if (m_AckRequests.size () > 0) 944 { 945 payload[offset] = eECIESx25519BlkAck; offset++; 946 htobe16buf (payload + offset, m_AckRequests.size () * 4); offset += 2; 947 for (auto& it: m_AckRequests) 948 { 949 htobe16buf (payload + offset, it.first); offset += 2; 950 htobe16buf (payload + offset, it.second); offset += 2; 951 } 952 m_AckRequests.clear (); 953 } 954 // next keys 955 if (m_SendReverseKey) 956 { 957 payload[offset] = eECIESx25519BlkNextKey; offset++; 958 htobe16buf (payload + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2; 959 payload[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG; 960 int keyID = m_NextReceiveRatchet->keyID - 1; 961 if (m_NextReceiveRatchet->newKey) 962 { 963 payload[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG; 964 keyID++; 965 } 966 offset++; // flag 967 htobe16buf (payload + offset, keyID); offset += 2; // keyid 968 if (m_NextReceiveRatchet->newKey) 969 { 970 memcpy (payload + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32); 971 offset += 32; // public key 972 } 973 m_SendReverseKey = false; 974 } 975 if (m_SendForwardKey) 976 { 977 payload[offset] = eECIESx25519BlkNextKey; offset++; 978 htobe16buf (payload + offset, m_NextSendRatchet->newKey ? 35 : 3); offset += 2; 979 payload[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; 980 if (!m_NextSendRatchet->keyID) payload[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only 981 offset++; // flag 982 htobe16buf (payload + offset, m_NextSendRatchet->keyID); offset += 2; // keyid 983 if (m_NextSendRatchet->newKey) 984 { 985 memcpy (payload + offset, m_NextSendRatchet->key->GetPublicKey (), 32); 986 offset += 32; // public key 987 } 988 } 989 // padding 990 if (paddingSize) 991 { 992 payload[offset] = eECIESx25519BlkPadding; offset++; 993 htobe16buf (payload + offset, paddingSize); offset += 2; 994 memset (payload + offset, 0, paddingSize); offset += paddingSize; 995 } 996 } 997 return payloadLen; 998 } 999 CreateGarlicClove(std::shared_ptr<const I2NPMessage> msg,uint8_t * buf,size_t len)1000 size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len) 1001 { 1002 if (!msg) return 0; 1003 uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1; 1004 if (m_Destination) cloveSize += 32; 1005 if ((int)len < cloveSize + 3) return 0; 1006 buf[0] = eECIESx25519BlkGalicClove; // clove type 1007 htobe16buf (buf + 1, cloveSize); // size 1008 buf += 3; 1009 if (m_Destination) 1010 { 1011 *buf = (eGarlicDeliveryTypeDestination << 5); 1012 memcpy (buf + 1, *m_Destination, 32); buf += 32; 1013 } 1014 else 1015 *buf = 0; 1016 buf++; // flag and delivery instructions 1017 *buf = msg->GetTypeID (); // I2NP msg type 1018 htobe32buf (buf + 1, msg->GetMsgID ()); // msgID 1019 htobe32buf (buf + 5, msg->GetExpiration () / 1000); // expiration in seconds 1020 memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ()); 1021 return cloveSize + 3; 1022 } 1023 CreateLeaseSetClove(std::shared_ptr<const i2p::data::LocalLeaseSet> ls,uint64_t ts,uint8_t * buf,size_t len)1024 size_t ECIESX25519AEADRatchetSession::CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len) 1025 { 1026 if (!ls || ls->GetStoreType () != i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2) 1027 { 1028 LogPrint (eLogError, "Garlic: Incorrect LeasetSet type to send"); 1029 return 0; 1030 } 1031 uint16_t cloveSize = 1 + 9 + DATABASE_STORE_HEADER_SIZE + ls->GetBufferLen (); // to local 1032 if ((int)len < cloveSize + 3) return 0; 1033 buf[0] = eECIESx25519BlkGalicClove; // clove type 1034 htobe16buf (buf + 1, cloveSize); // size 1035 buf += 3; 1036 *buf = 0; buf++; // flag and delivery instructions 1037 *buf = eI2NPDatabaseStore; buf++; // I2NP msg type 1038 RAND_bytes (buf, 4); buf += 4; // msgID 1039 htobe32buf (buf, (ts + I2NP_MESSAGE_EXPIRATION_TIMEOUT)/1000); buf += 4; // expiration 1040 // payload 1041 memcpy (buf + DATABASE_STORE_KEY_OFFSET, ls->GetStoreHash (), 32); 1042 buf[DATABASE_STORE_TYPE_OFFSET] = i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2; 1043 memset (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0, 4); // replyToken = 0 1044 buf += DATABASE_STORE_HEADER_SIZE; 1045 memcpy (buf, ls->GetBuffer (), ls->GetBufferLen ()); 1046 1047 return cloveSize + 3; 1048 } 1049 GenerateMoreReceiveTags(std::shared_ptr<ReceiveRatchetTagSet> receiveTagset,int numTags)1050 void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags (std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int numTags) 1051 { 1052 if (GetOwner ()) 1053 { 1054 for (int i = 0; i < numTags; i++) 1055 { 1056 auto tag = GetOwner ()->AddECIESx25519SessionNextTag (receiveTagset); 1057 if (!tag) 1058 { 1059 LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for receive tagset"); 1060 break; 1061 } 1062 } 1063 } 1064 } 1065 CheckExpired(uint64_t ts)1066 bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts) 1067 { 1068 CleanupUnconfirmedLeaseSet (ts); 1069 return ts > m_LastActivityTimestamp + ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT && // seconds 1070 ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds 1071 } 1072 RouterIncomingRatchetSession(const i2p::crypto::NoiseSymmetricState & initState)1073 RouterIncomingRatchetSession::RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState): 1074 ECIESX25519AEADRatchetSession (&i2p::context, false) 1075 { 1076 SetLeaseSetUpdateStatus (eLeaseSetDoNotSend); 1077 SetNoiseState (initState); 1078 } 1079 HandleNextMessage(const uint8_t * buf,size_t len)1080 bool RouterIncomingRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len) 1081 { 1082 if (!GetOwner ()) return false; 1083 m_CurrentNoiseState = GetNoiseState (); 1084 // we are Bob 1085 m_CurrentNoiseState.MixHash (buf, 32); 1086 uint8_t sharedSecret[32]; 1087 if (!GetOwner ()->Decrypt (buf, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) 1088 { 1089 LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key"); 1090 return false; 1091 } 1092 m_CurrentNoiseState.MixKey (sharedSecret); 1093 buf += 32; len -= 32; 1094 uint8_t nonce[12]; 1095 CreateNonce (0, nonce); 1096 std::vector<uint8_t> payload (len - 16); 1097 if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_CurrentNoiseState.m_H, 32, 1098 m_CurrentNoiseState.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt 1099 { 1100 LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed"); 1101 return false; 1102 } 1103 HandlePayload (payload.data (), len - 16, nullptr, 0); 1104 return true; 1105 } 1106 CreateGarlicPayload(std::shared_ptr<const I2NPMessage> msg,uint8_t * payload,bool datetime,size_t optimalSize)1107 static size_t CreateGarlicPayload (std::shared_ptr<const I2NPMessage> msg, uint8_t * payload, 1108 bool datetime, size_t optimalSize) 1109 { 1110 size_t len = 0; 1111 if (datetime) 1112 { 1113 // DateTime 1114 payload[0] = eECIESx25519BlkDateTime; 1115 htobe16buf (payload + 1, 4); 1116 htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); 1117 len = 7; 1118 } 1119 // I2NP 1120 payload += len; 1121 uint16_t cloveSize = msg->GetPayloadLength () + 10; 1122 payload[0] = eECIESx25519BlkGalicClove; // clove type 1123 htobe16buf (payload + 1, cloveSize); // size 1124 payload += 3; 1125 payload[0] = 0; // flag and delivery instructions 1126 payload[1] = msg->GetTypeID (); // I2NP msg type 1127 htobe32buf (payload + 2, msg->GetMsgID ()); // msgID 1128 htobe32buf (payload + 6, msg->GetExpiration () / 1000); // expiration in seconds 1129 memcpy (payload + 10, msg->GetPayload (), msg->GetPayloadLength ()); 1130 len += cloveSize + 3; 1131 payload += cloveSize; 1132 // padding 1133 int delta = (int)optimalSize - (int)len; 1134 if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size 1135 { 1136 uint8_t paddingSize = rand () & 0x0F; // 0 - 15 1137 if (delta > 3) 1138 { 1139 delta -= 3; 1140 if (paddingSize > delta) paddingSize %= delta; 1141 } 1142 payload[0] = eECIESx25519BlkPadding; 1143 htobe16buf (payload + 1, paddingSize); 1144 if (paddingSize) memset (payload + 3, 0, paddingSize); 1145 len += paddingSize + 3; 1146 } 1147 return len; 1148 } 1149 WrapECIESX25519Message(std::shared_ptr<const I2NPMessage> msg,const uint8_t * key,uint64_t tag)1150 std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag) 1151 { 1152 auto m = NewI2NPMessage (); 1153 m->Align (12); // in order to get buf aligned to 16 (12 + 4) 1154 uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length 1155 size_t offset = 0; 1156 memcpy (buf + offset, &tag, 8); offset += 8; 1157 auto payload = buf + offset; 1158 size_t len = CreateGarlicPayload (msg, payload, false, 956); // 1003 - 8 tag - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 3 local tunnel delivery 1159 uint8_t nonce[12]; 1160 memset (nonce, 0, 12); // n = 0 1161 if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, buf, 8, key, nonce, payload, len + 16, true)) // encrypt 1162 { 1163 LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); 1164 return nullptr; 1165 } 1166 offset += len + 16; 1167 htobe32buf (m->GetPayload (), offset); 1168 m->len += offset + 4; 1169 m->FillI2NPMessageHeader (eI2NPGarlic); 1170 return m; 1171 } 1172 WrapECIESX25519MessageForRouter(std::shared_ptr<const I2NPMessage> msg,const uint8_t * routerPublicKey)1173 std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<const I2NPMessage> msg, const uint8_t * routerPublicKey) 1174 { 1175 // Noise_N, we are Alice, routerPublicKey is Bob's 1176 i2p::crypto::NoiseSymmetricState noiseState; 1177 i2p::crypto::InitNoiseNState (noiseState, routerPublicKey); 1178 auto m = NewI2NPMessage (); 1179 m->Align (12); // in order to get buf aligned to 16 (12 + 4) 1180 uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length 1181 size_t offset = 0; 1182 auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); 1183 memcpy (buf + offset, ephemeralKeys->GetPublicKey (), 32); 1184 noiseState.MixHash (buf + offset, 32); // h = SHA256(h || aepk) 1185 offset += 32; 1186 uint8_t sharedSecret[32]; 1187 if (!ephemeralKeys->Agree (routerPublicKey, sharedSecret)) // x25519(aesk, bpk) 1188 { 1189 LogPrint (eLogWarning, "Garlic: Incorrect Bob static key"); 1190 return nullptr; 1191 } 1192 noiseState.MixKey (sharedSecret); 1193 auto payload = buf + offset; 1194 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 1195 uint8_t nonce[12]; 1196 memset (nonce, 0, 12); 1197 // encrypt payload 1198 if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, noiseState.m_H, 32, noiseState.m_CK + 32, nonce, payload, len + 16, true)) // encrypt 1199 { 1200 LogPrint (eLogWarning, "Garlic: Payload for router AEAD encryption failed"); 1201 return nullptr; 1202 } 1203 offset += len + 16; 1204 htobe32buf (m->GetPayload (), offset); 1205 m->len += offset + 4; 1206 m->FillI2NPMessageHeader (eI2NPGarlic); 1207 return m; 1208 } 1209 } 1210 } 1211