|
|
@ -47,6 +47,7 @@ namespace ssu |
|
|
|
{ |
|
|
|
{ |
|
|
|
switch (m_State) |
|
|
|
switch (m_State) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
case eSessionStateConfirmedSent: |
|
|
|
case eSessionStateEstablished: |
|
|
|
case eSessionStateEstablished: |
|
|
|
// most common case
|
|
|
|
// most common case
|
|
|
|
ProcessMessage (buf, len); |
|
|
|
ProcessMessage (buf, len); |
|
|
@ -86,23 +87,38 @@ namespace ssu |
|
|
|
LogPrint ("SSU test received"); |
|
|
|
LogPrint ("SSU test received"); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case PAYLOAD_TYPE_SESSION_DESTROYED: |
|
|
|
case PAYLOAD_TYPE_SESSION_DESTROYED: |
|
|
|
|
|
|
|
{ |
|
|
|
LogPrint ("SSU session destroy received"); |
|
|
|
LogPrint ("SSU session destroy received"); |
|
|
|
|
|
|
|
if (m_Server) |
|
|
|
|
|
|
|
m_Server->DeleteSession (this); // delete this
|
|
|
|
|
|
|
|
} |
|
|
|
break; |
|
|
|
break; |
|
|
|
default: |
|
|
|
default: |
|
|
|
LogPrint ("Unexpected SSU payload type ", (int)payloadType); |
|
|
|
LogPrint ("Unexpected SSU payload type ", (int)payloadType); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// TODO: try intro key as well
|
|
|
|
|
|
|
|
else |
|
|
|
else |
|
|
|
LogPrint ("MAC verifcation failed"); |
|
|
|
{ |
|
|
|
|
|
|
|
LogPrint ("MAC key failed. Trying intro key"); |
|
|
|
|
|
|
|
auto introKey = GetIntroKey (); |
|
|
|
|
|
|
|
if (introKey && Validate (buf, len, introKey)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Decrypt (buf, len, introKey); |
|
|
|
|
|
|
|
SSUHeader * header = (SSUHeader *)buf; |
|
|
|
|
|
|
|
LogPrint ("Unexpected SSU payload type ", (int)(header->flag >> 4)); |
|
|
|
|
|
|
|
// TODO:
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
LogPrint ("MAC verifcation failed"); |
|
|
|
|
|
|
|
m_State = eSessionStateUnknown; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) |
|
|
|
void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) |
|
|
|
{ |
|
|
|
{ |
|
|
|
LogPrint ("Process session request"); |
|
|
|
LogPrint ("Process session request"); |
|
|
|
// use our intro key
|
|
|
|
// use our intro key
|
|
|
|
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, |
|
|
|
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, buf, len)) |
|
|
|
i2p::context.GetRouterInfo (), buf, len)) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
m_State = eSessionStateRequestReceived; |
|
|
|
m_State = eSessionStateRequestReceived; |
|
|
|
LogPrint ("Session request received"); |
|
|
|
LogPrint ("Session request received"); |
|
|
@ -121,7 +137,7 @@ namespace ssu |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// use remote intro key
|
|
|
|
// use remote intro key
|
|
|
|
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, *m_RemoteRouter, buf, len)) |
|
|
|
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, buf, len)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
m_State = eSessionStateCreatedReceived; |
|
|
|
m_State = eSessionStateCreatedReceived; |
|
|
|
LogPrint ("Session created received"); |
|
|
|
LogPrint ("Session created received"); |
|
|
@ -132,8 +148,6 @@ namespace ssu |
|
|
|
i2p::context.UpdateAddress (ourIP.to_string ().c_str ()); |
|
|
|
i2p::context.UpdateAddress (ourIP.to_string ().c_str ()); |
|
|
|
uint32_t relayTag = be32toh (*(uint32_t *)(buf + sizeof (SSUHeader) + 263)); |
|
|
|
uint32_t relayTag = be32toh (*(uint32_t *)(buf + sizeof (SSUHeader) + 263)); |
|
|
|
SendSessionConfirmed (buf + sizeof (SSUHeader), ourAddress, relayTag); |
|
|
|
SendSessionConfirmed (buf + sizeof (SSUHeader), ourAddress, relayTag); |
|
|
|
m_State = eSessionStateEstablished; |
|
|
|
|
|
|
|
Established (); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -148,8 +162,8 @@ namespace ssu |
|
|
|
{ |
|
|
|
{ |
|
|
|
m_State = eSessionStateConfirmedReceived; |
|
|
|
m_State = eSessionStateConfirmedReceived; |
|
|
|
LogPrint ("Session confirmed received"); |
|
|
|
LogPrint ("Session confirmed received"); |
|
|
|
// TODO:
|
|
|
|
|
|
|
|
m_State = eSessionStateEstablished; |
|
|
|
m_State = eSessionStateEstablished; |
|
|
|
|
|
|
|
// TODO: send DeliverStatus
|
|
|
|
Established (); |
|
|
|
Established (); |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
else |
|
|
@ -161,10 +175,10 @@ namespace ssu |
|
|
|
|
|
|
|
|
|
|
|
void SSUSession::SendSessionRequest () |
|
|
|
void SSUSession::SendSessionRequest () |
|
|
|
{ |
|
|
|
{ |
|
|
|
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr; |
|
|
|
auto introKey = GetIntroKey (); |
|
|
|
if (!address) |
|
|
|
if (!introKey) |
|
|
|
{ |
|
|
|
{ |
|
|
|
LogPrint ("Missing remote SSU address"); |
|
|
|
LogPrint ("SSU is not supported"); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -177,7 +191,7 @@ namespace ssu |
|
|
|
uint8_t iv[16]; |
|
|
|
uint8_t iv[16]; |
|
|
|
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); |
|
|
|
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); |
|
|
|
rnd.GenerateBlock (iv, 16); // random iv
|
|
|
|
rnd.GenerateBlock (iv, 16); // random iv
|
|
|
|
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, 304, address->key, iv, address->key); |
|
|
|
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, 304, introKey, iv, introKey); |
|
|
|
|
|
|
|
|
|
|
|
m_State = eSessionStateRequestSent; |
|
|
|
m_State = eSessionStateRequestSent; |
|
|
|
m_Server->Send (buf, 304, m_RemoteEndpoint); |
|
|
|
m_Server->Send (buf, 304, m_RemoteEndpoint); |
|
|
@ -185,10 +199,10 @@ namespace ssu |
|
|
|
|
|
|
|
|
|
|
|
void SSUSession::SendSessionCreated (const uint8_t * x) |
|
|
|
void SSUSession::SendSessionCreated (const uint8_t * x) |
|
|
|
{ |
|
|
|
{ |
|
|
|
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr; |
|
|
|
auto introKey = GetIntroKey (); |
|
|
|
if (!address) |
|
|
|
if (!introKey) |
|
|
|
{ |
|
|
|
{ |
|
|
|
LogPrint ("Missing remote SSU address"); |
|
|
|
LogPrint ("SSU is not supported"); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
uint8_t signedData[532]; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time
|
|
|
|
uint8_t signedData[532]; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time
|
|
|
@ -224,20 +238,13 @@ namespace ssu |
|
|
|
m_Encryption.ProcessData (payload, payload, 48); |
|
|
|
m_Encryption.ProcessData (payload, payload, 48); |
|
|
|
|
|
|
|
|
|
|
|
// encrypt message with intro key
|
|
|
|
// encrypt message with intro key
|
|
|
|
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, address->key, iv, address->key); |
|
|
|
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, introKey, iv, introKey); |
|
|
|
m_State = eSessionStateRequestSent; |
|
|
|
m_State = eSessionStateRequestSent; |
|
|
|
m_Server->Send (buf, 368, m_RemoteEndpoint); |
|
|
|
m_Server->Send (buf, 368, m_RemoteEndpoint); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag) |
|
|
|
void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag) |
|
|
|
{ |
|
|
|
{ |
|
|
|
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr; |
|
|
|
|
|
|
|
if (!address) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
LogPrint ("Missing remote SSU address"); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t buf[480 + 18]; |
|
|
|
uint8_t buf[480 + 18]; |
|
|
|
uint8_t * payload = buf + sizeof (SSUHeader); |
|
|
|
uint8_t * payload = buf + sizeof (SSUHeader); |
|
|
|
*payload = 1; // 1 fragment
|
|
|
|
*payload = 1; // 1 fragment
|
|
|
@ -275,15 +282,15 @@ namespace ssu |
|
|
|
m_Server->Send (buf, 480, m_RemoteEndpoint); |
|
|
|
m_Server->Send (buf, 480, m_RemoteEndpoint); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, const i2p::data::RouterInfo& r, uint8_t * buf, size_t len) |
|
|
|
bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len) |
|
|
|
{ |
|
|
|
{ |
|
|
|
auto address = r.GetSSUAddress (); |
|
|
|
auto introKey = GetIntroKey (); |
|
|
|
if (address) |
|
|
|
if (introKey) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// use intro key for verification and decryption
|
|
|
|
// use intro key for verification and decryption
|
|
|
|
if (Validate (buf, len, address->key)) |
|
|
|
if (Validate (buf, len, introKey)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Decrypt (buf, len, address->key); |
|
|
|
Decrypt (buf, len, introKey); |
|
|
|
SSUHeader * header = (SSUHeader *)buf; |
|
|
|
SSUHeader * header = (SSUHeader *)buf; |
|
|
|
if ((header->flag >> 4) == expectedPayloadType) |
|
|
|
if ((header->flag >> 4) == expectedPayloadType) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -297,7 +304,7 @@ namespace ssu |
|
|
|
LogPrint ("MAC verifcation failed"); |
|
|
|
LogPrint ("MAC verifcation failed"); |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
else |
|
|
|
LogPrint ("SSU is not supported by ", r.GetIdentHashAbbreviation ()); |
|
|
|
LogPrint ("SSU is not supported"); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -374,6 +381,7 @@ namespace ssu |
|
|
|
|
|
|
|
|
|
|
|
void SSUSession::Established () |
|
|
|
void SSUSession::Established () |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
SendI2NPMessage (CreateDatabaseStoreMsg ()); |
|
|
|
if (!m_DelayedMessages.empty ()) |
|
|
|
if (!m_DelayedMessages.empty ()) |
|
|
|
{ |
|
|
|
{ |
|
|
|
for (auto it :m_DelayedMessages) |
|
|
|
for (auto it :m_DelayedMessages) |
|
|
@ -382,6 +390,22 @@ namespace ssu |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const uint8_t * SSUSession::GetIntroKey () const |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (m_RemoteRouter) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// we are client
|
|
|
|
|
|
|
|
auto address = m_RemoteRouter->GetSSUAddress (); |
|
|
|
|
|
|
|
return address ? address->key : nullptr; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// we are server
|
|
|
|
|
|
|
|
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); |
|
|
|
|
|
|
|
return address ? address->key : nullptr; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void SSUSession::SendI2NPMessage (I2NPMessage * msg) |
|
|
|
void SSUSession::SendI2NPMessage (I2NPMessage * msg) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (msg) |
|
|
|
if (msg) |
|
|
@ -463,11 +487,25 @@ namespace ssu |
|
|
|
m_IncomleteMessages[msgID] = msg; |
|
|
|
m_IncomleteMessages[msgID] = msg; |
|
|
|
if (isLast) |
|
|
|
if (isLast) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
SendMsgAck (msgID); |
|
|
|
if (fragmentNum > 0) |
|
|
|
if (fragmentNum > 0) |
|
|
|
m_IncomleteMessages.erase (msgID); |
|
|
|
m_IncomleteMessages.erase (msgID); |
|
|
|
msg->FromSSU (msgID); |
|
|
|
msg->FromSSU (msgID); |
|
|
|
i2p::HandleI2NPMessage (msg, false); |
|
|
|
if (m_State == eSessionStateEstablished) |
|
|
|
SendMsgAck (msgID); |
|
|
|
i2p::HandleI2NPMessage (msg, false); |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// we expect DeliveryStatus
|
|
|
|
|
|
|
|
if (msg->GetHeader ()->typeID == eI2NPDeliveryStatus) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
LogPrint ("SSU session established"); |
|
|
|
|
|
|
|
m_State = eSessionStateEstablished; |
|
|
|
|
|
|
|
Established (); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
LogPrint ("SSU unexpected message ", (int)msg->GetHeader ()->typeID); |
|
|
|
|
|
|
|
DeleteI2NPMessage (msg); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
buf += fragmentSize; |
|
|
|
buf += fragmentSize; |
|
|
@ -499,8 +537,21 @@ namespace ssu |
|
|
|
uint8_t buf[48 + 18], iv[16]; |
|
|
|
uint8_t buf[48 + 18], iv[16]; |
|
|
|
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); |
|
|
|
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); |
|
|
|
rnd.GenerateBlock (iv, 16); // random iv
|
|
|
|
rnd.GenerateBlock (iv, 16); // random iv
|
|
|
|
// encrypt message with session key
|
|
|
|
if (m_State == eSessionStateEstablished) |
|
|
|
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey); |
|
|
|
// encrypt message with session key
|
|
|
|
|
|
|
|
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey); |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
auto introKey = GetIntroKey (); |
|
|
|
|
|
|
|
if (introKey) |
|
|
|
|
|
|
|
// encrypt message with intro key
|
|
|
|
|
|
|
|
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, introKey, iv, introKey); |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
LogPrint ("SSU: can't send SessionDestroyed message"); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
m_Server->Send (buf, 48, m_RemoteEndpoint); |
|
|
|
m_Server->Send (buf, 48, m_RemoteEndpoint); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -514,10 +565,13 @@ namespace ssu |
|
|
|
uint32_t fragmentNum = 0; |
|
|
|
uint32_t fragmentNum = 0; |
|
|
|
while (len > 0) |
|
|
|
while (len > 0) |
|
|
|
{ |
|
|
|
{ |
|
|
|
uint8_t buf[SSU_MTU + 18], iv[16]; |
|
|
|
uint8_t buf[SSU_MTU + 18], iv[16], * payload = buf + sizeof (SSUHeader); |
|
|
|
buf[0] = DATA_FLAG_WANT_REPLY; // for compatibility
|
|
|
|
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
|
|
|
|
buf[1] = 1; // always 1 message fragment per message
|
|
|
|
payload++; |
|
|
|
*(uint32_t *)(buf + 2) = msgID; |
|
|
|
*payload = 1; // always 1 message fragment per message
|
|
|
|
|
|
|
|
payload++; |
|
|
|
|
|
|
|
*(uint32_t *)payload = msgID; |
|
|
|
|
|
|
|
payload += 4; |
|
|
|
bool isLast = (len <= payloadSize); |
|
|
|
bool isLast = (len <= payloadSize); |
|
|
|
size_t size = isLast ? len : payloadSize; |
|
|
|
size_t size = isLast ? len : payloadSize; |
|
|
|
uint32_t fragmentInfo = (fragmentNum << 17); |
|
|
|
uint32_t fragmentInfo = (fragmentNum << 17); |
|
|
@ -526,10 +580,11 @@ namespace ssu |
|
|
|
|
|
|
|
|
|
|
|
fragmentInfo |= size; |
|
|
|
fragmentInfo |= size; |
|
|
|
fragmentInfo = htobe32 (fragmentInfo); |
|
|
|
fragmentInfo = htobe32 (fragmentInfo); |
|
|
|
memcpy (buf + 6, (uint8_t *)(&fragmentInfo) + 1, 3); |
|
|
|
memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3); |
|
|
|
memcpy (buf + 9, msgBuf, size); |
|
|
|
payload += 3; |
|
|
|
|
|
|
|
memcpy (payload, msgBuf, size); |
|
|
|
|
|
|
|
|
|
|
|
size += sizeof (SSUHeader) + 9; |
|
|
|
size += payload - buf; |
|
|
|
if (size % 16) // make sure 16 bytes boundary
|
|
|
|
if (size % 16) // make sure 16 bytes boundary
|
|
|
|
size = (size/16 + 1)*16; |
|
|
|
size = (size/16 + 1)*16; |
|
|
|
|
|
|
|
|
|
|
@ -570,6 +625,7 @@ namespace ssu |
|
|
|
|
|
|
|
|
|
|
|
void SSUServer::Stop () |
|
|
|
void SSUServer::Stop () |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
DeleteAllSessions (); |
|
|
|
m_Socket.close (); |
|
|
|
m_Socket.close (); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|