Browse Source

tunnels through ECIES routers

pull/1565/head
orignal 4 years ago
parent
commit
c93ee0d65d
  1. 19
      libi2pd/I2NPProtocol.h
  2. 27
      libi2pd/Timestamp.cpp
  3. 3
      libi2pd/Timestamp.h
  4. 18
      libi2pd/Tunnel.cpp
  5. 72
      libi2pd/TunnelConfig.cpp
  6. 7
      libi2pd/TunnelConfig.h

19
libi2pd/I2NPProtocol.h

@ -81,6 +81,25 @@ namespace i2p
const size_t BUILD_RESPONSE_RECORD_PADDING_SIZE = 495; const size_t BUILD_RESPONSE_RECORD_PADDING_SIZE = 495;
const size_t BUILD_RESPONSE_RECORD_RET_OFFSET = BUILD_RESPONSE_RECORD_PADDING_OFFSET + BUILD_RESPONSE_RECORD_PADDING_SIZE; const size_t BUILD_RESPONSE_RECORD_RET_OFFSET = BUILD_RESPONSE_RECORD_PADDING_OFFSET + BUILD_RESPONSE_RECORD_PADDING_SIZE;
// ECIES BuildRequestRecordClearText
const size_t ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
const size_t ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET = ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET = ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32;
const size_t ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET = ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET + 32;
const size_t ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET = ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET + 32;
const size_t ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET = ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET + 32;
const size_t ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET = ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET + 16;
const size_t ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET = ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET + 1;
const size_t ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET = ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET + 3;
const size_t ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET = ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET = ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 464;
// ECIES BuildResponseRecord
const size_t ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET = 511;
enum I2NPMessageType enum I2NPMessageType
{ {
eI2NPDummyMsg = 0, eI2NPDummyMsg = 0,

27
libi2pd/Timestamp.cpp

@ -35,15 +35,21 @@ namespace util
std::chrono::system_clock::now().time_since_epoch()).count (); std::chrono::system_clock::now().time_since_epoch()).count ();
} }
static uint32_t GetLocalHoursSinceEpoch () static uint64_t GetLocalSecondsSinceEpoch ()
{ {
return std::chrono::duration_cast<std::chrono::hours>( return std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count (); std::chrono::system_clock::now().time_since_epoch()).count ();
} }
static uint64_t GetLocalSecondsSinceEpoch () static uint32_t GetLocalMinutesSinceEpoch ()
{ {
return std::chrono::duration_cast<std::chrono::seconds>( return std::chrono::duration_cast<std::chrono::minutes>(
std::chrono::system_clock::now().time_since_epoch()).count ();
}
static uint32_t GetLocalHoursSinceEpoch ()
{
return std::chrono::duration_cast<std::chrono::hours>(
std::chrono::system_clock::now().time_since_epoch()).count (); std::chrono::system_clock::now().time_since_epoch()).count ();
} }
@ -178,14 +184,19 @@ namespace util
return GetLocalMillisecondsSinceEpoch () + g_TimeOffset*1000; return GetLocalMillisecondsSinceEpoch () + g_TimeOffset*1000;
} }
uint32_t GetHoursSinceEpoch () uint64_t GetSecondsSinceEpoch ()
{ {
return GetLocalHoursSinceEpoch () + g_TimeOffset/3600; return GetLocalSecondsSinceEpoch () + g_TimeOffset;
} }
uint64_t GetSecondsSinceEpoch () uint32_t GetMinutesSinceEpoch ()
{ {
return GetLocalSecondsSinceEpoch () + g_TimeOffset; return GetLocalMinutesSinceEpoch () + g_TimeOffset/60;
}
uint32_t GetHoursSinceEpoch ()
{
return GetLocalHoursSinceEpoch () + g_TimeOffset/3600;
} }
void GetCurrentDate (char * date) void GetCurrentDate (char * date)

3
libi2pd/Timestamp.h

@ -20,8 +20,9 @@ namespace i2p
namespace util namespace util
{ {
uint64_t GetMillisecondsSinceEpoch (); uint64_t GetMillisecondsSinceEpoch ();
uint32_t GetHoursSinceEpoch ();
uint64_t GetSecondsSinceEpoch (); uint64_t GetSecondsSinceEpoch ();
uint32_t GetMinutesSinceEpoch ();
uint32_t GetHoursSinceEpoch ();
void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes
void GetDateString (uint64_t timestamp, char * date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes void GetDateString (uint64_t timestamp, char * date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes

18
libi2pd/Tunnel.cpp

@ -111,7 +111,7 @@ namespace tunnel
while (hop) while (hop)
{ {
decryption.SetKey (hop->replyKey); decryption.SetKey (hop->replyKey);
// decrypt records before and including current hop // decrypt records before and current hop
TunnelHopConfig * hop1 = hop; TunnelHopConfig * hop1 = hop;
while (hop1) while (hop1)
{ {
@ -119,9 +119,23 @@ namespace tunnel
if (idx >= 0 && idx < msg[0]) if (idx >= 0 && idx < msg[0])
{ {
uint8_t * record = msg + 1 + idx*TUNNEL_BUILD_RECORD_SIZE; uint8_t * record = msg + 1 + idx*TUNNEL_BUILD_RECORD_SIZE;
if (hop1 == hop && hop1->IsECIES ())
{
uint8_t nonce[12];
memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (record, TUNNEL_BUILD_RECORD_SIZE - 16,
hop->h, 32, hop->ck, nonce, record, TUNNEL_BUILD_RECORD_SIZE - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed");
return false;
}
}
else
{
decryption.SetIV (hop->replyIV); decryption.SetIV (hop->replyIV);
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
} }
}
else else
LogPrint (eLogWarning, "Tunnel: hop index ", idx, " is out of range"); LogPrint (eLogWarning, "Tunnel: hop index ", idx, " is out of range");
hop1 = hop1->prev; hop1 = hop1->prev;
@ -134,7 +148,7 @@ namespace tunnel
while (hop) while (hop)
{ {
const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE; const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
uint8_t ret = record[BUILD_RESPONSE_RECORD_RET_OFFSET]; uint8_t ret = record[hop->IsECIES () ? ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET : BUILD_RESPONSE_RECORD_RET_OFFSET];
LogPrint (eLogDebug, "Tunnel: Build response ret code=", (int)ret); LogPrint (eLogDebug, "Tunnel: Build response ret code=", (int)ret);
auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ()); auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ());
if (profile) if (profile)

72
libi2pd/TunnelConfig.cpp

@ -9,6 +9,9 @@
#include <memory> #include <memory>
#include <openssl/rand.h> #include <openssl/rand.h>
#include <openssl/sha.h>
#include "Crypto.h"
#include "Log.h"
#include "Transports.h" #include "Transports.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "I2PEndian.h" #include "I2PEndian.h"
@ -76,7 +79,32 @@ namespace tunnel
} }
} }
void TunnelHopConfig::CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx) const void TunnelHopConfig::CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx)
{
uint8_t flag = 0;
if (isGateway) flag |= 0x80;
if (isEndpoint) flag |= 0x40;
auto encryptor = ident->CreateEncryptor (nullptr);
if (IsECIES ())
{
uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
memset (clearText + ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 3); // set to 0 for compatibility
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ());
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, i2p::util::GetSecondsSinceEpoch () + 600); // 10 minutes
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
memset (clearText + ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET, 0, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET);
if (encryptor)
EncryptECIES (encryptor, clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx);
}
else
{ {
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
@ -87,28 +115,50 @@ namespace tunnel
memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32); memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32); memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16); memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
uint8_t flag = 0;
if (isGateway) flag |= 0x80;
if (isEndpoint) flag |= 0x40;
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag; clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ()); htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ());
htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET); RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET);
auto encryptor = ident->CreateEncryptor (nullptr);
if (encryptor) if (encryptor)
{
if (ident->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)
EncryptECIES (encryptor, clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx);
else
encryptor->Encrypt (clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx, false); encryptor->Encrypt (clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx, false);
} }
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
} }
void TunnelHopConfig::EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor, void TunnelHopConfig::EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor,
const uint8_t * clearText, uint8_t * encrypted, BN_CTX * ctx) const const uint8_t * plainText, uint8_t * encrypted, BN_CTX * ctx)
{
static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars
memcpy (ck, protocolName, 32); // ck = h = protocol_name || 0
SHA256 (ck, 32, h); // h = SHA256(h);
uint8_t hepk[32];
encryptor->Encrypt (nullptr, hepk, nullptr, false);
MixHash (hepk, 32);
auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
memcpy (encrypted, ephemeralKeys->GetPublicKey (), 32); encrypted += 32;
uint8_t sharedSecret[32];
ephemeralKeys->Agree (hepk, sharedSecret); // x25519(sesk, hepk)
uint8_t keydata[64];
i2p::crypto::HKDF (ck, sharedSecret, 32, "", keydata);
memcpy (ck, keydata, 32);
uint8_t nonce[12];
memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, h, 32,
keydata + 32, nonce, encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16, true)) // encrypt
{
LogPrint (eLogWarning, "Tunnel: Plaintext AEAD encryption failed");
return;
}
MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext)
}
void TunnelHopConfig::MixHash (const uint8_t * buf, size_t len)
{ {
memset (encrypted, 0, 512); // TODO: implement SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, h, 32);
SHA256_Update (&ctx, buf, len);
SHA256_Final (h, &ctx);
} }
} }
} }

7
libi2pd/TunnelConfig.h

@ -30,6 +30,7 @@ namespace tunnel
TunnelHopConfig * next, * prev; TunnelHopConfig * next, * prev;
int recordIndex; // record # in tunnel build message int recordIndex; // record # in tunnel build message
uint8_t ck[32], h[32]; // for ECIES
TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r); TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r);
@ -38,9 +39,11 @@ namespace tunnel
void SetNext (TunnelHopConfig * n); void SetNext (TunnelHopConfig * n);
void SetPrev (TunnelHopConfig * p); void SetPrev (TunnelHopConfig * p);
void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx) const; bool IsECIES () const { return ident->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET; };
void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx);
void EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor, void EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor,
const uint8_t * clearText, uint8_t * encrypted, BN_CTX * ctx) const; const uint8_t * clearText, uint8_t * encrypted, BN_CTX * ctx);
void MixHash (const uint8_t * buf, size_t len);
}; };
class TunnelConfig class TunnelConfig

Loading…
Cancel
Save