void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag) { if (key) { uint32_t ts = i2p::util::GetSecondsSinceEpoch (); m_Tags[SessionTag(tag, ts)] = std::make_shared<AESDecryption>(key); } }
void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag) { if (key) { uint32_t ts = i2p::util::GetSecondsSinceEpoch (); auto decryption = std::make_shared<i2p::crypto::CBCDecryption>(); decryption->SetKey (key); m_Tags[SessionTag(tag, ts)] = decryption; } }
void GarlicDestination::HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg) { uint8_t * buf = msg->GetPayload (); uint32_t length = bufbe32toh (buf); if (length > msg->GetLength ()) { LogPrint (eLogWarning, "Garlic: message length ", length, " exceeds I2NP message length ", msg->GetLength ()); return; } buf += 4; // length auto it = m_Tags.find (SessionTag(buf)); if (it != m_Tags.end ()) { // tag found. Use AES auto decryption = it->second; m_Tags.erase (it); // tag might be used only once if (length >= 32) { uint8_t iv[32]; // IV is first 16 bytes SHA256(buf, 32, iv); decryption->SetIV (iv); decryption->Decrypt (buf + 32, length - 32, buf + 32); HandleAESBlock (buf + 32, length - 32, decryption, msg->from); } else LogPrint (eLogWarning, "Garlic: message length ", length, " is less than 32 bytes"); } else { // tag not found. Use ElGamal ElGamalBlock elGamal; if (length >= 514 && Decrypt (buf, (uint8_t *)&elGamal, m_Ctx)) { auto decryption = std::make_shared<AESDecryption>(elGamal.sessionKey); uint8_t iv[32]; // IV is first 16 bytes SHA256(elGamal.preIV, 32, iv); decryption->SetIV (iv); decryption->Decrypt(buf + 514, length - 514, buf + 514); HandleAESBlock (buf + 514, length - 514, decryption, msg->from); } else LogPrint (eLogError, "Garlic: Failed to decrypt message"); } }
void GarlicDestination::LoadTags () { std::string ident = GetIdentHash().ToBase32(); std::string path = i2p::fs::DataDirPath("tags", (ident + ".tags")); uint32_t ts = i2p::util::GetSecondsSinceEpoch (); if (ts < i2p::fs::GetLastUpdateTime (path) + INCOMING_TAGS_EXPIRATION_TIMEOUT) { // might contain non-expired tags std::ifstream f (path, std::ifstream::binary); if (f) { std::map<i2p::crypto::AESKey, std::shared_ptr<AESDecryption> > keys; // 4 bytes timestamp, 32 bytes tag, 32 bytes key while (!f.eof ()) { uint32_t t; uint8_t tag[32], key[32]; f.read ((char *)&t, 4); if (f.eof ()) break; if (ts < t + INCOMING_TAGS_EXPIRATION_TIMEOUT) { f.read ((char *)tag, 32); f.read ((char *)key, 32); } else f.seekg (64, std::ios::cur); // skip if (f.eof ()) break; std::shared_ptr<AESDecryption> decryption; auto it = keys.find (key); if (it != keys.end ()) decryption = it->second; else decryption = std::make_shared<AESDecryption>(key); m_Tags.insert (std::make_pair (SessionTag (tag, ts), decryption)); } if (!m_Tags.empty ()) LogPrint (eLogInfo, m_Tags.size (), " loaded for ", ident); } } i2p::fs::Remove (path); }
void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption, std::shared_ptr<i2p::tunnel::InboundTunnel> from) { uint16_t tagCount = bufbe16toh (buf); buf += 2; len -= 2; if (tagCount > 0) { if (tagCount*32 > len) { LogPrint (eLogError, "Garlic: Tag count ", tagCount, " exceeds length ", len); return ; } uint32_t ts = i2p::util::GetSecondsSinceEpoch (); for (int i = 0; i < tagCount; i++) m_Tags[SessionTag(buf + i*32, ts)] = decryption; } buf += tagCount*32; len -= tagCount*32; uint32_t payloadSize = bufbe32toh (buf); if (payloadSize > len) { LogPrint (eLogError, "Garlic: Unexpected payload size ", payloadSize); return; } buf += 4; uint8_t * payloadHash = buf; buf += 32;// payload hash. if (*buf) // session key? buf += 32; // new session key buf++; // flag // payload uint8_t digest[32]; SHA256 (buf, payloadSize, digest); if (memcmp (payloadHash, digest, 32)) // payload hash doesn't match { LogPrint (eLogError, "Garlic: wrong payload hash"); return; } HandleGarlicPayload (buf, payloadSize, from); }
void GarlicDestination::HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg) { uint8_t * buf = msg->GetPayload (); uint32_t length = bufbe32toh (buf); if (length > msg->GetLength ()) { LogPrint (eLogError, "Garlic message length ", length, " exceeds I2NP message length ", msg->GetLength ()); return; } buf += 4; // length auto it = m_Tags.find (SessionTag(buf)); if (it != m_Tags.end ()) { // tag found. Use AES if (length >= 32) { uint8_t iv[32]; // IV is first 16 bytes CryptoPP::SHA256().CalculateDigest(iv, buf, 32); it->second->SetIV (iv); it->second->Decrypt (buf + 32, length - 32, buf + 32); HandleAESBlock (buf + 32, length - 32, it->second, msg->from); } else LogPrint (eLogError, "Garlic message length ", length, " is less than 32 bytes"); m_Tags.erase (it); // tag might be used only once } else { // tag not found. Use ElGamal ElGamalBlock elGamal; if (length >= 514 && i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true)) { auto decryption = std::make_shared<i2p::crypto::CBCDecryption>(); decryption->SetKey (elGamal.sessionKey); uint8_t iv[32]; // IV is first 16 bytes CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); decryption->SetIV (iv); decryption->Decrypt(buf + 514, length - 514, buf + 514); HandleAESBlock (buf + 514, length - 514, decryption, msg->from); } else LogPrint (eLogError, "Failed to decrypt garlic"); } // cleanup expired tags uint32_t ts = i2p::util::GetSecondsSinceEpoch (); if (ts > m_LastTagsCleanupTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) { if (m_LastTagsCleanupTime) { int numExpiredTags = 0; for (auto it = m_Tags.begin (); it != m_Tags.end ();) { if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) { numExpiredTags++; it = m_Tags.erase (it); } else it++; } LogPrint (numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ()); } m_LastTagsCleanupTime = ts; } }