chahca nonce size is 24 bytes

Lots of code was using 32-byte nonces for xchacha20 symmetric
encryption, but this just means 8 extra bytes per packet wasted as
chacha is only using the first 24 bytes of that nonce anyway.

Changing this resulted in a lot of dead/dying code breaking, so this
commit also removes a lot of that (and comments a couple places with
TODO instead)

Also nounce -> nonce where it came up.
pull/2216/head
Thomas Winget 7 months ago
parent abb2f63ec6
commit 9e9c1ea732

@ -12,7 +12,6 @@ endfunction()
lokinet_add_library(lokinet-cryptography
crypto/crypto.cpp
crypto/encrypted_frame.cpp
crypto/types.cpp
)
@ -207,7 +206,6 @@ lokinet_add_library(lokinet-config
# All path objects; link directly to lokinet-core
lokinet_add_library(lokinet-path
messages/relay.cpp
path/abstracthophandler.cpp
path/path.cpp
path/path_context.cpp

@ -65,7 +65,7 @@ namespace llarp
static bool
dh_client_priv(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const SymmNonce& n)
{
llarp::SharedSecret dh_result;
@ -81,13 +81,13 @@ namespace llarp
static bool
dh_server_priv(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const SymmNonce& n)
{
llarp::SharedSecret dh_result;
if (dh(dh_result, pk, sk.toPublic(), pk.data(), sk))
{
return crypto_generichash_blake2b(shared.data(), 32, n.data(), 32, dh_result.data(), 32)
return crypto_generichash_blake2b(shared.data(), 32, n.data(), n.size(), dh_result.data(), 32)
!= -1;
}
@ -102,7 +102,7 @@ namespace llarp
if (dh(dh_result.data(), pk, sk, pk, sk))
{
return crypto_generichash_blake2b(shared, 32, nonce, 32, dh_result.data(), 32) != -1;
return crypto_generichash_blake2b(shared, 32, nonce, 24, dh_result.data(), 32) != -1;
}
llarp::LogWarn("crypto::dh_server - dh failed");
@ -143,7 +143,7 @@ namespace llarp
}
bool
crypto::xchacha20(uint8_t* buf, size_t size, const SharedSecret& k, const TunnelNonce& n)
crypto::xchacha20(uint8_t* buf, size_t size, const SharedSecret& k, const SymmNonce& n)
{
return xchacha20(buf, size, n.data(), k.data());
}
@ -155,13 +155,13 @@ namespace llarp
}
// do a round of chacha for and return the nonce xor the given xor_factor
TunnelNonce
SymmNonce
crypto::onion(
unsigned char* buf,
size_t size,
const SharedSecret& k,
const TunnelNonce& nonce,
const ShortHash& xor_factor)
const SymmNonce& nonce,
const SymmNonce& xor_factor)
{
if (!crypto::xchacha20(buf, size, k, nonce))
throw std::runtime_error{"chacha failed during onion step"};
@ -171,14 +171,14 @@ namespace llarp
bool
crypto::dh_client(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const SymmNonce& n)
{
return dh_client_priv(shared, pk, sk, n);
}
/// path dh relay side
bool
crypto::dh_server(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const SymmNonce& n)
{
return dh_server_priv(shared, pk, sk, n);
}
@ -192,20 +192,6 @@ namespace llarp
{
return dh_server_priv(shared_secret, other_pk, local_pk, nonce);
}
/// transport dh client side
bool
crypto::transport_dh_client(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
return dh_client_priv(shared, pk, sk, n);
}
/// transport dh server side
bool
crypto::transport_dh_server(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
return dh_server_priv(shared, pk, sk, n);
}
bool
crypto::shorthash(ShortHash& result, uint8_t* buf, size_t size)

@ -23,36 +23,30 @@ namespace llarp
/// xchacha symmetric cipher
bool
xchacha20(uint8_t*, size_t size, const SharedSecret&, const TunnelNonce&);
xchacha20(uint8_t*, size_t size, const SharedSecret&, const SymmNonce&);
bool
xchacha20(uint8_t*, size_t size, const uint8_t*, const uint8_t*);
TunnelNonce
SymmNonce
onion(
unsigned char* buf,
size_t size,
const SharedSecret& k,
const TunnelNonce& nonce,
const ShortHash& xor_factor);
const SymmNonce& nonce,
const SymmNonce& xor_factor);
/// path dh creator's side
bool
dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
dh_client(SharedSecret&, const PubKey&, const SecretKey&, const SymmNonce&);
/// path dh relay side
bool
dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
dh_server(SharedSecret&, const PubKey&, const SecretKey&, const SymmNonce&);
bool
dh_server(
uint8_t* shared_secret,
const uint8_t* other_pk,
const uint8_t* local_pk,
const uint8_t* nonce);
/// transport dh client side
bool
transport_dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
/// transport dh server side
bool
transport_dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
/// blake2b 256 bit
bool
shorthash(ShortHash&, uint8_t*, size_t size);

@ -1,127 +0,0 @@
#include "encrypted_frame.hpp"
#include "crypto.hpp"
#include <llarp/util/logging.hpp>
namespace llarp
{
bool
EncryptedFrame::DoEncrypt(const SharedSecret& shared, bool noDH)
{
uint8_t* hash_ptr = data();
uint8_t* nonce_ptr = hash_ptr + SHORTHASHSIZE;
uint8_t* pubkey_ptr = nonce_ptr + TUNNONCESIZE;
uint8_t* body_ptr = pubkey_ptr + PUBKEYSIZE;
if (noDH)
{
crypto::randbytes(nonce_ptr, TUNNONCESIZE);
crypto::randbytes(pubkey_ptr, PUBKEYSIZE);
}
TunnelNonce nonce(nonce_ptr);
// encrypt body
if (!crypto::xchacha20(body_ptr, size() - EncryptedFrameOverheadSize, shared, nonce))
{
llarp::LogError("encrypt failed");
return false;
}
if (!crypto::hmac(hash_ptr, nonce_ptr, size() - SHORTHASHSIZE, shared))
{
llarp::LogError("Failed to generate message auth");
return false;
}
return true;
}
bool
EncryptedFrame::EncryptInPlace(const SecretKey& ourSecretKey, const PubKey& otherPubkey)
{
// format of frame is
// <32 bytes keyed hash of following data>
// <32 bytes nonce>
// <32 bytes pubkey>
// <N bytes encrypted payload>
//
byte_t* hash = data();
byte_t* noncePtr = hash + SHORTHASHSIZE;
byte_t* pubkey = noncePtr + TUNNONCESIZE;
SharedSecret shared;
// set our pubkey
memcpy(pubkey, ourSecretKey.toPublic().data(), PUBKEYSIZE);
// randomize nonce
crypto::randbytes(noncePtr, TUNNONCESIZE);
TunnelNonce nonce(noncePtr);
// derive shared key
if (!crypto::dh_client(shared, otherPubkey, ourSecretKey, nonce))
{
llarp::LogError("DH failed");
return false;
}
return DoEncrypt(shared, false);
}
bool
EncryptedFrame::DoDecrypt(const SharedSecret& shared)
{
uint8_t* hash_ptr = data();
uint8_t* nonce_ptr = hash_ptr + SHORTHASHSIZE;
uint8_t* body_ptr = hash_ptr + EncryptedFrameOverheadSize;
TunnelNonce nonce(nonce_ptr);
ShortHash digest;
if (!crypto::hmac(digest.data(), nonce_ptr, size() - SHORTHASHSIZE, shared))
{
llarp::LogError("Digest failed");
return false;
}
if (!std::equal(digest.begin(), digest.end(), hash_ptr))
{
llarp::LogError("message authentication failed");
return false;
}
if (!crypto::xchacha20(body_ptr, size() - EncryptedFrameOverheadSize, shared, nonce))
{
llarp::LogError("decrypt failed");
return false;
}
return true;
}
bool
EncryptedFrame::DecryptInPlace(const SecretKey& ourSecretKey)
{
// format of frame is
// <32 bytes keyed hash of following data>
// <32 bytes nonce>
// <32 bytes pubkey>
// <N bytes encrypted payload>
//
byte_t* noncePtr = data() + SHORTHASHSIZE;
TunnelNonce nonce(noncePtr);
PubKey otherPubkey(noncePtr + TUNNONCESIZE);
SharedSecret shared;
// use dh_server because we are not the creator of this message
if (!crypto::dh_server(shared, otherPubkey, ourSecretKey, nonce))
{
llarp::LogError("DH failed");
return false;
}
return DoDecrypt(shared);
}
} // namespace llarp

@ -1,87 +0,0 @@
#pragma once
#include "encrypted.hpp"
#include "types.hpp"
#include <llarp/util/buffer.hpp>
#include <llarp/util/mem.h>
#include <utility>
namespace llarp
{
static constexpr size_t EncryptedFrameOverheadSize = PUBKEYSIZE + TUNNONCESIZE + SHORTHASHSIZE;
static constexpr size_t EncryptedFrameBodySize = 128 * 6;
static constexpr size_t EncryptedFrameSize = EncryptedFrameOverheadSize + EncryptedFrameBodySize;
struct EncryptedFrame : public Encrypted<EncryptedFrameSize>
{
EncryptedFrame() : EncryptedFrame(EncryptedFrameBodySize)
{}
EncryptedFrame(size_t sz)
: Encrypted<EncryptedFrameSize>(
std::min(sz, EncryptedFrameBodySize) + EncryptedFrameOverheadSize)
{}
void
Resize(size_t sz)
{
if (sz <= EncryptedFrameSize)
{
_sz = sz;
UpdateBuffer();
}
}
bool
DoEncrypt(const SharedSecret& shared, bool noDH = false);
bool
DecryptInPlace(const SecretKey& seckey);
bool
DoDecrypt(const SharedSecret& shared);
bool
EncryptInPlace(const SecretKey& seckey, const PubKey& other);
};
template <typename User>
struct AsyncFrameDecrypter
{
using User_ptr = std::shared_ptr<User>;
using DecryptHandler = std::function<void(llarp_buffer_t*, User_ptr)>;
void
Decrypt(User_ptr user)
{
if (target.DecryptInPlace(seckey))
{
auto buf = target.Buffer();
buf->cur = buf->base + EncryptedFrameOverheadSize;
result(buf, user);
}
else
result(nullptr, user);
}
AsyncFrameDecrypter(const SecretKey& secretkey, DecryptHandler h)
: result(std::move(h)), seckey(secretkey)
{}
DecryptHandler result;
const SecretKey& seckey;
EncryptedFrame target;
using WorkFunc_t = std::function<void(void)>;
using WorkerFunction_t = std::function<void(WorkFunc_t)>;
void
AsyncDecrypt(const EncryptedFrame& frame, User_ptr u, WorkerFunction_t worker)
{
target = frame;
worker([this, u = std::move(u)]() mutable { Decrypt(std::move(u)); });
}
};
} // namespace llarp

@ -169,10 +169,6 @@ namespace llarp
/// PKE(result, publickey, secretkey, nonce)
using path_dh_func = bool (*)(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
/// TKE(result, publickey, secretkey, nonce)
using transport_dh_func =
bool (*)(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
/// SH(result, body)
using shorthash_func = bool (*)(ShortHash&, const llarp_buffer_t&);
} // namespace llarp

@ -1235,7 +1235,9 @@ namespace llarp
return;
}
// generate hash of hop key for nonce mutation
crypto::shorthash(hop->nonceXOR, hop->pathKey.data(), hop->pathKey.size());
ShortHash xor_hash;
crypto::shorthash(xor_hash, hop->pathKey.data(), hop->pathKey.size());
hop->nonceXOR = xor_hash.data(); // nonceXOR is 24 bytes, ShortHash is 32; this will truncate
// set and check path lifetime
hop->lifetime = 1ms * lifetime;
@ -1641,7 +1643,7 @@ namespace llarp
try
{
oxenc::bt_dict_consumer btdc{m.body()};
auto nonce = TunnelNonce{btdc.require<ustring_view>("NONCE").data()};
auto nonce = SymmNonce{btdc.require<ustring_view>("NONCE").data()};
auto path_id_str = btdc.require<ustring_view>("PATHID");
auto payload = btdc.require<std::string>("PAYLOAD");
auto path_id = PathID_t{path_id_str.data()};
@ -1675,7 +1677,7 @@ namespace llarp
return;
oxenc::bt_dict_consumer resp_btdc{response.body()};
auto nonce = TunnelNonce{resp_btdc.require<ustring_view>("NONCE").data()};
auto nonce = SymmNonce{resp_btdc.require<ustring_view>("NONCE").data()};
auto payload = resp_btdc.require<std::string>("PAYLOAD");
auto resp_payload = hop->onion_and_payload(payload, path_id, nonce);
prev_message.respond(std::move(resp_payload), false);

@ -29,7 +29,9 @@ namespace llarp
throw std::runtime_error{std::move(err)};
}
// generate nonceXOR value self->hop->pathKey
crypto::shorthash(hop.nonceXOR, hop.shared.data(), hop.shared.size());
ShortHash hash;
crypto::shorthash(hash, hop.shared.data(), hop.shared.size());
hop.nonceXOR = hash.data(); // nonceXOR is 24 bytes, ShortHash is 32; this will truncate
hop.upstream = nextHop;
}
@ -56,7 +58,7 @@ namespace llarp
crypto::encryption_keygen(framekey);
SharedSecret shared;
TunnelNonce outer_nonce;
SymmNonce outer_nonce;
outer_nonce.Randomize();
// derive (outer) shared key

@ -1,123 +0,0 @@
#include "relay.hpp"
#include <llarp/path/path_context.hpp>
#include <llarp/router/router.hpp>
#include <llarp/util/bencode.hpp>
namespace llarp
{
void
RelayUpstreamMessage::clear()
{
pathid.Zero();
enc.Clear();
nonce.Zero();
version = 0;
}
std::string
RelayUpstreamMessage::bt_encode() const
{
oxenc::bt_dict_producer btdp;
try
{
btdp.append("a", "u");
btdp.append("p", pathid.ToView());
btdp.append("v", llarp::constants::proto_version);
btdp.append("x", std::string_view{reinterpret_cast<const char*>(enc.data()), enc.size()});
btdp.append("y", std::string_view{reinterpret_cast<const char*>(nonce.data()), nonce.size()});
}
catch (...)
{
log::critical(link_cat, "Error: RelayUpstreamMessage failed to bt encode contents!");
}
return std::move(btdp).str();
}
bool
RelayUpstreamMessage::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictEntry("p", pathid, read, key, buf))
return false;
if (!BEncodeMaybeVerifyVersion("v", version, llarp::constants::proto_version, read, key, buf))
return false;
if (!BEncodeMaybeReadDictEntry("x", enc, read, key, buf))
return false;
if (!BEncodeMaybeReadDictEntry("y", nonce, read, key, buf))
return false;
return read;
}
bool
RelayUpstreamMessage::handle_message(Router* r) const
{
path::HopHandler_ptr path = r->path_context().GetPath(pathid);
path = path ? path : r->path_context().GetTransitHop(conn->remote_rc.router_id(), pathid);
if (path)
{
return path->HandleUpstream(llarp_buffer_t(enc), nonce, r);
}
return false;
}
void
RelayDownstreamMessage::clear()
{
pathid.Zero();
enc.Clear();
nonce.Zero();
version = 0;
}
std::string
RelayDownstreamMessage::bt_encode() const
{
oxenc::bt_dict_producer btdp;
try
{
btdp.append("a", "d");
btdp.append("p", pathid.ToView());
btdp.append("v", llarp::constants::proto_version);
btdp.append("x", std::string_view{reinterpret_cast<const char*>(enc.data()), enc.size()});
btdp.append("y", std::string_view{reinterpret_cast<const char*>(nonce.data()), nonce.size()});
}
catch (...)
{
log::critical(link_cat, "Error: RelayDownstreamMessage failed to bt encode contents!");
}
return std::move(btdp).str();
}
bool
RelayDownstreamMessage::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictEntry("p", pathid, read, key, buf))
return false;
if (!BEncodeMaybeVerifyVersion("v", version, llarp::constants::proto_version, read, key, buf))
return false;
if (!BEncodeMaybeReadDictEntry("x", enc, read, key, buf))
return false;
if (!BEncodeMaybeReadDictEntry("y", nonce, read, key, buf))
return false;
return read;
}
bool
RelayDownstreamMessage::handle_message(Router* r) const
{
path::HopHandler_ptr path = r->path_context().GetPath(pathid);
path = path ? path : r->path_context().GetTransitHop(conn->remote_rc.router_id(), pathid);
if (path)
{
return path->HandleDownstream(llarp_buffer_t(enc), nonce, r);
}
llarp::LogWarn("no path for downstream message id=", pathid);
return false;
}
} // namespace llarp

@ -1,75 +0,0 @@
#pragma once
#include "link_message.hpp"
#include <llarp/crypto/encrypted.hpp>
#include <llarp/crypto/types.hpp>
#include <llarp/path/path_types.hpp>
#include <vector>
namespace llarp
{
/*
Data messages to be sent via quic datagrams
*/
struct RelayUpstreamMessage final : public AbstractLinkMessage
{
Encrypted<MAX_LINK_MSG_SIZE - 128> enc;
TunnelNonce nonce;
bool
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
std::string
bt_encode() const override;
bool
handle_message(Router* router) const override;
void
clear() override;
const char*
name() const override
{
return "RelayUpstream";
}
uint16_t
priority() const override
{
return 0;
}
};
struct RelayDownstreamMessage final : public AbstractLinkMessage
{
Encrypted<MAX_LINK_MSG_SIZE - 128> enc;
TunnelNonce nonce;
bool
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
std::string
bt_encode() const override;
bool
handle_message(Router* router) const override;
void
clear() override;
const char*
name() const override
{
return "RelayDownstream";
}
uint16_t
priority() const override
{
return 0;
}
};
} // namespace llarp

@ -6,7 +6,7 @@ namespace llarp::path
{
std::string
make_onion_payload(
const TunnelNonce& nonce, const PathID_t& path_id, const std::string_view& inner_payload)
const SymmNonce& nonce, const PathID_t& path_id, const std::string_view& inner_payload)
{
return make_onion_payload(
nonce,
@ -17,7 +17,7 @@ namespace llarp::path
std::string
make_onion_payload(
const TunnelNonce& nonce, const PathID_t& path_id, const ustring_view& inner_payload)
const SymmNonce& nonce, const PathID_t& path_id, const ustring_view& inner_payload)
{
oxenc::bt_dict_producer next_dict;
next_dict.append("NONCE", nonce.ToView());
@ -26,35 +26,4 @@ namespace llarp::path
return std::move(next_dict).str();
}
// handle data in upstream direction
bool
AbstractHopHandler::HandleUpstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router* r)
{
auto& pkt = m_UpstreamQueue.emplace_back();
pkt.first.resize(X.sz);
std::copy_n(X.base, X.sz, pkt.first.begin());
pkt.second = Y;
r->TriggerPump();
return true;
}
// handle data in downstream direction
bool
AbstractHopHandler::HandleDownstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router* r)
{
auto& pkt = m_DownstreamQueue.emplace_back();
pkt.first.resize(X.sz);
std::copy_n(X.base, X.sz, pkt.first.begin());
pkt.second = Y;
r->TriggerPump();
return true;
}
void
AbstractHopHandler::DecayFilters(llarp_time_t now)
{
m_UpstreamReplayFilter.Decay(now);
m_DownstreamReplayFilter.Decay(now);
}
} // namespace llarp::path

@ -1,8 +1,7 @@
#pragma once
#include <llarp/crypto/encrypted_frame.hpp>
#include "path_types.hpp"
#include <llarp/crypto/types.hpp>
#include <llarp/messages/relay.hpp>
#include <llarp/util/decaying_hashset.hpp>
#include <llarp/util/types.hpp>
@ -25,24 +24,18 @@ namespace llarp
std::string
make_onion_payload(
const TunnelNonce& nonce, const PathID_t& path_id, const std::string_view& inner_payload);
const SymmNonce& nonce, const PathID_t& path_id, const std::string_view& inner_payload);
std::string
make_onion_payload(
const TunnelNonce& nonce, const PathID_t& path_id, const ustring_view& inner_payload);
const SymmNonce& nonce, const PathID_t& path_id, const ustring_view& inner_payload);
struct AbstractHopHandler
{
using TrafficEvent_t = std::pair<std::vector<byte_t>, TunnelNonce>;
using TrafficQueue_t = std::list<TrafficEvent_t>;
virtual ~AbstractHopHandler() = default;
virtual PathID_t
RXID() const = 0;
void
DecayFilters(llarp_time_t now);
virtual bool
Expired(llarp_time_t now) const = 0;
@ -61,17 +54,6 @@ namespace llarp
send_path_control_message(
std::string method, std::string body, std::function<void(std::string)> func) = 0;
/// send routing message and increment sequence number
virtual bool
SendRoutingMessage(std::string payload, Router* r) = 0;
// handle data in upstream direction
virtual bool
HandleUpstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router*);
// handle data in downstream direction
virtual bool
HandleDownstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router*);
/// return timestamp last remote activity happened at
virtual llarp_time_t
LastRemoteActivityAt() const = 0;
@ -82,29 +64,8 @@ namespace llarp
return m_SequenceNum++;
}
virtual void
FlushUpstream(Router* r) = 0;
virtual void
FlushDownstream(Router* r) = 0;
protected:
uint64_t m_SequenceNum = 0;
TrafficQueue_t m_UpstreamQueue;
TrafficQueue_t m_DownstreamQueue;
util::DecayingHashSet<TunnelNonce> m_UpstreamReplayFilter;
util::DecayingHashSet<TunnelNonce> m_DownstreamReplayFilter;
virtual void
UpstreamWork(TrafficQueue_t queue, Router* r) = 0;
virtual void
DownstreamWork(TrafficQueue_t queue, Router* r) = 0;
virtual void
HandleAllUpstream(std::vector<RelayUpstreamMessage> msgs, Router* r) = 0;
virtual void
HandleAllDownstream(std::vector<RelayDownstreamMessage> msgs, Router* r) = 0;
};
using HopHandler_ptr = std::shared_ptr<AbstractHopHandler>;

@ -99,7 +99,7 @@ namespace llarp::path
auto payload = std::move(btdp).str();
// TODO: old impl padded messages if smaller than a certain size; do we still want to?
TunnelNonce nonce;
SymmNonce nonce;
nonce.Randomize();
// chacha and mutate nonce for each hop
@ -131,13 +131,13 @@ namespace llarp::path
return;
}
TunnelNonce nonce{};
SymmNonce nonce{};
std::string payload;
try
{
oxenc::bt_dict_consumer btdc{m.body()};
auto nonce = TunnelNonce{btdc.require<ustring_view>("NONCE").data()};
auto nonce = SymmNonce{btdc.require<ustring_view>("NONCE").data()};
auto payload = btdc.require<std::string>("PAYLOAD");
}
catch (const std::exception& e)
@ -164,22 +164,6 @@ namespace llarp::path
});
}
bool
Path::HandleUpstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router* r)
{
if (not m_UpstreamReplayFilter.Insert(Y))
return false;
return AbstractHopHandler::HandleUpstream(X, Y, r);
}
bool
Path::HandleDownstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router* r)
{
if (not m_DownstreamReplayFilter.Insert(Y))
return false;
return AbstractHopHandler::HandleDownstream(X, Y, r);
}
RouterID
Path::Endpoint() const
{
@ -319,8 +303,6 @@ namespace llarp::path
{"ready", IsReady()},
{"txRateCurrent", m_LastTXRate},
{"rxRateCurrent", m_LastRXRate},
{"replayTX", m_UpstreamReplayFilter.Size()},
{"replayRX", m_DownstreamReplayFilter.Size()},
{"hasExit", SupportsAnyRoles(ePathRoleExit)}};
std::vector<util::StatusObject> hopsObj;
@ -454,73 +436,6 @@ namespace llarp::path
}
}
void
Path::HandleAllUpstream(std::vector<RelayUpstreamMessage> msgs, Router* r)
{
for (const auto& msg : msgs)
{
if (r->send_data_message(upstream(), msg.bt_encode()))
{
m_TXRate += msg.enc.size();
}
else
{
LogDebug("failed to send upstream to ", upstream());
}
}
r->TriggerPump();
}
void
Path::UpstreamWork(TrafficQueue_t msgs, Router* r)
{
std::vector<RelayUpstreamMessage> sendmsgs(msgs.size());
size_t idx = 0;
for (auto& ev : msgs)
{
TunnelNonce n = ev.second;
uint8_t* buf = ev.first.data();
size_t sz = ev.first.size();
for (const auto& hop : hops)
{
crypto::xchacha20(buf, sz, hop.shared, n);
n ^= hop.nonceXOR;
}
auto& msg = sendmsgs[idx];
std::memcpy(msg.enc.data(), buf, sz);
msg.nonce = ev.second;
msg.pathid = TXID();
++idx;
}
r->loop()->call([self = shared_from_this(), data = std::move(sendmsgs), r]() mutable {
self->HandleAllUpstream(std::move(data), r);
});
}
void
Path::FlushUpstream(Router* r)
{
if (not m_UpstreamQueue.empty())
{
r->queue_work([self = shared_from_this(),
data = std::exchange(m_UpstreamQueue, {}),
r]() mutable { self->UpstreamWork(std::move(data), r); });
}
}
void
Path::FlushDownstream(Router* r)
{
if (not m_DownstreamQueue.empty())
{
r->queue_work([self = shared_from_this(),
data = std::exchange(m_DownstreamQueue, {}),
r]() mutable { self->DownstreamWork(std::move(data), r); });
}
}
/// how long we wait for a path to become active again after it times out
constexpr auto PathReanimationTimeout = 45s;
@ -548,47 +463,6 @@ namespace llarp::path
return fmt::format("TX={} RX={}", TXID(), RXID());
}
void
Path::DownstreamWork(TrafficQueue_t msgs, Router* r)
{
std::vector<RelayDownstreamMessage> sendMsgs(msgs.size());
size_t idx = 0;
for (auto& ev : msgs)
{
sendMsgs[idx].nonce = ev.second;
uint8_t* buf = ev.first.data();
size_t sz = ev.first.size();
for (const auto& hop : hops)
{
sendMsgs[idx].nonce ^= hop.nonceXOR;
crypto::xchacha20(buf, sz, hop.shared, sendMsgs[idx].nonce);
}
std::memcpy(sendMsgs[idx].enc.data(), buf, sz);
++idx;
}
r->loop()->call([self = shared_from_this(), msgs = std::move(sendMsgs), r]() mutable {
self->HandleAllDownstream(std::move(msgs), r);
});
}
void
Path::HandleAllDownstream(std::vector<RelayDownstreamMessage> msgs, Router* /* r */)
{
for (const auto& msg : msgs)
{
const llarp_buffer_t buf{msg.enc};
m_RXRate += buf.sz;
// if (HandleRoutingMessage(buf, r))
// {
// r->TriggerPump();
// m_LastRecvMessage = r->now();
// }
}
}
/** Note: this is one of two places where AbstractRoutingMessage::bt_encode() is called, the
other of which is llarp/path/transit_hop.cpp in TransitHop::SendRoutingMessage(). For now,
we will default to the override of ::bt_encode() that returns an std::string. The role that
@ -604,6 +478,7 @@ namespace llarp::path
functions it calls and so on) will need to be modified to take an std::string that we can
std::move around.
*/
/* TODO: replace this with sending an onion-ed data message
bool
Path::SendRoutingMessage(std::string payload, Router*)
{
@ -627,6 +502,7 @@ namespace llarp::path
return true;
}
*/
template <typename Samples_t>
static llarp_time_t

@ -5,9 +5,7 @@
#include "pathset.hpp"
#include <llarp/constants/path.hpp>
#include <llarp/crypto/encrypted_frame.hpp>
#include <llarp/crypto/types.hpp>
#include <llarp/messages/relay.hpp>
#include <llarp/router_id.hpp>
#include <llarp/service/intro.hpp>
#include <llarp/util/aligned.hpp>
@ -122,14 +120,6 @@ namespace llarp
return _status;
}
// handle data in upstream direction
bool
HandleUpstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router*) override;
// handle data in downstream direction
bool
HandleDownstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router*) override;
const std::string&
ShortName() const;
@ -223,9 +213,6 @@ namespace llarp
std::string body,
std::function<void(std::string)> func = nullptr) override;
bool
SendRoutingMessage(std::string payload, Router* r) override;
bool
IsReady() const;
@ -252,25 +239,6 @@ namespace llarp
std::string
name() const;
void
FlushUpstream(Router* r) override;
void
FlushDownstream(Router* r) override;
protected:
void
UpstreamWork(TrafficQueue_t queue, Router* r) override;
void
DownstreamWork(TrafficQueue_t queue, Router* r) override;
void
HandleAllUpstream(std::vector<RelayUpstreamMessage> msgs, Router* r) override;
void
HandleAllDownstream(std::vector<RelayDownstreamMessage> msgs, Router* r) override;
private:
bool
SendLatencyMessage(Router* r);

@ -203,7 +203,6 @@ namespace llarp::path
}
else
{
itr->second->DecayFilters(now);
++itr;
}
}
@ -217,7 +216,6 @@ namespace llarp::path
}
else
{
itr->second->DecayFilters(now);
++itr;
}
}

@ -5,7 +5,6 @@
#include "pathset.hpp"
#include "transit_hop.hpp"
#include <llarp/crypto/encrypted_frame.hpp>
#include <llarp/ev/ev.hpp>
#include <llarp/net/ip_address.hpp>
#include <llarp/util/compare_ptr.hpp>

@ -32,11 +32,11 @@ namespace llarp
/// shared secret at this hop
SharedSecret shared;
/// hash of shared secret used for nonce mutation
ShortHash nonceXOR;
SymmNonce nonceXOR;
/// next hop's router id
RouterID upstream;
/// nonce for key exchange
TunnelNonce nonce;
SymmNonce nonce;
// lifetime
llarp_time_t lifetime = DEFAULT_LIFETIME;

@ -93,7 +93,9 @@ namespace llarp
throw std::runtime_error{std::move(err)};
}
// generate nonceXOR value self->hop->pathKey
crypto::shorthash(hop.nonceXOR, hop.shared.data(), hop.shared.size());
ShortHash hash;
crypto::shorthash(hash, hop.shared.data(), hop.shared.size());
hop.nonceXOR = hash.data(); // nonceXOR is 24 bytes, ShortHash is 32; this will truncate
hop.upstream = nextHop;
}
@ -120,7 +122,7 @@ namespace llarp
crypto::encryption_keygen(framekey);
SharedSecret shared;
TunnelNonce outer_nonce;
SymmNonce outer_nonce;
outer_nonce.Randomize();
// derive (outer) shared key

@ -1,7 +1,8 @@
#include "pathset.hpp"
#include "path.hpp"
#include <llarp/crypto/crypto.hpp>
namespace llarp::path
{
PathSet::PathSet(size_t num) : numDesiredPaths(num)
@ -441,16 +442,4 @@ namespace llarp::path
return chosen;
}
void
PathSet::UpstreamFlush(Router* r)
{
ForEachPath([r](const Path_ptr& p) { p->FlushUpstream(r); });
}
void
PathSet::DownstreamFlush(Router* r)
{
ForEachPath([r](const Path_ptr& p) { p->FlushDownstream(r); });
}
} // namespace llarp::path

@ -14,17 +14,11 @@ namespace llarp::path
TransitHop::TransitHop()
: AbstractHopHandler{}
, m_UpstreamGather{TRANSIT_HOP_QUEUE_SIZE}
, m_DownstreamGather{TRANSIT_HOP_QUEUE_SIZE}
{
m_UpstreamGather.enable();
m_DownstreamGather.enable();
m_UpstreamWorkCounter = 0;
m_DownstreamWorkCounter = 0;
}
void
TransitHop::onion(ustring& data, TunnelNonce& nonce, bool randomize) const
TransitHop::onion(ustring& data, SymmNonce& nonce, bool randomize) const
{
if (randomize)
nonce.Randomize();
@ -32,7 +26,7 @@ namespace llarp::path
}
void
TransitHop::onion(std::string& data, TunnelNonce& nonce, bool randomize) const
TransitHop::onion(std::string& data, SymmNonce& nonce, bool randomize) const
{
if (randomize)
nonce.Randomize();
@ -42,9 +36,9 @@ namespace llarp::path
std::string
TransitHop::onion_and_payload(
std::string& payload, PathID_t next_id, std::optional<TunnelNonce> nonce) const
std::string& payload, PathID_t next_id, std::optional<SymmNonce> nonce) const
{
TunnelNonce n;
SymmNonce n;
auto& nref = nonce ? *nonce : n;
onion(payload, nref, not nonce);
@ -88,6 +82,7 @@ namespace llarp::path
functions it calls and so on) will need to be modified to take an std::string that we can
std::move around.
*/
/* TODO: replace this with layer of onion + send data message
bool
TransitHop::SendRoutingMessage(std::string payload, Router* r)
{
@ -110,157 +105,7 @@ namespace llarp::path
return true;
}
void
TransitHop::DownstreamWork(TrafficQueue_t msgs, Router* r)
{
auto flushIt = [self = shared_from_this(), r]() {
std::vector<RelayDownstreamMessage> msgs;
while (auto maybe = self->m_DownstreamGather.tryPopFront())
{
msgs.push_back(*maybe);
}
self->HandleAllDownstream(std::move(msgs), r);
};
for (auto& ev : msgs)
{
RelayDownstreamMessage msg;
// const llarp_buffer_t buf(ev.first);
uint8_t* buf = ev.first.data();
size_t sz = ev.first.size();
msg.pathid = info.rxID;
msg.nonce = ev.second ^ nonceXOR;
crypto::xchacha20(buf, sz, pathKey, ev.second);
std::memcpy(msg.enc.data(), buf, sz);
llarp::LogDebug(
"relay ",
msg.enc.size(),
" bytes downstream from ",
info.upstream,
" to ",
info.downstream);
if (m_DownstreamGather.full())
{
r->loop()->call(flushIt);
}
if (m_DownstreamGather.enabled())
m_DownstreamGather.pushBack(msg);
}
r->loop()->call(flushIt);
}
void
TransitHop::UpstreamWork(TrafficQueue_t msgs, Router* r)
{
for (auto& ev : msgs)
{
RelayUpstreamMessage msg;
uint8_t* buf = ev.first.data();
size_t sz = ev.first.size();
crypto::xchacha20(buf, sz, pathKey, ev.second);
msg.pathid = info.txID;
msg.nonce = ev.second ^ nonceXOR;
std::memcpy(msg.enc.data(), buf, sz);
if (m_UpstreamGather.tryPushBack(msg) != thread::QueueReturn::Success)
break;
}
// Flush it:
r->loop()->call([self = shared_from_this(), r] {
std::vector<RelayUpstreamMessage> msgs;
while (auto maybe = self->m_UpstreamGather.tryPopFront())
{
msgs.push_back(*maybe);
}
self->HandleAllUpstream(std::move(msgs), r);
});
}
void
TransitHop::HandleAllUpstream(std::vector<RelayUpstreamMessage> msgs, Router* r)
{
if (IsEndpoint(r->pubkey()))
{
for (const auto& msg : msgs)
{
const llarp_buffer_t buf(msg.enc);
if (!r->ParseRoutingMessageBuffer(buf, *this, info.rxID))
{
LogWarn("invalid upstream data on endpoint ", info);
}
m_LastActivity = r->now();
}
FlushDownstream(r);
for (const auto& other : m_FlushOthers)
{
other->FlushDownstream(r);
}
m_FlushOthers.clear();
}
else
{
for (const auto& msg : msgs)
{
llarp::LogDebug(
"relay ",
msg.enc.size(),
" bytes upstream from ",
info.downstream,
" to ",
info.upstream);
r->send_data_message(info.upstream, msg.bt_encode());
}
}
r->TriggerPump();
}
void
TransitHop::HandleAllDownstream(std::vector<RelayDownstreamMessage> msgs, Router* r)
{
for (const auto& msg : msgs)
{
log::debug(
path_cat,
"Relaying {} bytes downstream from {} to {}",
msg.enc.size(),
info.upstream,
info.downstream);
// TODO: is this right?
r->send_data_message(info.downstream, msg.bt_encode());
}
r->TriggerPump();
}
void
TransitHop::FlushUpstream(Router* r)
{
if (not m_UpstreamQueue.empty())
{
r->queue_work([self = shared_from_this(),
data = std::exchange(m_UpstreamQueue, {}),
r]() mutable { self->UpstreamWork(std::move(data), r); });
}
}
void
TransitHop::FlushDownstream(Router* r)
{
if (not m_DownstreamQueue.empty())
{
r->queue_work([self = shared_from_this(),
data = std::exchange(m_DownstreamQueue, {}),
r]() mutable { self->DownstreamWork(std::move(data), r); });
}
}
*/
std::string
TransitHop::ToString() const
@ -272,8 +117,7 @@ namespace llarp::path
void
TransitHop::Stop()
{
m_UpstreamGather.disable();
m_DownstreamGather.disable();
// TODO: still need this concept?
}
void

@ -55,7 +55,7 @@ namespace llarp
TransitHopInfo info;
SharedSecret pathKey;
ShortHash nonceXOR;
SymmNonce nonceXOR;
llarp_time_t started = 0s;
// 10 minutes default
llarp_time_t lifetime = DEFAULT_LIFETIME;
@ -68,16 +68,16 @@ namespace llarp
// Does xchacha20 on `data` in-place with `nonce` and `pathKey`, then
// mutates `nonce` = `nonce` ^ `nonceXOR` in-place.
void
onion(ustring& data, TunnelNonce& nonce, bool randomize = false) const;
onion(ustring& data, SymmNonce& nonce, bool randomize = false) const;
void
onion(std::string& data, TunnelNonce& nonce, bool randomize = false) const;
onion(std::string& data, SymmNonce& nonce, bool randomize = false) const;
std::string
onion_and_payload(
std::string& payload,
PathID_t next_id,
std::optional<TunnelNonce> nonce = std::nullopt) const;
std::optional<SymmNonce> nonce = std::nullopt) const;
PathID_t
RXID() const override
@ -142,41 +142,12 @@ namespace llarp
std::string body,
std::function<void(std::string)> func) override;
// send routing message when end of path
bool
SendRoutingMessage(std::string payload, Router* r) override;
void
FlushUpstream(Router* r) override;
void
FlushDownstream(Router* r) override;
void
QueueDestroySelf(Router* r);
protected:
void
UpstreamWork(TrafficQueue_t queue, Router* r) override;
void
DownstreamWork(TrafficQueue_t queue, Router* r) override;
void
HandleAllUpstream(std::vector<RelayUpstreamMessage> msgs, Router* r) override;
void
HandleAllDownstream(std::vector<RelayDownstreamMessage> msgs, Router* r) override;
private:
void
SetSelfDestruct();
std::set<std::shared_ptr<TransitHop>, ComparePtr<std::shared_ptr<TransitHop>>> m_FlushOthers;
thread::Queue<RelayUpstreamMessage> m_UpstreamGather;
thread::Queue<RelayDownstreamMessage> m_DownstreamGather;
std::atomic<uint32_t> m_UpstreamWorkCounter;
std::atomic<uint32_t> m_DownstreamWorkCounter;
};
} // namespace path

@ -43,6 +43,9 @@ namespace llarp::service
AsyncKeyExchange::Encrypt(
std::shared_ptr<AsyncKeyExchange> self, std::shared_ptr<ProtocolFrameMessage> frame)
{
(void)self;
(void)frame;
/* TODO: client<->client session ("conversation"/"convo") key exchange
// derive ntru session key component
SharedSecret secret;
crypto::pqe_encrypt(frame->cipher, secret, self->introPubKey);
@ -73,5 +76,6 @@ namespace llarp::service
{
LogError("failed to encrypt and sign");
}
*/
}
} // namespace llarp::service

@ -1472,7 +1472,7 @@ namespace llarp::service
queue.pop();
}
auto r = router();
// auto r = router();
// TODO: locking on this container
// for (const auto& [addr, outctx] : _state->remote_sessions)
@ -1492,8 +1492,6 @@ namespace llarp::service
// if (item.second->SendRoutingMessage(*item.first, r))
// ConvoTagTX(item.first->protocol_frame_msg.convo_tag);
// }
UpstreamFlush(r);
}
std::optional<ConvoTag>

@ -167,7 +167,7 @@ namespace llarp::service
return std::nullopt;
IntroSet i{other_i};
encrypted.nounce.Randomize();
encrypted.nonce.Randomize();
// set timestamp
// TODO: round to nearest 1000 ms
i.time_signed = now;
@ -180,7 +180,7 @@ namespace llarp::service
auto bte = i.bt_encode();
const SharedSecret k{i.address_keys.Addr()};
crypto::xchacha20(reinterpret_cast<uint8_t*>(bte.data()), bte.size(), k, encrypted.nounce);
crypto::xchacha20(reinterpret_cast<uint8_t*>(bte.data()), bte.size(), k, encrypted.nonce);
std::memcpy(encrypted.introsetPayload.data(), bte.data(), bte.size());

@ -14,7 +14,7 @@ namespace llarp::service
std::string s)
: signedAt{signed_at}
, introsetPayload{reinterpret_cast<uint8_t*>(enc_payload.data()), enc_payload.size()}
, nounce{reinterpret_cast<uint8_t*>(nonce.data())}
, nonce{reinterpret_cast<uint8_t*>(nonce.data())}
{
derivedSigningKey = PubKey::from_string(signing_key);
sig.from_string(std::move(s));
@ -27,7 +27,7 @@ namespace llarp::service
oxenc::bt_dict_consumer btdc{bt_payload};
derivedSigningKey = PubKey::from_string(btdc.require<std::string>("d"));
nounce.from_string(btdc.require<std::string>("n"));
nonce.from_string(btdc.require<std::string>("n"));
signedAt = std::chrono::milliseconds{btdc.require<uint64_t>("s")};
introsetPayload = btdc.require<ustring>("x");
sig.from_string(btdc.require<std::string>("z"));
@ -54,7 +54,7 @@ namespace llarp::service
try
{
btdp.append("d", derivedSigningKey.ToView());
btdp.append("n", nounce.ToView());
btdp.append("n", nonce.ToView());
btdp.append("s", signedAt.count());
btdp.append(
"x",
@ -88,7 +88,7 @@ namespace llarp::service
if (not BEncodeMaybeReadDictEntry("d", derivedSigningKey, read, key, buf))
return false;
if (not BEncodeMaybeReadDictEntry("n", nounce, read, key, buf))
if (not BEncodeMaybeReadDictEntry("n", nonce, read, key, buf))
return false;
if (not BEncodeMaybeReadDictInt("s", signedAt, read, key, buf))
@ -111,7 +111,7 @@ namespace llarp::service
return fmt::format(
"[EncIntroSet d={} n={} s={} x=[{} bytes] z={}]",
derivedSigningKey,
nounce,
nonce,
signedAt.count(),
introsetPayload.size(),
sig);
@ -124,7 +124,7 @@ namespace llarp::service
std::string payload{
reinterpret_cast<const char*>(introsetPayload.data()), introsetPayload.size()};
crypto::xchacha20(reinterpret_cast<uint8_t*>(payload.data()), payload.size(), k, nounce);
crypto::xchacha20(reinterpret_cast<uint8_t*>(payload.data()), payload.size(), k, nonce);
return IntroSet{payload};
}

@ -138,7 +138,7 @@ namespace llarp::service
PubKey derivedSigningKey;
llarp_time_t signedAt = 0s;
ustring introsetPayload;
TunnelNonce nounce;
SymmNonce nonce;
std::optional<Tag> topic;
Signature sig;
@ -203,8 +203,8 @@ namespace llarp::service
inline bool
operator==(const EncryptedIntroSet& lhs, const EncryptedIntroSet& rhs)
{
return std::tie(lhs.signedAt, lhs.derivedSigningKey, lhs.nounce, lhs.sig)
== std::tie(rhs.signedAt, rhs.derivedSigningKey, rhs.nounce, rhs.sig);
return std::tie(lhs.signedAt, lhs.derivedSigningKey, lhs.nonce, lhs.sig)
== std::tie(rhs.signedAt, rhs.derivedSigningKey, rhs.nonce, rhs.sig);
}
inline bool

@ -302,8 +302,7 @@ namespace llarp::service
// PKE (A, B, N)
SharedSecret shared_secret;
if (!self->m_LocalIdentity.KeyExchange(
crypto::dh_server, shared_secret, self->msg->sender, self->frame.nonce))
if (!crypto::dh_server(shared_secret, self->msg->sender.EncryptionPublicKey(), self->m_LocalIdentity.enckey, self->frame.nonce))
{
LogError("x25519 key exchange failed");
Dump<MAX_PROTOCOL_MESSAGE_SIZE>(self->frame);

@ -75,7 +75,7 @@ namespace llarp
PQCipherBlock cipher;
Encrypted<2048> enc;
uint64_t flag; // set to indicate in plaintext a nack, aka "dont try again"
KeyExchangeNonce nonce;
SymmNonce nonce;
Signature sig;
PathID_t path_id;
service::ConvoTag convo_tag;

Loading…
Cancel
Save