std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken) { if (!leaseSet) return nullptr; auto m = NewI2NPShortMessage (); uint8_t * payload = m->GetPayload (); memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32); payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); size_t size = DATABASE_STORE_HEADER_SIZE; if (replyToken) { auto leases = leaseSet->GetNonExpiredLeases (); if (leases.size () > 0) { htobe32buf (payload + size, leases[0].tunnelID); size += 4; // reply tunnelID memcpy (payload + size, leases[0].tunnelGateway, 32); size += 32; // reply tunnel gateway } else htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); } memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ()); size += leaseSet->GetBufferLen (); m->len += size; m->FillI2NPMessageHeader (eI2NPDatabaseStore); return m; }
size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID) { size_t size = 0; if (m_Owner) { auto inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel (); if (inboundTunnel) { buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel size++; // hash and tunnelID sequence is reversed for Garlic memcpy (buf + size, inboundTunnel->GetNextIdentHash (), 32); // To Hash size += 32; htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID size += 4; // create msg auto msg = CreateDeliveryStatusMsg (msgID); if (m_Owner) { //encrypt uint8_t key[32], tag[32]; RAND_bytes (key, 32); // random session key RAND_bytes (tag, 32); // random session tag m_Owner->SubmitSessionKey (key, tag); GarlicRoutingSession garlic (key, tag); msg = garlic.WrapSingleMessage (msg); } memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); size += msg->GetLength (); // fill clove uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec uint32_t cloveID; RAND_bytes ((uint8_t *)&cloveID, 4); htobe32buf (buf + size, cloveID); // CloveID size += 4; htobe64buf (buf + size, ts); // Expiration of clove size += 8; memset (buf + size, 0, 3); // certificate of clove size += 3; } else LogPrint (eLogError, "Garlic: No inbound tunnels in the pool for DeliveryStatus"); } else LogPrint (eLogWarning, "Garlic: Missing local LeaseSet"); return size; }
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg) { size_t blockSize = 0; bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3); UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr; htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count blockSize += 2; if (newTags) // session tags recreated { for (int i = 0; i < newTags->numTags; i++) { memcpy (buf + blockSize, newTags->sessionTags[i], 32); // tags blockSize += 32; } } uint32_t * payloadSize = (uint32_t *)(buf + blockSize); blockSize += 4; uint8_t * payloadHash = buf + blockSize; blockSize += 32; buf[blockSize] = 0; // flag blockSize++; size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags); htobe32buf (payloadSize, len); SHA256(buf + blockSize, len, payloadHash); blockSize += len; size_t rem = blockSize % 16; if (rem) blockSize += (16-rem); //padding m_Encryption.Encrypt(buf, blockSize, buf); return blockSize; }
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination) { uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec size_t size = 0; if (isDestination) { buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination size++; memcpy (buf + size, m_Destination->GetIdentHash (), 32); size += 32; } else { buf[size] = 0;// delivery instructions flag local size++; } memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); size += msg->GetLength (); uint32_t cloveID; RAND_bytes ((uint8_t *)&cloveID, 4); htobe32buf (buf + size, cloveID); // CloveID size += 4; htobe64buf (buf + size, ts); // Expiration of clove size += 8; memset (buf + size, 0, 3); // certificate of clove size += 3; return size; }
void TunnelGatewayBuffer::CompleteCurrentTunnelDataMessage () { if (!m_CurrentTunnelDataMsg) return; uint8_t * payload = m_CurrentTunnelDataMsg->GetBuffer (); size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset; m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE; uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload (); htobe32buf (buf, m_TunnelID); RAND_bytes (buf + 4, 16); // original IV memcpy (payload + size, buf + 4, 16); // copy IV for checksum uint8_t hash[32]; SHA256(payload, size+16, hash); memcpy (buf+20, hash, 4); // checksum payload[-1] = 0; // zero ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1 if (paddingSize > 0) { // non-zero padding auto randomOffset = rand () % (TUNNEL_DATA_MAX_PAYLOAD_SIZE - paddingSize + 1); memcpy (buf + 24, m_NonZeroRandomBuffer + randomOffset, paddingSize); } // we can't fill message header yet because encryption is required m_TunnelDataMsgs.push_back (m_CurrentTunnelDataMsg); m_CurrentTunnelDataMsg = nullptr; }
void TunnelGatewayBuffer::CompleteCurrentTunnelDataMessage () { if (!m_CurrentTunnelDataMsg) return; uint8_t * payload = m_CurrentTunnelDataMsg->GetBuffer (); size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset; m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE; uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload (); htobe32buf (buf, m_TunnelID); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); rnd.GenerateBlock (buf + 4, 16); // original IV memcpy (payload + size, buf + 4, 16); // copy IV for checksum uint8_t hash[32]; CryptoPP::SHA256().CalculateDigest (hash, payload, size+16); memcpy (buf+20, hash, 4); // checksum payload[-1] = 0; // zero ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1 if (paddingSize > 0) { // non-zero padding auto randomOffset = rnd.GenerateWord32 (0, TUNNEL_DATA_MAX_PAYLOAD_SIZE - paddingSize); memcpy (buf + 24, m_NonZeroRandomBuffer + randomOffset, paddingSize); } // we can't fill message header yet because encryption is required m_TunnelDataMsgs.push_back (m_CurrentTunnelDataMsg); m_CurrentTunnelDataMsg = nullptr; }
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags) { uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec uint32_t msgID; RAND_bytes ((uint8_t *)&msgID, 4); size_t size = 0; uint8_t * numCloves = payload + size; *numCloves = 0; size++; if (m_Owner) { // resubmit non-confirmed LeaseSet if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted && i2p::util::GetMillisecondsSinceEpoch () > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT) m_LeaseSetUpdateStatus = eLeaseSetUpdated; // attach DeviveryStatus if necessary if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated { // clove is DeliveryStatus auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID); if (cloveSize > 0) // successive? { size += cloveSize; (*numCloves)++; if (newTags) // new tags created m_UnconfirmedTagsMsgs[msgID] = newTags; m_Owner->DeliveryStatusSent (shared_from_this (), msgID); } else LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created"); } // attach LeaseSet if (m_LeaseSetUpdateStatus == eLeaseSetUpdated) { m_LeaseSetUpdateStatus = eLeaseSetSubmitted; m_LeaseSetUpdateMsgID = msgID; m_LeaseSetSubmissionTime = i2p::util::GetMillisecondsSinceEpoch (); // clove if our leaseSet must be attached auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ()); size += CreateGarlicClove (payload + size, leaseSet, false); (*numCloves)++; } } if (msg) // clove message ifself if presented { size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false); (*numCloves)++; } memset (payload + size, 0, 3); // certificate of message size += 3; htobe32buf (payload + size, msgID); // MessageID size += 4; htobe64buf (payload + size, ts); // Expiration of message size += 8; return size; }
void TransitTunnelParticipant::HandleTunnelDataMsg( std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) { auto newMsg = CreateEmptyTunnelDataMsg(); EncryptTunnelMsg(tunnelMsg, newMsg); m_NumTransmittedBytes += tunnelMsg->GetLength(); htobe32buf(newMsg->GetPayload(), GetNextTunnelID()); newMsg->FillI2NPMessageHeader(e_I2NPTunnelData); m_TunnelDataMsgs.push_back(newMsg); }
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload) { auto msg = NewI2NPShortMessage (); htobe32buf (msg->GetPayload (), tunnelID); msg->len += 4; // tunnelID msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4); msg->FillI2NPMessageHeader (eI2NPTunnelData); return msg; }
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID) { auto m = NewI2NPShortMessage (); uint8_t * buf = m->GetPayload (); if (msgID) { htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID); htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch ()); } else // for SSU establishment { RAND_bytes ((uint8_t *)&msgID, 4); htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID); htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2 } m->len += DELIVERY_STATUS_SIZE; m->FillI2NPMessageHeader (eI2NPDeliveryStatus); return m; }
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len) { auto msg = NewI2NPMessage (len); uint8_t * payload = msg->GetPayload (); htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); msg->len += TUNNEL_GATEWAY_HEADER_SIZE; if (msg->Concat (buf, len) < len) LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen); msg->FillI2NPMessageHeader (eI2NPTunnelGateway); return msg; }
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg) { if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE) { // message is capable to be used without copying uint8_t * payload = msg->GetBuffer () - TUNNEL_GATEWAY_HEADER_SIZE; htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); int len = msg->GetLength (); htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE); msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len; msg->FillI2NPMessageHeader (eI2NPTunnelGateway); return msg; } else return CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ()); }
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID) { auto msg = NewI2NPMessage (len); size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; msg->offset += gatewayMsgOffset; msg->len += gatewayMsgOffset; if (msg->Concat (buf, len) < len) LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen); msg->FillI2NPMessageHeader (msgType, replyMsgID); // create content message len = msg->GetLength (); msg->offset -= gatewayMsgOffset; uint8_t * payload = msg->GetPayload (); htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); msg->FillI2NPMessageHeader (eI2NPTunnelGateway); // gateway message return msg; }
I2NPMessage * DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort) { I2NPMessage * msg = NewI2NPMessage (); CryptoPP::Gzip compressor; // default level compressor.Put (payload, len); compressor.MessageEnd(); int size = compressor.MaxRetrievable (); uint8_t * buf = msg->GetPayload (); htobe32buf (buf, size); // length buf += 4; compressor.Get (buf, size); htobe16buf (buf + 4, fromPort); // source port htobe16buf (buf + 6, toPort); // destination port buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol msg->len += size + 4; FillI2NPMessageHeader (msg, eI2NPData); return msg; }
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers) { auto m = excludedPeers ? NewI2NPMessage () : NewI2NPShortMessage (); uint8_t * buf = m->GetPayload (); memcpy (buf, key, 32); // key buf += 32; memcpy (buf, from, 32); // from buf += 32; uint8_t flag = exploratory ? DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP : DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP; if (replyTunnelID) { *buf = flag | DATABASE_LOOKUP_DELIVERY_FLAG; // set delivery flag htobe32buf (buf+1, replyTunnelID); buf += 5; } else { *buf = flag; // flag buf++; } if (excludedPeers) { int cnt = excludedPeers->size (); htobe16buf (buf, cnt); buf += 2; for (auto& it: *excludedPeers) { memcpy (buf, it, 32); buf += 32; } } else { // nothing to exclude htobuf16 (buf, 0); buf += 2; } m->len += (buf - m->GetPayload ()); m->FillI2NPMessageHeader (eI2NPDatabaseLookup); return m; }
LeaseSet::LeaseSet (std::shared_ptr<const i2p::tunnel::TunnelPool> pool): m_IsValid (true) { if (!pool) return; // header auto localDestination = pool->GetLocalDestination (); if (!localDestination) { m_Buffer = nullptr; m_BufferLen = 0; m_IsValid = false; LogPrint (eLogError, "LeaseSet: Destination for local LeaseSet doesn't exist"); return; } m_Buffer = new uint8_t[MAX_LS_BUFFER_SIZE]; m_BufferLen = localDestination->GetIdentity ()->ToBuffer (m_Buffer, MAX_LS_BUFFER_SIZE); memcpy (m_Buffer + m_BufferLen, localDestination->GetEncryptionPublicKey (), 256); m_BufferLen += 256; auto signingKeyLen = localDestination->GetIdentity ()->GetSigningPublicKeyLen (); memset (m_Buffer + m_BufferLen, 0, signingKeyLen); m_BufferLen += signingKeyLen; auto tunnels = pool->GetInboundTunnels (5); // 5 tunnels maximum m_Buffer[m_BufferLen] = tunnels.size (); // num leases m_BufferLen++; // leases for (auto it: tunnels) { memcpy (m_Buffer + m_BufferLen, it->GetNextIdentHash (), 32); m_BufferLen += 32; // gateway id htobe32buf (m_Buffer + m_BufferLen, it->GetNextTunnelID ()); m_BufferLen += 4; // tunnel id uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration ts *= 1000; // in milliseconds ts += rand () % 6; // + random milliseconds 0-5 htobe64buf (m_Buffer + m_BufferLen, ts); m_BufferLen += 8; // end date } // signature localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen); m_BufferLen += localDestination->GetIdentity ()->GetSignatureLen (); LogPrint (eLogDebug, "LeaseSet: Local LeaseSet of ", tunnels.size (), " leases created"); ReadFromBuffer (); }
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, const std::set<i2p::data::IdentHash>& excludedFloodfills, const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag) { int cnt = excludedFloodfills.size (); auto m = cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage (); uint8_t * buf = m->GetPayload (); memcpy (buf, dest, 32); // key buf += 32; memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW buf += 32; *buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID buf += 5; // excluded htobe16buf (buf, cnt); buf += 2; if (cnt > 0) { for (auto& it: excludedFloodfills) { memcpy (buf, it, 32); buf += 32; } } // encryption memcpy (buf, replyKey, 32); buf[32] = 1; // 1 tag memcpy (buf + 33, replyTag, 32); buf += 65; m->len += (buf - m->GetPayload ()); m->FillI2NPMessageHeader (eI2NPDatabaseLookup); return m; }
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router, uint32_t replyToken) { if (!router) // we send own RouterInfo router = context.GetSharedRouterInfo (); auto m = NewI2NPShortMessage (); uint8_t * payload = m->GetPayload (); memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32); payload[DATABASE_STORE_TYPE_OFFSET] = 0; // RouterInfo htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE; if (replyToken) { memset (buf, 0, 4); // zero tunnelID means direct reply buf += 4; memcpy (buf, router->GetIdentHash (), 32); buf += 32; } uint8_t * sizePtr = buf; buf += 2; m->len += (buf - payload); // payload size i2p::data::GzipDeflator deflator; size_t size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len); if (size) { htobe16buf (sizePtr, size); // size m->len += size; } else m = nullptr; if (m) m->FillI2NPMessageHeader (eI2NPDatabaseStore); return m; }
void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block) { bool messageCreated = false; if (!m_CurrentTunnelDataMsg) { CreateCurrentTunnelDataMessage (); messageCreated = true; } // create delivery instructions uint8_t di[43]; // max delivery instruction length is 43 for tunnel size_t diLen = 1;// flag if (block.deliveryType != eDeliveryTypeLocal) // tunnel or router { if (block.deliveryType == eDeliveryTypeTunnel) { htobe32buf (di + diLen, block.tunnelID); diLen += 4; // tunnelID } memcpy (di + diLen, block.hash, 32); diLen += 32; //len } di[0] = block.deliveryType << 5; // set delivery type // create fragments std::shared_ptr<I2NPMessage> msg = block.data; auto fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length if (fullMsgLen <= m_RemainingSize) { // message fits. First and last fragment htobe16buf (di + diLen, msg->GetLength ()); diLen += 2; // size memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), msg->GetLength ()); m_CurrentTunnelDataMsg->len += diLen + msg->GetLength (); m_RemainingSize -= diLen + msg->GetLength (); if (!m_RemainingSize) CompleteCurrentTunnelDataMessage (); } else { if (!messageCreated) // check if we should complete previous message { auto numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE; // length of bytes don't fit full tunnel message // every follow-on fragment adds 7 bytes auto nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE; if (!nonFit || nonFit > m_RemainingSize) { CompleteCurrentTunnelDataMessage (); CreateCurrentTunnelDataMessage (); } } if (diLen + 6 <= m_RemainingSize) { // delivery instructions fit uint32_t msgID; memcpy (&msgID, msg->GetHeader () + I2NP_HEADER_MSGID_OFFSET, 4); // in network bytes order size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size) // first fragment di[0] |= 0x08; // fragmented htobuf32 (di + diLen, msgID); diLen += 4; // Message ID htobe16buf (di + diLen, size); diLen += 2; // size memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), size); m_CurrentTunnelDataMsg->len += diLen + size; CompleteCurrentTunnelDataMessage (); // follow on fragments int fragmentNumber = 1; while (size < msg->GetLength ()) { CreateCurrentTunnelDataMessage (); uint8_t * buf = m_CurrentTunnelDataMsg->GetBuffer (); buf[0] = 0x80 | (fragmentNumber << 1); // frag bool isLastFragment = false; size_t s = msg->GetLength () - size; if (s > TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7) // 7 follow on instructions s = TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7; else // last fragment { buf[0] |= 0x01; isLastFragment = true; } htobuf32 (buf + 1, msgID); //Message ID htobe16buf (buf + 5, s); // size memcpy (buf + 7, msg->GetBuffer () + size, s); m_CurrentTunnelDataMsg->len += s+7; if (isLastFragment) { m_RemainingSize -= s+7; if (!m_RemainingSize) CompleteCurrentTunnelDataMessage (); } else CompleteCurrentTunnelDataMessage (); size += s; fragmentNumber++; } } else { // delivery instructions don't fit. Create new message CompleteCurrentTunnelDataMessage (); PutI2NPMsg (block); // don't delete msg because it's taken care inside } } }
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags) { uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); uint32_t msgID; RAND_bytes ((uint8_t *)&msgID, 4); size_t size = 0; uint8_t * numCloves = payload + size; *numCloves = 0; size++; if (m_Owner) { // resubmit non-confirmed LeaseSet if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted && ts > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT) { m_LeaseSetUpdateStatus = eLeaseSetUpdated; SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed } // attach DeviveryStatus if necessary if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated { // clove is DeliveryStatus auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID); if (cloveSize > 0) // successive? { size += cloveSize; (*numCloves)++; if (newTags) // new tags created { newTags->msgID = msgID; m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr<UnconfirmedTags>(newTags))); newTags = nullptr; // got acquired } m_Owner->DeliveryStatusSent (shared_from_this (), msgID); } else LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created"); } // attach LeaseSet if (m_LeaseSetUpdateStatus == eLeaseSetUpdated) { if (m_LeaseSetUpdateMsgID) m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); // remove previous m_LeaseSetUpdateStatus = eLeaseSetSubmitted; m_LeaseSetUpdateMsgID = msgID; m_LeaseSetSubmissionTime = ts; // clove if our leaseSet must be attached auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ()); size += CreateGarlicClove (payload + size, leaseSet, false); (*numCloves)++; } } if (msg) // clove message ifself if presented { size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false); (*numCloves)++; } memset (payload + size, 0, 3); // certificate of message size += 3; htobe32buf (payload + size, msgID); // MessageID size += 4; htobe64buf (payload + size, ts + 8000); // Expiration of message, 8 sec size += 8; if (newTags) delete newTags; // not acquired, delete return size; }
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) { auto m = NewI2NPMessage (); m->Align (12); // in order to get buf aligned to 16 (12 + 4) size_t len = 0; uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length // find non-expired tag bool tagFound = false; SessionTag tag; if (m_NumTags > 0) { uint32_t ts = i2p::util::GetSecondsSinceEpoch (); while (!m_SessionTags.empty ()) { if (ts < m_SessionTags.front ().creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) { tag = m_SessionTags.front (); m_SessionTags.pop_front (); // use same tag only once tagFound = true; break; } else m_SessionTags.pop_front (); // remove expired tag } } // create message if (!tagFound) // new session { LogPrint (eLogInfo, "Garlic: No tags available, will use ElGamal"); if (!m_Destination) { LogPrint (eLogError, "Garlic: Can't use ElGamal for unknown destination"); return nullptr; } // create ElGamal block ElGamalBlock elGamal; memcpy (elGamal.sessionKey, m_SessionKey, 32); RAND_bytes (elGamal.preIV, 32); // Pre-IV uint8_t iv[32]; // IV is first 16 bytes SHA256(elGamal.preIV, 32, iv); BN_CTX * ctx = BN_CTX_new (); m_Destination->Encrypt ((uint8_t *)&elGamal, buf, ctx); BN_CTX_free (ctx); m_Encryption.SetIV (iv); buf += 514; len += 514; } else // existing session { // session tag memcpy (buf, tag, 32); uint8_t iv[32]; // IV is first 16 bytes SHA256(tag, 32, iv); m_Encryption.SetIV (iv); buf += 32; len += 32; } // AES block len += CreateAESBlock (buf, msg); htobe32buf (m->GetPayload (), len); m->len += len + 4; m->FillI2NPMessageHeader (eI2NPGarlic); return m; }