mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-01-22 08:14:15 +00:00
complete Bob side of NTCP2
This commit is contained in:
parent
07e7c2d852
commit
0ff9c9da27
@ -41,7 +41,7 @@ namespace transport
|
||||
HMAC(EVP_sha256(), tempKey, 32, m_CK, 33, derived, &len);
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * priv, const uint8_t * pub)
|
||||
void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, const uint8_t * priv, const uint8_t * rs, const uint8_t * epub)
|
||||
{
|
||||
static const uint8_t protocolNameHash[] =
|
||||
{
|
||||
@ -60,28 +60,28 @@ namespace transport
|
||||
SHA256_Update (&ctx, hh, 32);
|
||||
SHA256_Update (&ctx, rs, 32);
|
||||
SHA256_Final (m_H, &ctx);
|
||||
// h = SHA256(h || pub)
|
||||
// h = SHA256(h || epub)
|
||||
SHA256_Init (&ctx);
|
||||
SHA256_Update (&ctx, m_H, 32);
|
||||
SHA256_Update (&ctx, pub, 32);
|
||||
SHA256_Update (&ctx, epub, 32);
|
||||
SHA256_Final (m_H, &ctx);
|
||||
// x25519 between rs and priv
|
||||
uint8_t inputKeyMaterial[32];
|
||||
i2p::crypto::GetEd25519 ()->ScalarMul (rs, priv, inputKeyMaterial, m_Ctx); // rs*priv
|
||||
i2p::crypto::GetEd25519 ()->ScalarMul (pub, priv, inputKeyMaterial, m_Ctx); // rs*priv
|
||||
MixKey (inputKeyMaterial, m_K);
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF1Alice ()
|
||||
{
|
||||
KeyDerivationFunction1 (m_RemoteStaticKey, GetPriv (), GetPub ());
|
||||
KeyDerivationFunction1 (m_RemoteStaticKey, GetPriv (), m_RemoteStaticKey, GetPub ());
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF1Bob ()
|
||||
{
|
||||
KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticPrivateKey (), GetRemotePub ());
|
||||
KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticPrivateKey (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ());
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen)
|
||||
void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub)
|
||||
{
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init (&ctx);
|
||||
@ -99,7 +99,7 @@ namespace transport
|
||||
}
|
||||
SHA256_Init (&ctx);
|
||||
SHA256_Update (&ctx, m_H, 32);
|
||||
SHA256_Update (&ctx, GetRemotePub (), 32);
|
||||
SHA256_Update (&ctx, epub, 32);
|
||||
SHA256_Final (m_H, &ctx);
|
||||
|
||||
// x25519 between remote pub and priv
|
||||
@ -108,6 +108,16 @@ namespace transport
|
||||
MixKey (inputKeyMaterial, m_K);
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF2Alice (const uint8_t * sessionRequest, size_t sessionRequestLen)
|
||||
{
|
||||
KeyDerivationFunction2 (sessionRequest, sessionRequestLen, GetRemotePub ());
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF2Bob (const uint8_t * sessionRequest, size_t sessionRequestLen)
|
||||
{
|
||||
KeyDerivationFunction2 (sessionRequest, sessionRequestLen, GetPub ());
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF3Alice ()
|
||||
{
|
||||
uint8_t inputKeyMaterial[32];
|
||||
@ -137,14 +147,17 @@ namespace transport
|
||||
m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), m_IsSending (false)
|
||||
{
|
||||
m_Establisher.reset (new NTCP2Establisher);
|
||||
auto addr = in_RemoteRouter->GetNTCPAddress ();
|
||||
if (addr->ntcp2)
|
||||
if (in_RemoteRouter) // Alice
|
||||
{
|
||||
memcpy (m_Establisher->m_RemoteStaticKey, addr->ntcp2->staticKey, 32);
|
||||
memcpy (m_Establisher->m_IV, addr->ntcp2->iv, 16);
|
||||
auto addr = in_RemoteRouter->GetNTCP2Address ();
|
||||
if (addr->ntcp2)
|
||||
{
|
||||
memcpy (m_Establisher->m_RemoteStaticKey, addr->ntcp2->staticKey, 32);
|
||||
memcpy (m_Establisher->m_IV, addr->ntcp2->iv, 16);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters");
|
||||
}
|
||||
|
||||
NTCP2Session::~NTCP2Session ()
|
||||
@ -331,7 +344,7 @@ namespace transport
|
||||
encryption.SetIV (m_Establisher->m_IV);
|
||||
encryption.Encrypt (m_Establisher->GetPub (), 32, m_SessionCreatedBuffer); // Y
|
||||
// encryption key for next block (m_K)
|
||||
m_Establisher->KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen);
|
||||
m_Establisher->KDF2Bob (m_SessionRequestBuffer, m_SessionRequestBufferLen);
|
||||
auto paddingLen = rand () % (287 - 64);
|
||||
uint8_t options[16];
|
||||
memset (options, 0, 16);
|
||||
@ -342,7 +355,7 @@ namespace transport
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
i2p::crypto::AEADChaCha20Poly1305 (options, 16, m_Establisher->GetH (), 32, m_Establisher->GetK (), nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt
|
||||
// fill padding
|
||||
RAND_bytes (m_SessionCreatedBuffer + 56, paddingLen);
|
||||
RAND_bytes (m_SessionCreatedBuffer + 64, paddingLen);
|
||||
// send message
|
||||
m_SessionCreatedBufferLen = paddingLen + 64;
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_SessionCreatedBuffer, m_SessionCreatedBufferLen), boost::asio::transfer_all (),
|
||||
@ -366,7 +379,7 @@ namespace transport
|
||||
decryption.SetIV (m_Establisher->m_IV);
|
||||
decryption.Decrypt (m_SessionCreatedBuffer, 32, m_Establisher->GetRemotePub ());
|
||||
// decryption key for next block (m_K)
|
||||
m_Establisher->KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen);
|
||||
m_Establisher->KDF2Alice (m_SessionRequestBuffer, m_SessionRequestBufferLen);
|
||||
// decrypt and verify MAC
|
||||
uint8_t payload[16];
|
||||
uint8_t nonce[12];
|
||||
@ -386,7 +399,7 @@ namespace transport
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionCreated MAC verification failed ");
|
||||
LogPrint (eLogWarning, "NTCP2: SessionCreated AEAD verification failed ");
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
@ -492,12 +505,12 @@ namespace transport
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part 1 read error: ", ecode.message ());
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 read error: ", ecode.message ());
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: SessionConfirmed Part 1 received");
|
||||
LogPrint (eLogDebug, "NTCP2: SessionConfirmed Part1 received");
|
||||
// update AD
|
||||
uint8_t h[80];
|
||||
memcpy (h, m_Establisher->GetH (), 32);
|
||||
@ -515,44 +528,58 @@ namespace transport
|
||||
// part 1
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (1, nonce);
|
||||
i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 48, h, 32, m_Establisher->GetK (), nonce, m_Establisher->m_RemoteStaticKey, 32, false); // decrypt S
|
||||
// part 2
|
||||
// update AD again
|
||||
memcpy (h + 32, m_SessionConfirmedBuffer, 48);
|
||||
SHA256 (h, 80, m_Establisher->m_H);
|
||||
|
||||
std::vector<uint8_t> buf(m_Establisher->m3p2Len - 16); // -MAC
|
||||
m_Establisher->KDF3Bob ();
|
||||
memset (nonce, 0, 12); // set nonce to 0 again
|
||||
i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m_Establisher->m3p2Len, m_Establisher->GetH (), 32, m_Establisher->GetK (), nonce, buf.data (), m_Establisher->m3p2Len - 16, false); // decrypt
|
||||
// process RI
|
||||
if (buf[0] == eNTCP2BlkRouterInfo)
|
||||
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, h, 32, m_Establisher->GetK (), nonce, m_Establisher->m_RemoteStaticKey, 32, false)) // decrypt S
|
||||
{
|
||||
auto size = bufbe16toh (buf.data () + 1);
|
||||
if (size <= buf.size () - 3)
|
||||
// part 2
|
||||
// update AD again
|
||||
memcpy (h + 32, m_SessionConfirmedBuffer, 48);
|
||||
SHA256 (h, 80, m_Establisher->m_H);
|
||||
|
||||
std::vector<uint8_t> buf(m_Establisher->m3p2Len - 16); // -MAC
|
||||
m_Establisher->KDF3Bob ();
|
||||
memset (nonce, 0, 12); // set nonce to 0 again
|
||||
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m_Establisher->m3p2Len - 16, m_Establisher->GetH (), 32, m_Establisher->GetK (), nonce, buf.data (), m_Establisher->m3p2Len - 16, false)) // decrypt
|
||||
{
|
||||
// TODO: check flag
|
||||
i2p::data::netdb.AddRouterInfo (buf.data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag
|
||||
// TODO: process options
|
||||
}
|
||||
// process RI
|
||||
if (buf[0] == eNTCP2BlkRouterInfo)
|
||||
{
|
||||
auto size = bufbe16toh (buf.data () + 1);
|
||||
if (size <= buf.size () - 3)
|
||||
{
|
||||
// TODO: check flag
|
||||
i2p::data::netdb.AddRouterInfo (buf.data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag
|
||||
// TODO: process options
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "NTCP2: unexpected block ", (int)buf[0], " in SessionConfirmed");
|
||||
// caclulate new h again for KDF data
|
||||
memcpy (m_SessionConfirmedBuffer + 16, m_Establisher->GetH (), 32); // h || ciphertext
|
||||
SHA256 (m_SessionConfirmedBuffer + 16, m_Establisher->m3p2Len + 32, m_Establisher->m_H); //h = SHA256(h || ciphertext);
|
||||
KeyDerivationFunctionDataPhase ();
|
||||
// Bob
|
||||
m_SendKey = m_Kba;
|
||||
m_ReceiveKey = m_Kab;
|
||||
m_SendSipKey = m_Sipkeysba;
|
||||
m_ReceiveSipKey = m_Sipkeysab;
|
||||
memcpy (m_ReceiveIV, m_Sipkeysab + 16, 8);
|
||||
memcpy (m_SendIV, m_Sipkeysba + 16, 8);
|
||||
Established ();
|
||||
ReceiveLength ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed");
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 AEAD verification failed ");
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "NTCP2: unexpected block ", (int)buf[0], " in SessionConfirmed");
|
||||
// caclulate new h again for KDF data
|
||||
memcpy (m_SessionConfirmedBuffer + 16, m_Establisher->GetH (), 32); // h || ciphertext
|
||||
SHA256 (m_SessionConfirmedBuffer + 16, m_Establisher->m3p2Len + 32, m_Establisher->m_H); //h = SHA256(h || ciphertext);
|
||||
KeyDerivationFunctionDataPhase ();
|
||||
// Bob
|
||||
m_SendKey = m_Kba;
|
||||
m_ReceiveKey = m_Kab;
|
||||
m_SendSipKey = m_Sipkeysba;
|
||||
m_ReceiveSipKey = m_Sipkeysab;
|
||||
memcpy (m_ReceiveIV, m_Sipkeysab + 16, 8);
|
||||
memcpy (m_SendIV, m_Sipkeysba + 16, 8);
|
||||
Established ();
|
||||
ReceiveLength ();
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed ");
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -794,7 +821,7 @@ namespace transport
|
||||
continue;
|
||||
}
|
||||
|
||||
LogPrint (eLogInfo, "NTC2P: Start listening TCP port ", address->port);
|
||||
LogPrint (eLogInfo, "NTCP2: Start listening TCP port ", address->port);
|
||||
auto conn = std::make_shared<NTCP2Session>(*this);
|
||||
m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1));
|
||||
}
|
||||
|
@ -44,12 +44,14 @@ namespace transport
|
||||
|
||||
void KDF1Alice ();
|
||||
void KDF1Bob ();
|
||||
|
||||
void MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived);
|
||||
void KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * priv, const uint8_t * pub); // for SessionRequest
|
||||
void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen); // for SessionCreate
|
||||
void KDF2Alice (const uint8_t * sessionRequest, size_t sessionRequestLen);
|
||||
void KDF2Bob (const uint8_t * sessionRequest, size_t sessionRequestLen);
|
||||
void KDF3Alice (); // for SessionConfirmed part 2
|
||||
void KDF3Bob ();
|
||||
|
||||
void MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived);
|
||||
void KeyDerivationFunction1 (const uint8_t * pub, const uint8_t * priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
|
||||
void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
|
||||
void CreateEphemeralKey ();
|
||||
|
||||
|
||||
|
@ -478,7 +478,7 @@ namespace i2p
|
||||
if (n2k)
|
||||
{
|
||||
n2k.seekg (0, std::ios::end);
|
||||
len = fk.tellg();
|
||||
len = n2k.tellg();
|
||||
n2k.seekg (0, std::ios::beg);
|
||||
if (len == sizeof (NTCP2PrivateKeys))
|
||||
{
|
||||
|
@ -873,6 +873,7 @@ namespace data
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const
|
||||
{
|
||||
// TODO: make it more gereric using comparator
|
||||
#if (BOOST_VERSION >= 105300)
|
||||
auto addresses = boost::atomic_load (&m_Addresses);
|
||||
#else
|
||||
@ -889,6 +890,25 @@ namespace data
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetNTCP2Address (bool v4only) const
|
||||
{
|
||||
// TODO: implement through GetAddress
|
||||
#if (BOOST_VERSION >= 105300)
|
||||
auto addresses = boost::atomic_load (&m_Addresses);
|
||||
#else
|
||||
auto addresses = m_Addresses;
|
||||
#endif
|
||||
for (const auto& address : *addresses)
|
||||
{
|
||||
if (address->IsPublishedNTCP2 ())
|
||||
{
|
||||
if (!v4only || address->host.is_v4 ())
|
||||
return address;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<RouterProfile> RouterInfo::GetProfile () const
|
||||
{
|
||||
if (!m_Profile)
|
||||
|
@ -142,6 +142,7 @@ namespace data
|
||||
uint64_t GetTimestamp () const { return m_Timestamp; };
|
||||
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
|
||||
std::shared_ptr<const Address> GetNTCPAddress (bool v4only = true) const;
|
||||
std::shared_ptr<const Address> GetNTCP2Address (bool v4only = true) const;
|
||||
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
|
||||
std::shared_ptr<const Address> GetSSUV6Address () const;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user