Garlic.cpp
1 /* 2 * Copyright (c) 2013-2025, 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 <inttypes.h> 10 #include "I2PEndian.h" 11 #include <map> 12 #include <string> 13 #include "Crypto.h" 14 #include "RouterContext.h" 15 #include "I2NPProtocol.h" 16 #include "Tunnel.h" 17 #include "TunnelPool.h" 18 #include "Transports.h" 19 #include "Timestamp.h" 20 #include "Log.h" 21 #include "FS.h" 22 #include "ECIESX25519AEADRatchetSession.h" 23 #include "Garlic.h" 24 25 namespace i2p 26 { 27 namespace garlic 28 { 29 GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet): 30 m_Owner (owner), m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend), 31 m_LeaseSetUpdateMsgID (0), m_IsWithJava (false), m_NumSentPackets (0) 32 { 33 } 34 35 GarlicRoutingSession::GarlicRoutingSession (): 36 m_Owner (nullptr), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0) 37 { 38 } 39 40 GarlicRoutingSession::~GarlicRoutingSession () 41 { 42 } 43 44 std::shared_ptr<GarlicRoutingPath> GarlicRoutingSession::GetSharedRoutingPath () 45 { 46 if (!m_SharedRoutingPath) return nullptr; 47 uint32_t ts = i2p::util::GetSecondsSinceEpoch (); 48 if (!m_SharedRoutingPath->outboundTunnel->IsEstablished () || 49 ts*1000LL > m_SharedRoutingPath->remoteLease->endDate || 50 ts > m_SharedRoutingPath->updateTime + ROUTING_PATH_EXPIRATION_TIMEOUT) 51 m_SharedRoutingPath = nullptr; 52 return m_SharedRoutingPath; 53 } 54 55 void GarlicRoutingSession::SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path) 56 { 57 if (path && path->outboundTunnel && path->remoteLease) 58 path->updateTime = i2p::util::GetSecondsSinceEpoch (); 59 else 60 path = nullptr; 61 m_SharedRoutingPath = path; 62 } 63 64 bool GarlicRoutingSession::MessageConfirmed (uint32_t msgID) 65 { 66 if (msgID == GetLeaseSetUpdateMsgID ()) 67 { 68 SetLeaseSetUpdateStatus (eLeaseSetUpToDate); 69 SetLeaseSetUpdateMsgID (0); 70 LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed"); 71 return true; 72 } 73 return false; 74 } 75 76 void GarlicRoutingSession::CleanupUnconfirmedLeaseSet (uint64_t ts) 77 { 78 if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASESET_CONFIRMATION_TIMEOUT) 79 { 80 if (GetOwner ()) 81 GetOwner ()->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); 82 m_LeaseSetUpdateMsgID = 0; 83 } 84 } 85 86 std::shared_ptr<I2NPMessage> GarlicRoutingSession::CreateEncryptedDeliveryStatusMsg (uint32_t msgID) 87 { 88 auto msg = CreateDeliveryStatusMsg (msgID); 89 if (GetOwner ()) 90 { 91 //encrypt 92 uint8_t key[32], tag[32]; 93 RAND_bytes (key, 32); // random session key 94 RAND_bytes (tag, 32); // random session tag 95 GetOwner ()->SubmitSessionKey (key, tag); 96 ElGamalAESSession garlic (key, tag); 97 msg = garlic.WrapSingleMessage (msg); 98 } 99 return msg; 100 } 101 102 std::vector<std::shared_ptr<I2NPMessage> > GarlicRoutingSession::WrapMultipleMessages (const std::vector<std::shared_ptr<const I2NPMessage> >& msgs) 103 { 104 std::vector<std::shared_ptr<I2NPMessage> > ret; 105 for (auto& msg: msgs) 106 ret.push_back (WrapSingleMessage(msg)); 107 return ret; 108 } 109 110 ElGamalAESSession::ElGamalAESSession (GarlicDestination * owner, 111 std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet): 112 GarlicRoutingSession (owner, attachLeaseSet), 113 m_Destination (destination), m_NumTags (numTags) 114 { 115 // create new session tags and session key 116 RAND_bytes (m_SessionKey, 32); 117 m_Encryption.SetKey (m_SessionKey); 118 } 119 120 ElGamalAESSession::ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag): 121 m_NumTags(1) 122 { 123 memcpy (m_SessionKey, sessionKey, 32); 124 m_Encryption.SetKey (m_SessionKey); 125 m_SessionTags.push_back (sessionTag); 126 m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch (); 127 } 128 129 std::shared_ptr<I2NPMessage> ElGamalAESSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) 130 { 131 auto m = NewI2NPMessage (); 132 m->Align (12); // in order to get buf aligned to 16 (12 + 4) 133 size_t len = 0; 134 uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length 135 136 // find non-expired tag 137 bool tagFound = false; 138 SessionTag tag; 139 if (m_NumTags > 0) 140 { 141 uint32_t ts = i2p::util::GetSecondsSinceEpoch (); 142 while (!m_SessionTags.empty ()) 143 { 144 if (ts < m_SessionTags.front ().creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) 145 { 146 tag = m_SessionTags.front (); 147 m_SessionTags.pop_front (); // use same tag only once 148 tagFound = true; 149 break; 150 } 151 else 152 m_SessionTags.pop_front (); // remove expired tag 153 } 154 } 155 // create message 156 if (!tagFound) // new session 157 { 158 LogPrint (eLogInfo, "Garlic: No tags available, will use ElGamal"); 159 if (!m_Destination) 160 { 161 LogPrint (eLogError, "Garlic: Can't use ElGamal for unknown destination"); 162 return nullptr; 163 } 164 // create ElGamal block 165 ElGamalBlock elGamal; 166 memcpy (elGamal.sessionKey, m_SessionKey, 32); 167 RAND_bytes (elGamal.preIV, 32); // Pre-IV 168 uint8_t iv[32]; // IV is first 16 bytes 169 SHA256(elGamal.preIV, 32, iv); 170 m_Destination->Encrypt ((uint8_t *)&elGamal, buf); 171 m_IV = iv; 172 buf += 514; 173 len += 514; 174 } 175 else // existing session 176 { 177 // session tag 178 memcpy (buf, tag, 32); 179 uint8_t iv[32]; // IV is first 16 bytes 180 SHA256(tag, 32, iv); 181 m_IV = iv; 182 buf += 32; 183 len += 32; 184 } 185 // AES block 186 len += CreateAESBlock (buf, msg); 187 htobe32buf (m->GetPayload (), len); 188 m->len += len + 4; 189 m->FillI2NPMessageHeader (eI2NPGarlic); 190 return m; 191 } 192 193 size_t ElGamalAESSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg) 194 { 195 size_t blockSize = 0; 196 bool createNewTags = GetOwner () && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3); 197 UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr; 198 htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count 199 blockSize += 2; 200 if (newTags) // session tags recreated 201 { 202 for (int i = 0; i < newTags->numTags; i++) 203 { 204 memcpy (buf + blockSize, newTags->sessionTags[i], 32); // tags 205 blockSize += 32; 206 } 207 } 208 uint32_t * payloadSize = (uint32_t *)(buf + blockSize); 209 blockSize += 4; 210 uint8_t * payloadHash = buf + blockSize; 211 blockSize += 32; 212 buf[blockSize] = 0; // flag 213 blockSize++; 214 size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags); 215 htobe32buf (payloadSize, len); 216 SHA256(buf + blockSize, len, payloadHash); 217 blockSize += len; 218 size_t rem = blockSize % 16; 219 if (rem) 220 blockSize += (16-rem); //padding 221 m_Encryption.Encrypt(buf, blockSize, m_IV, buf); 222 return blockSize; 223 } 224 225 size_t ElGamalAESSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags) 226 { 227 uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); 228 uint32_t msgID; 229 RAND_bytes ((uint8_t *)&msgID, 4); 230 size_t size = 0; 231 uint8_t * numCloves = payload + size; 232 *numCloves = 0; 233 size++; 234 235 if (GetOwner ()) 236 { 237 // resubmit non-confirmed LeaseSet 238 if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASESET_CONFIRMATION_TIMEOUT) 239 { 240 SetLeaseSetUpdateStatus (eLeaseSetUpdated); 241 SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed 242 } 243 244 // attach DeviveryStatus if necessary 245 if (newTags || GetLeaseSetUpdateStatus () == eLeaseSetUpdated) // new tags created or leaseset updated 246 { 247 // clove is DeliveryStatus 248 auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID); 249 if (cloveSize > 0) // successive? 250 { 251 size += cloveSize; 252 (*numCloves)++; 253 if (newTags) // new tags created 254 { 255 newTags->msgID = msgID; 256 m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr<UnconfirmedTags>(newTags))); 257 newTags = nullptr; // got acquired 258 } 259 GetOwner ()->DeliveryStatusSent (shared_from_this (), msgID); 260 } 261 else 262 LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created"); 263 } 264 // attach LeaseSet 265 if (GetLeaseSetUpdateStatus () == eLeaseSetUpdated) 266 { 267 if (GetLeaseSetUpdateMsgID ()) GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ()); // remove previous 268 SetLeaseSetUpdateStatus (eLeaseSetSubmitted); 269 SetLeaseSetUpdateMsgID (msgID); 270 SetLeaseSetSubmissionTime (ts); 271 // clove if our leaseSet must be attached 272 auto leaseSet = CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ()); 273 size += CreateGarlicClove (payload + size, leaseSet, false); 274 (*numCloves)++; 275 } 276 } 277 if (msg) // clove message itself if presented 278 { 279 size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false); 280 (*numCloves)++; 281 } 282 memset (payload + size, 0, 3); // certificate of message 283 size += 3; 284 htobe32buf (payload + size, msgID); // MessageID 285 size += 4; 286 htobe64buf (payload + size, ts + 8000); // Expiration of message, 8 sec 287 size += 8; 288 289 if (newTags) delete newTags; // not acquired, delete 290 return size; 291 } 292 293 size_t ElGamalAESSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination) 294 { 295 uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec 296 size_t size = 0; 297 if (isDestination) 298 { 299 buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination 300 size++; 301 memcpy (buf + size, m_Destination->GetIdentHash (), 32); 302 size += 32; 303 } 304 else 305 { 306 buf[size] = 0;// delivery instructions flag local 307 size++; 308 } 309 310 memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); 311 size += msg->GetLength (); 312 uint32_t cloveID; 313 RAND_bytes ((uint8_t *)&cloveID, 4); 314 htobe32buf (buf + size, cloveID); // CloveID 315 size += 4; 316 htobe64buf (buf + size, ts); // Expiration of clove 317 size += 8; 318 memset (buf + size, 0, 3); // certificate of clove 319 size += 3; 320 return size; 321 } 322 323 size_t ElGamalAESSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID) 324 { 325 size_t size = 0; 326 if (GetOwner ()) 327 { 328 auto inboundTunnel = GetOwner ()->GetTunnelPool ()->GetNextInboundTunnel (); 329 if (inboundTunnel) 330 { 331 buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel 332 size++; 333 // hash and tunnelID sequence is reversed for Garlic 334 memcpy (buf + size, inboundTunnel->GetNextIdentHash (), 32); // To Hash 335 size += 32; 336 htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID 337 size += 4; 338 // create msg 339 auto msg = CreateEncryptedDeliveryStatusMsg (msgID); 340 if (msg) 341 { 342 memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); 343 size += msg->GetLength (); 344 } 345 // fill clove 346 uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec 347 uint32_t cloveID; 348 RAND_bytes ((uint8_t *)&cloveID, 4); 349 htobe32buf (buf + size, cloveID); // CloveID 350 size += 4; 351 htobe64buf (buf + size, ts); // Expiration of clove 352 size += 8; 353 memset (buf + size, 0, 3); // certificate of clove 354 size += 3; 355 } 356 else 357 LogPrint (eLogError, "Garlic: No inbound tunnels in the pool for DeliveryStatus"); 358 } 359 else 360 LogPrint (eLogWarning, "Garlic: Missing local LeaseSet"); 361 362 return size; 363 } 364 365 ElGamalAESSession::UnconfirmedTags * ElGamalAESSession::GenerateSessionTags () 366 { 367 auto tags = new UnconfirmedTags (m_NumTags); 368 tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch (); 369 for (int i = 0; i < m_NumTags; i++) 370 { 371 RAND_bytes (tags->sessionTags[i], 32); 372 tags->sessionTags[i].creationTime = tags->tagsCreationTime; 373 } 374 return tags; 375 } 376 377 bool ElGamalAESSession::MessageConfirmed (uint32_t msgID) 378 { 379 TagsConfirmed (msgID); 380 if (!GarlicRoutingSession::MessageConfirmed (msgID)) 381 CleanupExpiredTags (); 382 return true; 383 } 384 385 void ElGamalAESSession::TagsConfirmed (uint32_t msgID) 386 { 387 uint32_t ts = i2p::util::GetSecondsSinceEpoch (); 388 auto it = m_UnconfirmedTagsMsgs.find (msgID); 389 if (it != m_UnconfirmedTagsMsgs.end ()) 390 { 391 auto& tags = it->second; 392 if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) 393 { 394 for (int i = 0; i < tags->numTags; i++) 395 m_SessionTags.push_back (tags->sessionTags[i]); 396 } 397 m_UnconfirmedTagsMsgs.erase (it); 398 } 399 } 400 401 bool ElGamalAESSession::CleanupExpiredTags () 402 { 403 auto ts = i2p::util::GetSecondsSinceEpoch (); 404 for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();) 405 { 406 if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) 407 it = m_SessionTags.erase (it); 408 else 409 ++it; 410 } 411 CleanupUnconfirmedTags (); 412 CleanupUnconfirmedLeaseSet (ts); 413 return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty (); 414 } 415 416 bool ElGamalAESSession::CleanupUnconfirmedTags () 417 { 418 bool ret = false; 419 uint32_t ts = i2p::util::GetSecondsSinceEpoch (); 420 // delete expired unconfirmed tags 421 for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();) 422 { 423 if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT) 424 { 425 if (GetOwner ()) 426 GetOwner ()->RemoveDeliveryStatusSession (it->first); 427 it = m_UnconfirmedTagsMsgs.erase (it); 428 ret = true; 429 } 430 else 431 ++it; 432 } 433 return ret; 434 } 435 436 GarlicDestination::GarlicDestination (): 437 m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL), 438 m_NumTags (32), // 32 tags by default 439 m_PayloadBuffer (nullptr), m_LastIncomingSessionTimestamp (0), 440 m_NumRatchetInboundTags (0) // 0 means standard 441 { 442 } 443 444 GarlicDestination::~GarlicDestination () 445 { 446 if (m_PayloadBuffer) 447 delete[] m_PayloadBuffer; 448 } 449 450 void GarlicDestination::CleanUp () 451 { 452 for (auto it: m_Sessions) 453 it.second->SetOwner (nullptr); 454 m_Sessions.clear (); 455 m_DeliveryStatusSessions.clear (); 456 m_Tags.clear (); 457 for (auto it: m_ECIESx25519Sessions) 458 { 459 it.second->Terminate (); 460 it.second->SetOwner (nullptr); 461 } 462 m_ECIESx25519Sessions.clear (); 463 m_ECIESx25519Tags.clear (); 464 } 465 void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag) 466 { 467 if (key) 468 { 469 uint32_t ts = i2p::util::GetSecondsSinceEpoch (); 470 m_Tags[SessionTag(tag, ts)] = std::make_shared<AESDecryption>(key); 471 } 472 } 473 474 void GarlicDestination::AddECIESx25519Key (const uint8_t * key, const uint8_t * tag) 475 { 476 uint64_t t; 477 memcpy (&t, tag, 8); 478 AddECIESx25519Key (key, t); 479 } 480 481 void GarlicDestination::AddECIESx25519Key (const uint8_t * key, uint64_t tag) 482 { 483 auto tagset = std::make_shared<SymmetricKeyTagSet>(this, key); 484 m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{0, tagset}); 485 } 486 487 bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) 488 { 489 AddSessionKey (key, tag); 490 return true; 491 } 492 493 void GarlicDestination::SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) 494 { 495 AddECIESx25519Key (key, tag); 496 } 497 498 void GarlicDestination::HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg) 499 { 500 uint8_t * buf = msg->GetPayload (); 501 uint32_t length = bufbe32toh (buf); 502 if (length > msg->GetLength ()) 503 { 504 LogPrint (eLogWarning, "Garlic: Message length ", length, " exceeds I2NP message length ", msg->GetLength ()); 505 return; 506 } 507 auto mod = length & 0x0f; // %16 508 buf += 4; // length 509 510 bool found = false; 511 bool supportsRatchets = SupportsRatchets (); 512 if (supportsRatchets) 513 // try ECIESx25519 tag 514 found = HandleECIESx25519TagMessage (buf, length); 515 if (!found) 516 { 517 auto it = !mod ? m_Tags.find (SessionTag(buf)) : m_Tags.end (); // AES block is multiple of 16 518 // AES tag might be used even if encryption type is not ElGamal/AES 519 if (it != m_Tags.end ()) // try AES tag 520 { 521 // tag found. Use AES 522 auto decryption = it->second; 523 m_Tags.erase (it); // tag might be used only once 524 if (length >= 32) 525 { 526 uint8_t iv[32]; // IV is first 16 bytes 527 SHA256(buf, 32, iv); 528 decryption->Decrypt (buf + 32, length - 32, iv, buf + 32); 529 HandleAESBlock (buf + 32, length - 32, decryption, msg->from); 530 found = true; 531 } 532 else 533 LogPrint (eLogWarning, "Garlic: Message length ", length, " is less than 32 bytes"); 534 } 535 if (!found) // assume new session 536 { 537 // AES tag not found. Handle depending on encryption type 538 // try ElGamal/AES first if leading block is 514 539 ElGamalBlock elGamal; 540 if (mod == 2 && length >= 514 && SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) && 541 Decrypt (buf, (uint8_t *)&elGamal, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)) 542 { 543 auto decryption = std::make_shared<AESDecryption>(elGamal.sessionKey); 544 uint8_t iv[32]; // IV is first 16 bytes 545 SHA256(elGamal.preIV, 32, iv); 546 decryption->Decrypt(buf + 514, length - 514, iv, buf + 514); 547 HandleAESBlock (buf + 514, length - 514, decryption, msg->from); 548 } 549 else if (supportsRatchets) 550 { 551 // otherwise ECIESx25519 552 auto ts = i2p::util::GetMillisecondsSinceEpoch (); 553 if (ts > m_LastIncomingSessionTimestamp + INCOMING_SESSIONS_MINIMAL_INTERVAL) 554 { 555 auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming 556 if (session->HandleNextMessage (buf, length, nullptr, 0)) 557 m_LastIncomingSessionTimestamp = ts; 558 else 559 LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); 560 } 561 else 562 LogPrint (eLogWarning, "Garlic: Incoming sessions come too often"); 563 } 564 else 565 LogPrint (eLogError, "Garlic: Failed to decrypt message"); 566 } 567 } 568 } 569 570 bool GarlicDestination::HandleECIESx25519TagMessage (uint8_t * buf, size_t len) 571 { 572 uint64_t tag; 573 memcpy (&tag, buf, 8); 574 auto it = m_ECIESx25519Tags.find (tag); 575 if (it != m_ECIESx25519Tags.end ()) 576 { 577 if (!it->second.tagset || !it->second.tagset->HandleNextMessage (buf, len, it->second.index)) 578 LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); 579 m_ECIESx25519Tags.erase (it); 580 return true; 581 } 582 return false; 583 } 584 585 void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption, 586 std::shared_ptr<i2p::tunnel::InboundTunnel> from) 587 { 588 uint16_t tagCount = bufbe16toh (buf); 589 buf += 2; len -= 2; 590 if (tagCount > 0) 591 { 592 if (tagCount*32 > len) 593 { 594 LogPrint (eLogError, "Garlic: Tag count ", tagCount, " exceeds length ", len); 595 return ; 596 } 597 uint32_t ts = i2p::util::GetSecondsSinceEpoch (); 598 for (int i = 0; i < tagCount; i++) 599 m_Tags[SessionTag(buf + i*32, ts)] = decryption; 600 } 601 buf += tagCount*32; 602 len -= tagCount*32; 603 uint32_t payloadSize = bufbe32toh (buf); 604 if (payloadSize > len) 605 { 606 LogPrint (eLogError, "Garlic: Unexpected payload size ", payloadSize); 607 return; 608 } 609 buf += 4; 610 uint8_t * payloadHash = buf; 611 buf += 32;// payload hash. 612 if (*buf) // session key? 613 buf += 32; // new session key 614 buf++; // flag 615 616 // payload 617 uint8_t digest[32]; 618 SHA256 (buf, payloadSize, digest); 619 if (memcmp (payloadHash, digest, 32)) // payload hash doesn't match 620 { 621 LogPrint (eLogError, "Garlic: Wrong payload hash"); 622 return; 623 } 624 HandleGarlicPayload (buf, payloadSize, from); 625 } 626 627 void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) 628 { 629 if (len < 1) 630 { 631 LogPrint (eLogError, "Garlic: Payload is too short"); 632 return; 633 } 634 int numCloves = buf[0]; 635 LogPrint (eLogDebug, "Garlic: ", numCloves," cloves"); 636 buf++; len--; 637 for (int i = 0; i < numCloves; i++) 638 { 639 const uint8_t * buf1 = buf; 640 // delivery instructions 641 uint8_t flag = buf[0]; 642 buf++; // flag 643 if (flag & 0x80) // encrypted? 644 { 645 // TODO: implement 646 LogPrint (eLogWarning, "Garlic: Clove encrypted"); 647 buf += 32; 648 } 649 ptrdiff_t offset = buf - buf1; 650 GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03); 651 switch (deliveryType) 652 { 653 case eGarlicDeliveryTypeLocal: 654 LogPrint (eLogDebug, "Garlic: Type local"); 655 if (offset > (int)len) 656 { 657 LogPrint (eLogError, "Garlic: Message is too short"); 658 break; 659 } 660 HandleI2NPMessage (buf, len - offset); 661 break; 662 case eGarlicDeliveryTypeDestination: 663 LogPrint (eLogDebug, "Garlic: Type destination"); 664 buf += 32; // destination. check it later or for multiple destinations 665 offset = buf - buf1; 666 if (offset > (int)len) 667 { 668 LogPrint (eLogError, "Garlic: Message is too short"); 669 break; 670 } 671 HandleI2NPMessage (buf, len - offset); 672 break; 673 case eGarlicDeliveryTypeTunnel: 674 { 675 LogPrint (eLogDebug, "Garlic: Type tunnel"); 676 // gwHash and gwTunnel sequence is reverted 677 uint8_t * gwHash = buf; 678 buf += 32; 679 offset = buf - buf1; 680 if (offset + 4 > (int)len) 681 { 682 LogPrint (eLogError, "Garlic: Message is too short"); 683 break; 684 } 685 uint32_t gwTunnel = bufbe32toh (buf); 686 buf += 4; offset += 4; 687 auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len - offset), from); 688 if (from) // received through an inbound tunnel 689 { 690 std::shared_ptr<i2p::tunnel::OutboundTunnel> tunnel; 691 if (from->GetTunnelPool ()) 692 tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel (); 693 else 694 LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel"); 695 if (tunnel) // we have sent it through an outbound tunnel 696 tunnel->SendTunnelDataMsgTo (gwHash, gwTunnel, msg); 697 else 698 LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); 699 } 700 else // received directly 701 i2p::transport::transports.SendMessage (gwHash, i2p::CreateTunnelGatewayMsg (gwTunnel, msg)); // send directly 702 break; 703 } 704 case eGarlicDeliveryTypeRouter: 705 { 706 uint8_t * ident = buf; 707 buf += 32; 708 offset = buf - buf1; 709 if (!from) // received directly 710 { 711 if (offset > (int)len) 712 { 713 LogPrint (eLogError, "Garlic: Message is too short"); 714 break; 715 } 716 i2p::transport::transports.SendMessage (ident, 717 CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len - offset))); 718 } 719 else 720 LogPrint (eLogWarning, "Garlic: Type router for inbound tunnels not supported"); 721 break; 722 } 723 default: 724 LogPrint (eLogWarning, "Garlic: Unknown delivery type ", (int)deliveryType); 725 } 726 if (offset > (int)len) 727 { 728 LogPrint (eLogError, "Garlic: Message is too short"); 729 break; 730 } 731 buf += GetI2NPMessageLength (buf, len - offset); // I2NP 732 buf += 4; // CloveID 733 buf += 8; // Date 734 buf += 3; // Certificate 735 offset = buf - buf1; 736 if (offset > (int)len) 737 { 738 LogPrint (eLogError, "Garlic: Clove is too long"); 739 break; 740 } 741 len -= offset; 742 } 743 } 744 745 std::shared_ptr<I2NPMessage> GarlicDestination::WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router, 746 std::shared_ptr<I2NPMessage> msg) 747 { 748 if (router->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) 749 return WrapECIESX25519MessageForRouter (msg, router->GetIdentity ()->GetEncryptionPublicKey ()); 750 else 751 { 752 auto session = GetRoutingSession (router, false); 753 return session->WrapSingleMessage (msg); 754 } 755 } 756 757 std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession ( 758 std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet, 759 bool requestNewIfNotFound) 760 { 761 if (destination->GetEncryptionType () >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) 762 { 763 if (SupportsEncryptionType (destination->GetEncryptionType ())) 764 { 765 ECIESX25519AEADRatchetSessionPtr session; 766 uint8_t staticKey[32]; 767 destination->Encrypt (nullptr, staticKey); // we are supposed to get static key 768 auto it = m_ECIESx25519Sessions.find (staticKey); 769 if (it != m_ECIESx25519Sessions.end ()) 770 { 771 session = it->second; 772 if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ())) 773 { 774 LogPrint (eLogDebug, "Garlic: Session restarted"); 775 requestNewIfNotFound = true; // it's not a new session 776 session = nullptr; 777 } 778 } 779 if (!session && requestNewIfNotFound) 780 { 781 session = std::make_shared<ECIESX25519AEADRatchetSession> (this, true); 782 session->SetRemoteStaticKey (destination->GetEncryptionType (), staticKey); 783 } 784 if (session && destination->IsDestination ()) 785 session->SetDestination (destination->GetIdentHash ()); // NS or NSR 786 return session; 787 } 788 else 789 LogPrint (eLogError, "Garlic: Non-supported encryption type ", destination->GetEncryptionType ()); 790 } 791 else 792 { 793 ElGamalAESSessionPtr session; 794 { 795 std::unique_lock<std::mutex> l(m_SessionsMutex); 796 auto it = m_Sessions.find (destination->GetIdentHash ()); 797 if (it != m_Sessions.end ()) 798 session = it->second; 799 } 800 if (!session) 801 { 802 session = std::make_shared<ElGamalAESSession> (this, destination, 803 attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests 804 std::unique_lock<std::mutex> l(m_SessionsMutex); 805 m_Sessions[destination->GetIdentHash ()] = session; 806 } 807 return session; 808 } 809 return nullptr; 810 } 811 812 void GarlicDestination::CleanupExpiredTags () 813 { 814 // incoming 815 uint32_t ts = i2p::util::GetSecondsSinceEpoch (); 816 int numExpiredTags = 0; 817 for (auto it = m_Tags.begin (); it != m_Tags.end ();) 818 { 819 if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) 820 { 821 numExpiredTags++; 822 it = m_Tags.erase (it); 823 } 824 else 825 ++it; 826 } 827 if (numExpiredTags > 0) 828 LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ()); 829 830 // outgoing 831 { 832 std::unique_lock<std::mutex> l(m_SessionsMutex); 833 for (auto it = m_Sessions.begin (); it != m_Sessions.end ();) 834 { 835 it->second->GetSharedRoutingPath (); // delete shared path if necessary 836 if (!it->second->CleanupExpiredTags ()) 837 { 838 LogPrint (eLogInfo, "Garlic: Routing session to ", it->first.ToBase32 (), " deleted"); 839 it->second->SetOwner (nullptr); 840 it = m_Sessions.erase (it); 841 } 842 else 843 ++it; 844 } 845 } 846 // delivery status sessions 847 { 848 std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex); 849 for (auto it = m_DeliveryStatusSessions.begin (); it != m_DeliveryStatusSessions.end (); ) 850 { 851 if (it->second->GetOwner () != this) 852 it = m_DeliveryStatusSessions.erase (it); 853 else 854 ++it; 855 } 856 } 857 // ECIESx25519 858 for (auto it = m_ECIESx25519Sessions.begin (); it != m_ECIESx25519Sessions.end ();) 859 { 860 if (it->second->CheckExpired (ts)) 861 { 862 it->second->Terminate (); 863 it = m_ECIESx25519Sessions.erase (it); 864 } 865 else 866 ++it; 867 } 868 869 numExpiredTags = 0; 870 for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();) 871 { 872 if (it->second.tagset->IsExpired (ts) || it->second.tagset->IsIndexExpired (it->second.index)) 873 { 874 it->second.tagset->DeleteSymmKey (it->second.index); 875 it = m_ECIESx25519Tags.erase (it); 876 numExpiredTags++; 877 } 878 else 879 { 880 if (it->second.tagset->IsSessionTerminated ()) 881 { 882 it = m_ECIESx25519Tags.erase (it); 883 numExpiredTags++; 884 } 885 else 886 ++it; 887 } 888 } 889 if (numExpiredTags > 0) 890 LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ()); 891 } 892 893 void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID) 894 { 895 std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex); 896 m_DeliveryStatusSessions.erase (msgID); 897 } 898 899 void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID) 900 { 901 std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex); 902 m_DeliveryStatusSessions[msgID] = session; 903 } 904 905 void GarlicDestination::HandleDeliveryStatusMessage (uint32_t msgID) 906 { 907 GarlicRoutingSessionPtr session; 908 { 909 std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex); 910 auto it = m_DeliveryStatusSessions.find (msgID); 911 if (it != m_DeliveryStatusSessions.end ()) 912 { 913 session = it->second; 914 m_DeliveryStatusSessions.erase (it); 915 } 916 } 917 if (session) 918 { 919 session->MessageConfirmed (msgID); 920 LogPrint (eLogDebug, "Garlic: Message ", msgID, " acknowledged"); 921 } 922 } 923 924 void GarlicDestination::SetLeaseSetUpdated (bool post) 925 { 926 { 927 std::unique_lock<std::mutex> l(m_SessionsMutex); 928 for (auto& it: m_Sessions) 929 it.second->SetLeaseSetUpdated (); 930 } 931 for (auto& it: m_ECIESx25519Sessions) 932 it.second->SetLeaseSetUpdated (); 933 } 934 935 void GarlicDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) 936 { 937 HandleGarlicMessage (msg); 938 } 939 940 void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) 941 { 942 uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); 943 HandleDeliveryStatusMessage (msgID); 944 } 945 946 void GarlicDestination::SaveTags () 947 { 948 if (m_Tags.empty ()) return; 949 std::string ident = GetIdentHash().ToBase32(); 950 std::string path = i2p::fs::DataDirPath("tags", (ident + ".tags")); 951 std::ofstream f (path, std::ofstream::binary | std::ofstream::out | std::ofstream::trunc); 952 uint32_t ts = i2p::util::GetSecondsSinceEpoch (); 953 // 4 bytes timestamp, 32 bytes tag, 32 bytes key 954 for (auto it: m_Tags) 955 { 956 if (ts < it.first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) 957 { 958 f.write ((char *)&it.first.creationTime, 4); 959 f.write ((char *)it.first.data (), 32); 960 f.write ((char *)it.second->GetKey ().data (), 32); 961 } 962 } 963 } 964 965 void GarlicDestination::LoadTags () 966 { 967 std::string ident = GetIdentHash().ToBase32(); 968 std::string path = i2p::fs::DataDirPath("tags", (ident + ".tags")); 969 uint32_t ts = i2p::util::GetSecondsSinceEpoch (); 970 if (ts < i2p::fs::GetLastUpdateTime (path) + INCOMING_TAGS_EXPIRATION_TIMEOUT) 971 { 972 // might contain non-expired tags 973 std::ifstream f (path, std::ifstream::binary); 974 if (f) 975 { 976 std::map<i2p::crypto::AESKey, std::shared_ptr<AESDecryption> > keys; 977 // 4 bytes timestamp, 32 bytes tag, 32 bytes key 978 while (!f.eof ()) 979 { 980 uint32_t t; 981 uint8_t tag[32], key[32]; 982 f.read ((char *)&t, 4); if (f.eof ()) break; 983 if (ts < t + INCOMING_TAGS_EXPIRATION_TIMEOUT) 984 { 985 f.read ((char *)tag, 32); 986 f.read ((char *)key, 32); 987 } 988 else 989 f.seekg (64, std::ios::cur); // skip 990 if (f.eof ()) break; 991 992 std::shared_ptr<AESDecryption> decryption; 993 auto it = keys.find (key); 994 if (it != keys.end ()) 995 decryption = it->second; 996 else 997 decryption = std::make_shared<AESDecryption>(key); 998 m_Tags.insert (std::make_pair (SessionTag (tag, ts), decryption)); 999 } 1000 if (!m_Tags.empty ()) 1001 LogPrint (eLogInfo, "Garlic: ", m_Tags.size (), " tags loaded for ", ident); 1002 } 1003 } 1004 i2p::fs::Remove (path); 1005 } 1006 1007 void CleanUpTagsFiles () 1008 { 1009 std::vector<std::string> files; 1010 i2p::fs::ReadDir (i2p::fs::DataDirPath("tags"), files); 1011 uint32_t ts = i2p::util::GetSecondsSinceEpoch (); 1012 for (auto it: files) 1013 if (ts >= i2p::fs::GetLastUpdateTime (it) + INCOMING_TAGS_EXPIRATION_TIMEOUT) 1014 i2p::fs::Remove (it); 1015 } 1016 1017 void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len, 1018 ECIESX25519AEADRatchetSession * from) 1019 { 1020 const uint8_t * buf1 = buf; 1021 uint8_t flag = buf[0]; buf++; // flag 1022 GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03); 1023 switch (deliveryType) 1024 { 1025 case eGarlicDeliveryTypeDestination: 1026 LogPrint (eLogDebug, "Garlic: Type destination"); 1027 buf += 32; // TODO: check destination 1028 [[fallthrough]]; 1029 // no break here 1030 case eGarlicDeliveryTypeLocal: 1031 { 1032 LogPrint (eLogDebug, "Garlic: Type local"); 1033 I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid 1034 int32_t msgID = bufbe32toh (buf); buf += 4; // msgID 1035 buf += 4; // expiration 1036 ptrdiff_t offset = buf - buf1; 1037 if (offset <= (int)len) 1038 HandleCloveI2NPMessage (typeID, buf, len - offset, msgID, from); 1039 else 1040 LogPrint (eLogError, "Garlic: Clove is too long"); 1041 break; 1042 } 1043 case eGarlicDeliveryTypeTunnel: 1044 { 1045 LogPrint (eLogDebug, "Garlic: Type tunnel"); 1046 // gwHash and gwTunnel sequence is reverted 1047 const uint8_t * gwHash = buf; 1048 buf += 32; 1049 ptrdiff_t offset = buf - buf1; 1050 if (offset + 13 > (int)len) 1051 { 1052 LogPrint (eLogError, "Garlic: Message is too short"); 1053 break; 1054 } 1055 uint32_t gwTunnel = bufbe32toh (buf); buf += 4; 1056 I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid 1057 uint32_t msgID = bufbe32toh (buf); buf += 4; // msgID 1058 buf += 4; // expiration 1059 offset += 13; 1060 if (GetTunnelPool ()) 1061 { 1062 auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel (); 1063 if (tunnel) 1064 tunnel->SendTunnelDataMsgTo (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset, msgID)); 1065 else 1066 LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); 1067 } 1068 else 1069 LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel"); 1070 break; 1071 } 1072 default: 1073 LogPrint (eLogWarning, "Garlic: Unexpected delivery type ", (int)deliveryType); 1074 } 1075 } 1076 1077 uint64_t GarlicDestination::AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset) 1078 { 1079 auto index = tagset->GetNextIndex (); 1080 uint64_t tag = tagset->GetNextSessionTag (); 1081 if (tag) 1082 m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{index, tagset}); 1083 return tag; 1084 } 1085 1086 void GarlicDestination::AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session) 1087 { 1088 i2p::data::Tag<32> staticKeyTag (staticKey); 1089 auto it = m_ECIESx25519Sessions.find (staticKeyTag); 1090 if (it != m_ECIESx25519Sessions.end ()) 1091 { 1092 if (it->second->CanBeRestarted (i2p::util::GetSecondsSinceEpoch ())) 1093 { 1094 it->second->Terminate (); // detach 1095 m_ECIESx25519Sessions.erase (it); 1096 } 1097 else 1098 { 1099 LogPrint (eLogInfo, "Garlic: ECIESx25519 session with static key ", staticKeyTag.ToBase64 (), " already exists"); 1100 return; 1101 } 1102 } 1103 m_ECIESx25519Sessions.emplace (staticKeyTag, session); 1104 } 1105 1106 void GarlicDestination::RemoveECIESx25519Session (const uint8_t * staticKey) 1107 { 1108 auto it = m_ECIESx25519Sessions.find (staticKey); 1109 if (it != m_ECIESx25519Sessions.end ()) 1110 { 1111 it->second->Terminate (); 1112 m_ECIESx25519Sessions.erase (it); 1113 } 1114 } 1115 1116 uint8_t * GarlicDestination::GetPayloadBuffer () 1117 { 1118 if (!m_PayloadBuffer) 1119 m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE]; 1120 return m_PayloadBuffer; 1121 } 1122 1123 bool GarlicDestination::AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, 1124 const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) 1125 { 1126 return m_Encryptor.Encrypt (msg, msgLen, ad, adLen, key, nonce, buf, len); 1127 } 1128 1129 bool GarlicDestination::AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, 1130 const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) 1131 { 1132 return m_Decryptor.Decrypt (msg, msgLen, ad, adLen, key, nonce, buf, len); 1133 } 1134 } 1135 }