crypto and message encoding

- libsodium calls streamlined and moved away from stupid typedefs
- buffer handling taken away from buffer_t and towards ustrings and strings
- lots of stuff deleted
- team is working well
- re-implementing message handling in proper link_manager methods
pull/2204/head
dr7ana 9 months ago
parent ae319091d6
commit d9ead7d0f6

@ -1,7 +1,6 @@
include(Version)
target_sources(lokinet-cryptography PRIVATE
crypto/crypto_libsodium.cpp
crypto/crypto.cpp
crypto/encrypted_frame.cpp
crypto/types.cpp
@ -150,7 +149,6 @@ add_library(lokinet-consensus
# lokinet-dht holds all logic related to interacting with and participating in the DHT hashring
add_library(lokinet-dht
STATIC
dht/context.cpp
dht/dht.cpp
dht/explorenetworkjob.cpp
dht/localtaglookup.cpp
@ -200,7 +198,6 @@ add_library(lokinet-layer-link
STATIC
link/connection.cpp
link/link_manager.cpp
messages/dht_immediate.cpp
messages/link_intro.cpp
)

@ -1343,13 +1343,10 @@ namespace llarp
std::set<IPRange> seenRanges;
for (const auto& hop : rcs)
{
for (const auto& addr : hop.addrs)
const auto network_addr = net::In6ToHUInt(hop.addr.in6().sin6_addr) & netmask;
if (auto [it, inserted] = seenRanges.emplace(network_addr, netmask); not inserted)
{
const auto network_addr = net::In6ToHUInt(addr.ip) & netmask;
if (auto [it, inserted] = seenRanges.emplace(network_addr, netmask); not inserted)
{
return false;
}
return false;
}
}
return true;

@ -6,41 +6,37 @@
#include <llarp/util/types.hpp>
#include <llarp/util/time.hpp>
namespace llarp
namespace llarp::path
{
namespace path
{
/// maximum path length
constexpr std::size_t max_len = 8;
/// default path length
constexpr std::size_t default_len = 4;
/// pad messages to the nearest this many bytes
constexpr std::size_t pad_size = 128;
/// default path lifetime in ms
constexpr std::chrono::milliseconds default_lifetime = 20min;
/// minimum into lifetime we will advertise
constexpr std::chrono::milliseconds min_intro_lifetime = default_lifetime / 2;
/// number of slices of path lifetime to spread intros out via
constexpr auto intro_spread_slices = 4;
/// spacing frequency at which we try to build paths for introductions
constexpr std::chrono::milliseconds intro_path_spread = default_lifetime / intro_spread_slices;
/// how long away from expiration in millseconds do we consider an intro to become stale
constexpr std::chrono::milliseconds intro_stale_threshold =
default_lifetime - intro_path_spread;
/// Minimum paths to keep around for intros; mainly used at startup (the
/// spread, above, should be able to maintain more than this number of paths
/// normally once things are going).
constexpr std::size_t min_intro_paths = 4;
/// after this many ms a path build times out
constexpr auto build_timeout = 10s;
/// maximum path length
constexpr std::size_t MAX_LEN = 8;
/// default path length
constexpr std::size_t DEFAULT_LEN = 4;
/// pad messages to the nearest this many bytes
constexpr std::size_t PAD_SIZE = 128;
/// default path lifetime in ms
constexpr std::chrono::milliseconds DEFAULT_LIFETIME = 20min;
/// minimum into lifetime we will advertise
constexpr std::chrono::milliseconds MIN_INTRO_LIFETIME = DEFAULT_LIFETIME / 2;
/// number of slices of path lifetime to spread intros out via
constexpr auto INTRO_SPREAD_SLICES = 4;
/// spacing frequency at which we try to build paths for introductions
constexpr std::chrono::milliseconds INTRO_PATH_SPREAD = DEFAULT_LIFETIME / INTRO_SPREAD_SLICES;
/// how long away from expiration in millseconds do we consider an intro to become stale
constexpr std::chrono::milliseconds INTRO_STALE_THRESHOLD = DEFAULT_LIFETIME - INTRO_PATH_SPREAD;
/// Minimum paths to keep around for intros; mainly used at startup (the
/// spread, above, should be able to maintain more than this number of paths
/// normally once things are going).
constexpr std::size_t MIN_INTRO_PATHS = 4;
/// after this many ms a path build times out
constexpr auto BUILD_TIMEOUT = 10s;
/// measure latency every this interval ms
constexpr auto latency_interval = 20s;
/// if a path is inactive for this amount of time it's dead
constexpr auto alive_timeout = latency_interval * 1.5;
/// measure latency every this interval ms
constexpr auto LATENCY_INTERVAL = 20s;
/// if a path is inactive for this amount of time it's dead
constexpr auto ALIVE_TIMEOUT = LATENCY_INTERVAL * 1.5;
/// how big transit hop traffic queues are
constexpr std::size_t transit_hop_queue_size = 256;
/// how big transit hop traffic queues are
constexpr std::size_t TRANSIT_HOP_QUEUE_SIZE = 256;
} // namespace path
} // namespace llarp
} // namespace llarp::path

@ -1,6 +1,504 @@
#include "crypto.hpp"
#include <sodium/core.h>
#include <sodium/crypto_generichash.h>
#include <sodium/crypto_sign.h>
#include <sodium/crypto_scalarmult.h>
#include <sodium/crypto_scalarmult_ed25519.h>
#include <sodium/crypto_stream_xchacha20.h>
#include <sodium/crypto_core_ed25519.h>
#include <sodium/crypto_aead_xchacha20poly1305.h>
#include <sodium/randombytes.h>
#include <sodium/utils.h>
#include <oxenc/endian.h>
#include <llarp/util/mem.hpp>
#include <llarp/util/str.hpp>
#include <cassert>
#include <cstring>
#ifdef HAVE_CRYPT
#include <crypt.h>
#endif
#include <llarp/util/str.hpp>
namespace llarp
{
Crypto* CryptoManager::m_crypto = nullptr;
}
static bool
dh(llarp::SharedSecret& out,
const PubKey& client_pk,
const PubKey& server_pk,
const uint8_t* themPub,
const SecretKey& usSec)
{
llarp::SharedSecret shared;
crypto_generichash_state h;
if (crypto_scalarmult_curve25519(shared.data(), usSec.data(), themPub))
{
return false;
}
crypto_generichash_blake2b_init(&h, nullptr, 0U, shared.size());
crypto_generichash_blake2b_update(&h, client_pk.data(), 32);
crypto_generichash_blake2b_update(&h, server_pk.data(), 32);
crypto_generichash_blake2b_update(&h, shared.data(), 32);
crypto_generichash_blake2b_final(&h, out.data(), shared.size());
return true;
}
static bool
dh_client_priv(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
llarp::SharedSecret dh_result;
if (dh(dh_result, sk.toPublic(), pk, pk.data(), sk))
{
return crypto_generichash_blake2b(shared.data(), 32, n.data(), 32, dh_result.data(), 32)
!= -1;
}
llarp::LogWarn("crypto::dh_client - dh failed");
return false;
}
static bool
dh_server_priv(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& 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)
!= -1;
}
llarp::LogWarn("crypto::dh_server - dh failed");
return false;
}
Crypto::Crypto()
{
if (sodium_init() == -1)
{
throw std::runtime_error("sodium_init() returned -1");
}
char* avx2 = std::getenv("AVX2_FORCE_DISABLE");
if (avx2 && std::string(avx2) == "1")
{
ntru_init(1);
}
else
{
ntru_init(0);
}
int seed = 0;
randombytes(reinterpret_cast<unsigned char*>(&seed), sizeof(seed));
srand(seed);
}
std::optional<AlignedBuffer<32>>
Crypto::maybe_decrypt_name(std::string_view ciphertext, SymmNonce nounce, std::string_view name)
{
const auto payloadsize = ciphertext.size() - crypto_aead_xchacha20poly1305_ietf_ABYTES;
if (payloadsize != 32)
return {};
SharedSecret derivedKey{};
ShortHash namehash{};
ustring name_buf{reinterpret_cast<const uint8_t*>(name.data()), name.size()};
if (not shorthash(namehash, name_buf.data(), name_buf.size()))
return {};
if (not hmac(derivedKey.data(), name_buf.data(), derivedKey.size(), namehash))
return {};
AlignedBuffer<32> result{};
if (crypto_aead_xchacha20poly1305_ietf_decrypt(
result.data(),
nullptr,
nullptr,
reinterpret_cast<const byte_t*>(ciphertext.data()),
ciphertext.size(),
nullptr,
0,
nounce.data(),
derivedKey.data())
== -1)
{
return {};
}
return result;
}
bool
Crypto::xchacha20(uint8_t* buf, size_t size, const SharedSecret& k, const TunnelNonce& n)
{
return crypto_stream_xchacha20_xor(buf, buf, size, n.data(), k.data()) == 0;
}
bool
Crypto::xchacha20_alt(
const llarp_buffer_t& out, const llarp_buffer_t& in, const SharedSecret& k, const byte_t* n)
{
if (in.sz > out.sz)
return false;
return crypto_stream_xchacha20_xor(out.base, in.base, in.sz, n, k.data()) == 0;
}
bool
Crypto::dh_client(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& 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)
{
return dh_server_priv(shared, pk, sk, n);
}
/// 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)
{
return crypto_generichash_blake2b(result.data(), ShortHash::SIZE, buf, size, nullptr, 0) != -1;
}
bool
Crypto::hmac(uint8_t* result, uint8_t* buf, size_t size, const SharedSecret& secret)
{
return crypto_generichash_blake2b(result, HMACSIZE, buf, size, secret.data(), HMACSECSIZE)
!= -1;
}
static bool
hash(uint8_t* result, const llarp_buffer_t& buff)
{
return crypto_generichash_blake2b(result, HASHSIZE, buff.base, buff.sz, nullptr, 0) != -1;
}
bool
Crypto::sign(Signature& sig, const SecretKey& secret, uint8_t* buf, size_t size)
{
return crypto_sign_detached(sig.data(), nullptr, buf, size, secret.data()) != -1;
}
bool
Crypto::sign(Signature& sig, const PrivateKey& privkey, uint8_t* buf, size_t size)
{
PubKey pubkey;
privkey.toPublic(pubkey);
crypto_hash_sha512_state hs;
unsigned char nonce[64];
unsigned char hram[64];
unsigned char mulres[32];
// r = H(s || M) where here s is pseudorandom bytes typically generated as
// part of hashing the seed (i.e. [a,s] = H(k)), but for derived
// PrivateKeys will come from a hash of the root key's s concatenated with
// the derivation hash.
crypto_hash_sha512_init(&hs);
crypto_hash_sha512_update(&hs, privkey.signingHash(), 32);
crypto_hash_sha512_update(&hs, buf, size);
crypto_hash_sha512_final(&hs, nonce);
crypto_core_ed25519_scalar_reduce(nonce, nonce);
// copy pubkey into sig to make (for now) sig = (R || A)
memmove(sig.data() + 32, pubkey.data(), 32);
// R = r * B
crypto_scalarmult_ed25519_base_noclamp(sig.data(), nonce);
// hram = H(R || A || M)
crypto_hash_sha512_init(&hs);
crypto_hash_sha512_update(&hs, sig.data(), 64);
crypto_hash_sha512_update(&hs, buf, size);
crypto_hash_sha512_final(&hs, hram);
// S = r + H(R || A || M) * s, so sig = (R || S)
crypto_core_ed25519_scalar_reduce(hram, hram);
crypto_core_ed25519_scalar_mul(mulres, hram, privkey.data());
crypto_core_ed25519_scalar_add(sig.data() + 32, mulres, nonce);
sodium_memzero(nonce, sizeof nonce);
return true;
}
bool
Crypto::verify(const PubKey& pub, uint8_t* buf, size_t size, const Signature& sig)
{
return crypto_sign_verify_detached(sig.data(), buf, size, pub.data()) != -1;
}
bool
Crypto::verify(uint8_t* pub, uint8_t* buf, size_t size, uint8_t* sig)
{
return crypto_sign_verify_detached(sig, buf, size, pub) != -1;
}
/// clamp a 32 byte ec point
static void
clamp_ed25519(byte_t* out)
{
out[0] &= 248;
out[31] &= 127;
out[31] |= 64;
}
template <typename K>
static K
clamp(const K& p)
{
K out = p;
clamp_ed25519(out);
return out;
}
template <typename K>
static bool
is_clamped(const K& key)
{
K other(key);
clamp_ed25519(other.data());
return other == key;
}
constexpr static char derived_key_hash_str[161] =
"just imagine what would happen if we all decided to understand. you "
"can't in the and by be or then before so just face it this text hurts "
"to read? lokinet yolo!";
template <typename K>
static bool
make_scalar(AlignedBuffer<32>& out, const K& k, uint64_t i)
{
// b = BLIND-STRING || k || i
std::array<byte_t, 160 + K::SIZE + sizeof(uint64_t)> buf;
std::copy(derived_key_hash_str, derived_key_hash_str + 160, buf.begin());
std::copy(k.begin(), k.end(), buf.begin() + 160);
oxenc::write_host_as_little(i, buf.data() + 160 + K::SIZE);
// n = H(b)
// h = make_point(n)
ShortHash n;
return -1
!= crypto_generichash_blake2b(n.data(), ShortHash::SIZE, buf.data(), buf.size(), nullptr, 0)
&& -1 != crypto_core_ed25519_from_uniform(out.data(), n.data());
}
static AlignedBuffer<32> zero;
bool
Crypto::derive_subkey(
PubKey& out_pubkey, const PubKey& root_pubkey, uint64_t key_n, const AlignedBuffer<32>* hash)
{
// scalar h = H( BLIND-STRING || root_pubkey || key_n )
AlignedBuffer<32> h;
if (hash)
h = *hash;
else if (not make_scalar(h, root_pubkey, key_n))
{
LogError("cannot make scalar");
return false;
}
return 0 == crypto_scalarmult_ed25519(out_pubkey.data(), h.data(), root_pubkey.data());
}
bool
Crypto::derive_subkey_private(
PrivateKey& out_key, const SecretKey& root_key, uint64_t key_n, const AlignedBuffer<32>* hash)
{
// Derives a private subkey from a root key.
//
// The basic idea is:
//
// h = H( BLIND-STRING || A || key_n )
// a - private key
// A = aB - public key
// s - signing hash
// a' = ah - derived private key
// A' = a'B = (ah)B - derived public key
// s' = H(h || s) - derived signing hash
//
// libsodium throws some wrenches in the mechanics which are a nuisance,
// the biggest of which is that sodium's secret key is *not* `a`; rather
// it is the seed. If you want to get the private key (i.e. "a"), you
// need to SHA-512 hash it and then clamp that.
//
// This also makes signature verification harder: we can't just use
// sodium's sign function because it wants to be given the seed rather
// than the private key, and moreover we can't actually *get* the seed to
// make libsodium happy because we only have `ah` above; thus we
// reimplemented most of sodium's detached signing function but without
// the hash step.
//
// Lastly, for the signing hash s', we need some value that is both
// different from the root s but also unknowable from the public key
// (since otherwise `r` in the signing function would be known), so we
// generate it from a hash of `h` and the root key's (psuedorandom)
// signing hash, `s`.
//
const auto root_pubkey = root_key.toPublic();
AlignedBuffer<32> h;
if (hash)
h = *hash;
else if (not make_scalar(h, root_pubkey, key_n))
{
LogError("cannot make scalar");
return false;
}
h[0] &= 248;
h[31] &= 63;
h[31] |= 64;
PrivateKey a;
if (!root_key.toPrivate(a))
return false;
// a' = ha
crypto_core_ed25519_scalar_mul(out_key.data(), h.data(), a.data());
// s' = H(h || s)
std::array<byte_t, 64> buf;
std::copy(h.begin(), h.end(), buf.begin());
std::copy(a.signingHash(), a.signingHash() + 32, buf.begin() + 32);
return -1
!= crypto_generichash_blake2b(
out_key.signingHash(), 32, buf.data(), buf.size(), nullptr, 0);
return true;
}
bool
Crypto::seed_to_secretkey(llarp::SecretKey& secret, const llarp::IdentitySecret& seed)
{
return crypto_sign_ed25519_seed_keypair(secret.data() + 32, secret.data(), seed.data()) != -1;
}
void
Crypto::randomize(const llarp_buffer_t& buff)
{
randombytes((unsigned char*)buff.base, buff.sz);
}
void
Crypto::randbytes(byte_t* ptr, size_t sz)
{
randombytes((unsigned char*)ptr, sz);
}
void
Crypto::identity_keygen(llarp::SecretKey& keys)
{
PubKey pk;
int result = crypto_sign_keypair(pk.data(), keys.data());
assert(result != -1);
const PubKey sk_pk = keys.toPublic();
assert(pk == sk_pk);
(void)result;
(void)sk_pk;
// encryption_keygen(keys);
}
bool
Crypto::check_identity_privkey(const llarp::SecretKey& keys)
{
AlignedBuffer<crypto_sign_SEEDBYTES> seed;
llarp::PubKey pk;
llarp::SecretKey sk;
if (crypto_sign_ed25519_sk_to_seed(seed.data(), keys.data()) == -1)
return false;
if (crypto_sign_seed_keypair(pk.data(), sk.data(), seed.data()) == -1)
return false;
return keys.toPublic() == pk && sk == keys;
}
void
Crypto::encryption_keygen(llarp::SecretKey& keys)
{
auto d = keys.data();
randbytes(d, 32);
crypto_scalarmult_curve25519_base(d + 32, d);
}
bool
Crypto::pqe_encrypt(PQCipherBlock& ciphertext, SharedSecret& sharedkey, const PQPubKey& pubkey)
{
return crypto_kem_enc(ciphertext.data(), sharedkey.data(), pubkey.data()) != -1;
}
bool
Crypto::pqe_decrypt(
const PQCipherBlock& ciphertext, SharedSecret& sharedkey, const byte_t* secretkey)
{
return crypto_kem_dec(sharedkey.data(), ciphertext.data(), secretkey) != -1;
}
void
Crypto::pqe_keygen(PQKeyPair& keypair)
{
auto d = keypair.data();
crypto_kem_keypair(d + PQ_SECRETKEYSIZE, d);
}
bool
Crypto::check_passwd_hash(std::string pwhash, std::string challenge)
{
(void)pwhash;
(void)challenge;
bool ret = false;
#ifdef HAVE_CRYPT
auto pos = pwhash.find_last_of('$');
auto settings = pwhash.substr(0, pos);
crypt_data data{};
if (char* ptr = crypt_r(challenge.c_str(), settings.c_str(), &data))
{
ret = ptr == pwhash;
}
sodium_memzero(&data, sizeof(data));
#endif
return ret;
}
const byte_t*
seckey_topublic(const SecretKey& sec)
{
return sec.data() + 32;
}
const byte_t*
pq_keypair_to_public(const PQKeyPair& k)
{
return k.data() + PQ_SECRETKEYSIZE;
}
const byte_t*
pq_keypair_to_secret(const PQKeyPair& k)
{
return k.data();
}
uint64_t
randint()
{
uint64_t i;
randombytes((byte_t*)&i, sizeof(i));
return i;
}
} // namespace llarp

@ -9,105 +9,106 @@
#include <cstdint>
/**
* crypto.hpp
*
* libsodium abstraction layer
* potentially allow libssl support in the future
*/
namespace llarp
{
/// library crypto configuration
struct Crypto
{
virtual ~Crypto() = 0;
Crypto();
~Crypto() = default;
/// decrypt cipherText name given the key generated from name
virtual std::optional<AlignedBuffer<32>>
maybe_decrypt_name(std::string_view ciphertext, SymmNonce nounce, std::string_view name) = 0;
/// decrypt cipherText given the key generated from name
std::optional<AlignedBuffer<32>>
maybe_decrypt_name(std::string_view ciphertext, SymmNonce nounce, std::string_view name);
/// xchacha symmetric cipher
virtual bool
xchacha20(const llarp_buffer_t&, const SharedSecret&, const TunnelNonce&) = 0;
bool
xchacha20(uint8_t*, size_t size, const SharedSecret&, const TunnelNonce&);
/// xchacha symmetric cipher (multibuffer)
virtual bool
xchacha20_alt(
const llarp_buffer_t&, const llarp_buffer_t&, const SharedSecret&, const byte_t*) = 0;
bool
xchacha20_alt(const llarp_buffer_t&, const llarp_buffer_t&, const SharedSecret&, const byte_t*);
/// path dh creator's side
virtual bool
dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) = 0;
bool
dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
/// path dh relay side
virtual bool
dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) = 0;
bool
dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
/// transport dh client side
virtual bool
transport_dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) = 0;
bool
transport_dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
/// transport dh server side
virtual bool
transport_dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) = 0;
bool
transport_dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
/// blake2b 256 bit
virtual bool
shorthash(ShortHash&, const llarp_buffer_t&) = 0;
/// blake2s 256 bit "hmac" (keyed hash)
virtual bool
hmac(byte_t*, const llarp_buffer_t&, const SharedSecret&) = 0;
bool
shorthash(ShortHash&, uint8_t*, size_t size);
/// blake2s 256 bit hmac
bool
hmac(uint8_t*, uint8_t*, size_t, const SharedSecret&);
/// ed25519 sign
virtual bool
sign(Signature&, const SecretKey&, const llarp_buffer_t&) = 0;
bool
sign(Signature&, const SecretKey&, uint8_t* buf, size_t size);
/// ed25519 sign (custom with derived keys)
virtual bool
sign(Signature&, const PrivateKey&, const llarp_buffer_t&) = 0;
bool
sign(Signature&, const PrivateKey&, uint8_t* buf, size_t size);
/// ed25519 verify
virtual bool
verify(const PubKey&, const llarp_buffer_t&, const Signature&) = 0;
/// derive sub keys for public keys
virtual bool
derive_subkey(PubKey&, const PubKey&, uint64_t, const AlignedBuffer<32>* = nullptr) = 0;
/// derive sub keys for private keys
virtual bool
bool
verify(const PubKey&, uint8_t*, size_t, const Signature&);
bool
verify(uint8_t*, uint8_t*, size_t, uint8_t*);
/// derive sub keys for public keys. hash is really only intended for
/// testing ands key_n if given.
bool
derive_subkey(
PubKey& derived,
const PubKey& root,
uint64_t key_n,
const AlignedBuffer<32>* hash = nullptr);
/// derive sub keys for private keys. hash is really only intended for
/// testing ands key_n if given.
bool
derive_subkey_private(
PrivateKey&, const SecretKey&, uint64_t, const AlignedBuffer<32>* = nullptr) = 0;
PrivateKey& derived,
const SecretKey& root,
uint64_t key_n,
const AlignedBuffer<32>* hash = nullptr);
/// seed to secretkey
virtual bool
seed_to_secretkey(llarp::SecretKey&, const llarp::IdentitySecret&) = 0;
bool
seed_to_secretkey(llarp::SecretKey&, const llarp::IdentitySecret&);
/// randomize buffer
virtual void
randomize(const llarp_buffer_t&) = 0;
void
randomize(const llarp_buffer_t&);
/// randomizer memory
virtual void
randbytes(byte_t*, size_t) = 0;
void
randbytes(byte_t*, size_t);
/// generate signing keypair
virtual void
identity_keygen(SecretKey&) = 0;
void
identity_keygen(SecretKey&);
/// generate encryption keypair
virtual void
encryption_keygen(SecretKey&) = 0;
void
encryption_keygen(SecretKey&);
/// generate post quantum encrytion key
virtual void
pqe_keygen(PQKeyPair&) = 0;
void
pqe_keygen(PQKeyPair&);
/// post quantum decrypt (buffer, sharedkey_dst, sec)
virtual bool
pqe_decrypt(const PQCipherBlock&, SharedSecret&, const byte_t*) = 0;
bool
pqe_decrypt(const PQCipherBlock&, SharedSecret&, const byte_t*);
/// post quantum encrypt (buffer, sharedkey_dst, pub)
virtual bool
pqe_encrypt(PQCipherBlock&, SharedSecret&, const PQPubKey&) = 0;
bool
pqe_encrypt(PQCipherBlock&, SharedSecret&, const PQPubKey&);
virtual bool
check_identity_privkey(const SecretKey&) = 0;
bool
check_identity_privkey(const SecretKey&);
/// check if a password hash string matches the challenge
virtual bool
check_passwd_hash(std::string pwhash, std::string challenge) = 0;
bool
check_passwd_hash(std::string pwhash, std::string challenge);
};
inline Crypto::~Crypto() = default;
/// return random 64bit unsigned interger
uint64_t
randint();

@ -1,518 +0,0 @@
#include "crypto_libsodium.hpp"
#include <sodium/crypto_generichash.h>
#include <sodium/crypto_sign.h>
#include <sodium/crypto_scalarmult.h>
#include <sodium/crypto_scalarmult_ed25519.h>
#include <sodium/crypto_stream_xchacha20.h>
#include <sodium/crypto_core_ed25519.h>
#include <sodium/crypto_aead_xchacha20poly1305.h>
#include <sodium/randombytes.h>
#include <sodium/utils.h>
#include <oxenc/endian.h>
#include <llarp/util/mem.hpp>
#include <llarp/util/str.hpp>
#include <cassert>
#include <cstring>
#ifdef HAVE_CRYPT
#include <crypt.h>
#endif
#include <llarp/util/str.hpp>
extern "C"
{
extern int
sodium_init(void);
}
namespace llarp
{
namespace sodium
{
static bool
dh(llarp::SharedSecret& out,
const PubKey& client_pk,
const PubKey& server_pk,
const uint8_t* themPub,
const SecretKey& usSec)
{
llarp::SharedSecret shared;
crypto_generichash_state h;
if (crypto_scalarmult_curve25519(shared.data(), usSec.data(), themPub))
{
return false;
}
crypto_generichash_blake2b_init(&h, nullptr, 0U, shared.size());
crypto_generichash_blake2b_update(&h, client_pk.data(), 32);
crypto_generichash_blake2b_update(&h, server_pk.data(), 32);
crypto_generichash_blake2b_update(&h, shared.data(), 32);
crypto_generichash_blake2b_final(&h, out.data(), shared.size());
return true;
}
static bool
dh_client_priv(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
llarp::SharedSecret dh_result;
if (dh(dh_result, sk.toPublic(), pk, pk.data(), sk))
{
return crypto_generichash_blake2b(shared.data(), 32, n.data(), 32, dh_result.data(), 32)
!= -1;
}
llarp::LogWarn("crypto::dh_client - dh failed");
return false;
}
static bool
dh_server_priv(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& 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)
!= -1;
}
llarp::LogWarn("crypto::dh_server - dh failed");
return false;
}
CryptoLibSodium::CryptoLibSodium()
{
if (sodium_init() == -1)
{
throw std::runtime_error("sodium_init() returned -1");
}
char* avx2 = std::getenv("AVX2_FORCE_DISABLE");
if (avx2 && std::string(avx2) == "1")
{
ntru_init(1);
}
else
{
ntru_init(0);
}
int seed = 0;
randombytes(reinterpret_cast<unsigned char*>(&seed), sizeof(seed));
srand(seed);
}
std::optional<AlignedBuffer<32>>
CryptoLibSodium::maybe_decrypt_name(
std::string_view ciphertext, SymmNonce nounce, std::string_view name)
{
const auto payloadsize = ciphertext.size() - crypto_aead_xchacha20poly1305_ietf_ABYTES;
if (payloadsize != 32)
return {};
SharedSecret derivedKey{};
ShortHash namehash{};
const llarp_buffer_t namebuf(reinterpret_cast<const char*>(name.data()), name.size());
if (not shorthash(namehash, namebuf))
return {};
if (not hmac(derivedKey.data(), namebuf, namehash))
return {};
AlignedBuffer<32> result{};
if (crypto_aead_xchacha20poly1305_ietf_decrypt(
result.data(),
nullptr,
nullptr,
reinterpret_cast<const byte_t*>(ciphertext.data()),
ciphertext.size(),
nullptr,
0,
nounce.data(),
derivedKey.data())
== -1)
{
return {};
}
return result;
}
bool
CryptoLibSodium::xchacha20(
const llarp_buffer_t& buff, const SharedSecret& k, const TunnelNonce& n)
{
return crypto_stream_xchacha20_xor(buff.base, buff.base, buff.sz, n.data(), k.data()) == 0;
}
bool
CryptoLibSodium::xchacha20_alt(
const llarp_buffer_t& out, const llarp_buffer_t& in, const SharedSecret& k, const byte_t* n)
{
if (in.sz > out.sz)
return false;
return crypto_stream_xchacha20_xor(out.base, in.base, in.sz, n, k.data()) == 0;
}
bool
CryptoLibSodium::dh_client(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
return dh_client_priv(shared, pk, sk, n);
}
/// path dh relay side
bool
CryptoLibSodium::dh_server(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
return dh_server_priv(shared, pk, sk, n);
}
/// transport dh client side
bool
CryptoLibSodium::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
CryptoLibSodium::transport_dh_server(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
return dh_server_priv(shared, pk, sk, n);
}
bool
CryptoLibSodium::shorthash(ShortHash& result, const llarp_buffer_t& buff)
{
return crypto_generichash_blake2b(
result.data(), ShortHash::SIZE, buff.base, buff.sz, nullptr, 0)
!= -1;
}
bool
CryptoLibSodium::hmac(byte_t* result, const llarp_buffer_t& buff, const SharedSecret& secret)
{
return crypto_generichash_blake2b(
result, HMACSIZE, buff.base, buff.sz, secret.data(), HMACSECSIZE)
!= -1;
}
static bool
hash(uint8_t* result, const llarp_buffer_t& buff)
{
return crypto_generichash_blake2b(result, HASHSIZE, buff.base, buff.sz, nullptr, 0) != -1;
}
bool
CryptoLibSodium::sign(Signature& sig, const SecretKey& secret, const llarp_buffer_t& buf)
{
return crypto_sign_detached(sig.data(), nullptr, buf.base, buf.sz, secret.data()) != -1;
}
bool
CryptoLibSodium::sign(Signature& sig, const PrivateKey& privkey, const llarp_buffer_t& buf)
{
PubKey pubkey;
privkey.toPublic(pubkey);
crypto_hash_sha512_state hs;
unsigned char nonce[64];
unsigned char hram[64];
unsigned char mulres[32];
// r = H(s || M) where here s is pseudorandom bytes typically generated as
// part of hashing the seed (i.e. [a,s] = H(k)), but for derived
// PrivateKeys will come from a hash of the root key's s concatenated with
// the derivation hash.
crypto_hash_sha512_init(&hs);
crypto_hash_sha512_update(&hs, privkey.signingHash(), 32);
crypto_hash_sha512_update(&hs, buf.base, buf.sz);
crypto_hash_sha512_final(&hs, nonce);
crypto_core_ed25519_scalar_reduce(nonce, nonce);
// copy pubkey into sig to make (for now) sig = (R || A)
memmove(sig.data() + 32, pubkey.data(), 32);
// R = r * B
crypto_scalarmult_ed25519_base_noclamp(sig.data(), nonce);
// hram = H(R || A || M)
crypto_hash_sha512_init(&hs);
crypto_hash_sha512_update(&hs, sig.data(), 64);
crypto_hash_sha512_update(&hs, buf.base, buf.sz);
crypto_hash_sha512_final(&hs, hram);
// S = r + H(R || A || M) * s, so sig = (R || S)
crypto_core_ed25519_scalar_reduce(hram, hram);
crypto_core_ed25519_scalar_mul(mulres, hram, privkey.data());
crypto_core_ed25519_scalar_add(sig.data() + 32, mulres, nonce);
sodium_memzero(nonce, sizeof nonce);
return true;
}
bool
CryptoLibSodium::verify(const PubKey& pub, const llarp_buffer_t& buf, const Signature& sig)
{
return crypto_sign_verify_detached(sig.data(), buf.base, buf.sz, pub.data()) != -1;
}
/// clamp a 32 byte ec point
static void
clamp_ed25519(byte_t* out)
{
out[0] &= 248;
out[31] &= 127;
out[31] |= 64;
}
template <typename K>
static K
clamp(const K& p)
{
K out = p;
clamp_ed25519(out);
return out;
}
template <typename K>
static bool
is_clamped(const K& key)
{
K other(key);
clamp_ed25519(other.data());
return other == key;
}
constexpr static char derived_key_hash_str[161] =
"just imagine what would happen if we all decided to understand. you "
"can't in the and by be or then before so just face it this text hurts "
"to read? lokinet yolo!";
template <typename K>
static bool
make_scalar(AlignedBuffer<32>& out, const K& k, uint64_t i)
{
// b = BLIND-STRING || k || i
std::array<byte_t, 160 + K::SIZE + sizeof(uint64_t)> buf;
std::copy(derived_key_hash_str, derived_key_hash_str + 160, buf.begin());
std::copy(k.begin(), k.end(), buf.begin() + 160);
oxenc::write_host_as_little(i, buf.data() + 160 + K::SIZE);
// n = H(b)
// h = make_point(n)
ShortHash n;
return -1
!= crypto_generichash_blake2b(
n.data(), ShortHash::SIZE, buf.data(), buf.size(), nullptr, 0)
&& -1 != crypto_core_ed25519_from_uniform(out.data(), n.data());
}
static AlignedBuffer<32> zero;
bool
CryptoLibSodium::derive_subkey(
PubKey& out_pubkey,
const PubKey& root_pubkey,
uint64_t key_n,
const AlignedBuffer<32>* hash)
{
// scalar h = H( BLIND-STRING || root_pubkey || key_n )
AlignedBuffer<32> h;
if (hash)
h = *hash;
else if (not make_scalar(h, root_pubkey, key_n))
{
LogError("cannot make scalar");
return false;
}
return 0 == crypto_scalarmult_ed25519(out_pubkey.data(), h.data(), root_pubkey.data());
}
bool
CryptoLibSodium::derive_subkey_private(
PrivateKey& out_key,
const SecretKey& root_key,
uint64_t key_n,
const AlignedBuffer<32>* hash)
{
// Derives a private subkey from a root key.
//
// The basic idea is:
//
// h = H( BLIND-STRING || A || key_n )
// a - private key
// A = aB - public key
// s - signing hash
// a' = ah - derived private key
// A' = a'B = (ah)B - derived public key
// s' = H(h || s) - derived signing hash
//
// libsodium throws some wrenches in the mechanics which are a nuisance,
// the biggest of which is that sodium's secret key is *not* `a`; rather
// it is the seed. If you want to get the private key (i.e. "a"), you
// need to SHA-512 hash it and then clamp that.
//
// This also makes signature verification harder: we can't just use
// sodium's sign function because it wants to be given the seed rather
// than the private key, and moreover we can't actually *get* the seed to
// make libsodium happy because we only have `ah` above; thus we
// reimplemented most of sodium's detached signing function but without
// the hash step.
//
// Lastly, for the signing hash s', we need some value that is both
// different from the root s but also unknowable from the public key
// (since otherwise `r` in the signing function would be known), so we
// generate it from a hash of `h` and the root key's (psuedorandom)
// signing hash, `s`.
//
const auto root_pubkey = root_key.toPublic();
AlignedBuffer<32> h;
if (hash)
h = *hash;
else if (not make_scalar(h, root_pubkey, key_n))
{
LogError("cannot make scalar");
return false;
}
h[0] &= 248;
h[31] &= 63;
h[31] |= 64;
PrivateKey a;
if (!root_key.toPrivate(a))
return false;
// a' = ha
crypto_core_ed25519_scalar_mul(out_key.data(), h.data(), a.data());
// s' = H(h || s)
std::array<byte_t, 64> buf;
std::copy(h.begin(), h.end(), buf.begin());
std::copy(a.signingHash(), a.signingHash() + 32, buf.begin() + 32);
return -1
!= crypto_generichash_blake2b(
out_key.signingHash(), 32, buf.data(), buf.size(), nullptr, 0);
return true;
}
bool
CryptoLibSodium::seed_to_secretkey(llarp::SecretKey& secret, const llarp::IdentitySecret& seed)
{
return crypto_sign_ed25519_seed_keypair(secret.data() + 32, secret.data(), seed.data()) != -1;
}
void
CryptoLibSodium::randomize(const llarp_buffer_t& buff)
{
randombytes((unsigned char*)buff.base, buff.sz);
}
void
CryptoLibSodium::randbytes(byte_t* ptr, size_t sz)
{
randombytes((unsigned char*)ptr, sz);
}
void
CryptoLibSodium::identity_keygen(llarp::SecretKey& keys)
{
PubKey pk;
int result = crypto_sign_keypair(pk.data(), keys.data());
assert(result != -1);
const PubKey sk_pk = keys.toPublic();
assert(pk == sk_pk);
(void)result;
(void)sk_pk;
// encryption_keygen(keys);
}
bool
CryptoLibSodium::check_identity_privkey(const llarp::SecretKey& keys)
{
AlignedBuffer<crypto_sign_SEEDBYTES> seed;
llarp::PubKey pk;
llarp::SecretKey sk;
if (crypto_sign_ed25519_sk_to_seed(seed.data(), keys.data()) == -1)
return false;
if (crypto_sign_seed_keypair(pk.data(), sk.data(), seed.data()) == -1)
return false;
return keys.toPublic() == pk && sk == keys;
}
void
CryptoLibSodium::encryption_keygen(llarp::SecretKey& keys)
{
auto d = keys.data();
randbytes(d, 32);
crypto_scalarmult_curve25519_base(d + 32, d);
}
bool
CryptoLibSodium::pqe_encrypt(
PQCipherBlock& ciphertext, SharedSecret& sharedkey, const PQPubKey& pubkey)
{
return crypto_kem_enc(ciphertext.data(), sharedkey.data(), pubkey.data()) != -1;
}
bool
CryptoLibSodium::pqe_decrypt(
const PQCipherBlock& ciphertext, SharedSecret& sharedkey, const byte_t* secretkey)
{
return crypto_kem_dec(sharedkey.data(), ciphertext.data(), secretkey) != -1;
}
void
CryptoLibSodium::pqe_keygen(PQKeyPair& keypair)
{
auto d = keypair.data();
crypto_kem_keypair(d + PQ_SECRETKEYSIZE, d);
}
bool
CryptoLibSodium::check_passwd_hash(std::string pwhash, std::string challenge)
{
(void)pwhash;
(void)challenge;
bool ret = false;
#ifdef HAVE_CRYPT
auto pos = pwhash.find_last_of('$');
auto settings = pwhash.substr(0, pos);
crypt_data data{};
if (char* ptr = crypt_r(challenge.c_str(), settings.c_str(), &data))
{
ret = ptr == pwhash;
}
sodium_memzero(&data, sizeof(data));
#endif
return ret;
}
} // namespace sodium
const byte_t*
seckey_topublic(const SecretKey& sec)
{
return sec.data() + 32;
}
const byte_t*
pq_keypair_to_public(const PQKeyPair& k)
{
return k.data() + PQ_SECRETKEYSIZE;
}
const byte_t*
pq_keypair_to_secret(const PQKeyPair& k)
{
return k.data();
}
uint64_t
randint()
{
uint64_t i;
randombytes((byte_t*)&i, sizeof(i));
return i;
}
} // namespace llarp

@ -1,113 +0,0 @@
#pragma once
#include "crypto.hpp"
namespace llarp
{
namespace sodium
{
struct CryptoLibSodium final : public Crypto
{
CryptoLibSodium();
~CryptoLibSodium() override = default;
/// decrypt cipherText given the key generated from name
std::optional<AlignedBuffer<32>>
maybe_decrypt_name(
std::string_view ciphertext, SymmNonce nounce, std::string_view name) override;
/// xchacha symmetric cipher
bool
xchacha20(const llarp_buffer_t&, const SharedSecret&, const TunnelNonce&) override;
/// xchacha symmetric cipher (multibuffer)
bool
xchacha20_alt(
const llarp_buffer_t&,
const llarp_buffer_t&,
const SharedSecret&,
const byte_t*) override;
/// path dh creator's side
bool
dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) override;
/// path dh relay side
bool
dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) override;
/// transport dh client side
bool
transport_dh_client(
SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) override;
/// transport dh server side
bool
transport_dh_server(
SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) override;
/// blake2b 256 bit
bool
shorthash(ShortHash&, const llarp_buffer_t&) override;
/// blake2s 256 bit hmac
bool
hmac(byte_t*, const llarp_buffer_t&, const SharedSecret&) override;
/// ed25519 sign
bool
sign(Signature&, const SecretKey&, const llarp_buffer_t&) override;
/// ed25519 sign (custom with derived keys)
bool
sign(Signature&, const PrivateKey&, const llarp_buffer_t&) override;
/// ed25519 verify
bool
verify(const PubKey&, const llarp_buffer_t&, const Signature&) override;
/// derive sub keys for public keys. hash is really only intended for
/// testing and overrides key_n if given.
bool
derive_subkey(
PubKey& derived,
const PubKey& root,
uint64_t key_n,
const AlignedBuffer<32>* hash = nullptr) override;
/// derive sub keys for private keys. hash is really only intended for
/// testing and overrides key_n if given.
bool
derive_subkey_private(
PrivateKey& derived,
const SecretKey& root,
uint64_t key_n,
const AlignedBuffer<32>* hash = nullptr) override;
/// seed to secretkey
bool
seed_to_secretkey(llarp::SecretKey&, const llarp::IdentitySecret&) override;
/// randomize buffer
void
randomize(const llarp_buffer_t&) override;
/// randomizer memory
void
randbytes(byte_t*, size_t) override;
/// generate signing keypair
void
identity_keygen(SecretKey&) override;
/// generate encryption keypair
void
encryption_keygen(SecretKey&) override;
/// generate post quantum encrytion key
void
pqe_keygen(PQKeyPair&) override;
/// post quantum decrypt (buffer, sharedkey_dst, sec)
bool
pqe_decrypt(const PQCipherBlock&, SharedSecret&, const byte_t*) override;
/// post quantum encrypt (buffer, sharedkey_dst, pub)
bool
pqe_encrypt(PQCipherBlock&, SharedSecret&, const PQPubKey&) override;
bool
check_identity_privkey(const SecretKey&) override;
bool
check_passwd_hash(std::string pwhash, std::string challenge) override;
};
} // namespace sodium
} // namespace llarp

@ -9,42 +9,29 @@ namespace llarp
bool
EncryptedFrame::DoEncrypt(const SharedSecret& shared, bool noDH)
{
byte_t* hash = data();
byte_t* noncePtr = hash + SHORTHASHSIZE;
byte_t* pubkey = noncePtr + TUNNONCESIZE;
byte_t* body = pubkey + PUBKEYSIZE;
auto crypto = CryptoManager::instance();
// if noDH flag, means key exchange has already taken place
// in this case, set pubkey to random noise and choose a
// random nonce here
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(noncePtr, TUNNONCESIZE);
crypto->randbytes(pubkey, PUBKEYSIZE);
crypto->randbytes(nonce_ptr, TUNNONCESIZE);
crypto->randbytes(pubkey_ptr, PUBKEYSIZE);
}
TunnelNonce nonce(noncePtr);
llarp_buffer_t buf;
buf.base = body;
buf.cur = buf.base;
buf.sz = size() - EncryptedFrameOverheadSize;
TunnelNonce nonce(nonce_ptr);
// encrypt body
if (!crypto->xchacha20(buf, shared, nonce))
if (!crypto->xchacha20(body_ptr, size() - EncryptedFrameOverheadSize, shared, nonce))
{
llarp::LogError("encrypt failed");
return false;
}
// generate message auth
buf.base = noncePtr;
buf.cur = buf.base;
buf.sz = size() - SHORTHASHSIZE;
if (!crypto->hmac(hash, buf, shared))
if (!crypto->hmac(hash_ptr, nonce_ptr, size() - SHORTHASHSIZE, shared))
{
llarp::LogError("Failed to generate message auth");
return false;
@ -89,36 +76,28 @@ namespace llarp
bool
EncryptedFrame::DoDecrypt(const SharedSecret& shared)
{
ShortHash hash(data());
byte_t* noncePtr = data() + SHORTHASHSIZE;
byte_t* body = data() + EncryptedFrameOverheadSize;
TunnelNonce nonce(noncePtr);
auto crypto = CryptoManager::instance();
llarp_buffer_t buf;
buf.base = noncePtr;
buf.cur = buf.base;
buf.sz = size() - SHORTHASHSIZE;
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(), buf, shared))
if (!crypto->hmac(digest.data(), nonce_ptr, size() - SHORTHASHSIZE, shared))
{
llarp::LogError("Digest failed");
return false;
}
if (!std::equal(digest.begin(), digest.end(), hash.begin()))
if (!std::equal(digest.begin(), digest.end(), hash_ptr))
{
llarp::LogError("message authentication failed");
return false;
}
buf.base = body;
buf.cur = body;
buf.sz = size() - EncryptedFrameOverheadSize;
if (!crypto->xchacha20(buf, shared, nonce))
if (!crypto->xchacha20(body_ptr, size() - EncryptedFrameOverheadSize, shared, nonce))
{
llarp::LogError("decrypt failed");
return false;

@ -21,7 +21,7 @@ namespace llarp
explicit PubKey(const byte_t* ptr) : AlignedBuffer<SIZE>(ptr)
{}
explicit PubKey(const Data& data) : AlignedBuffer<SIZE>(data)
explicit PubKey(const std::array<byte_t, SIZE>& data) : AlignedBuffer<SIZE>(data)
{}
explicit PubKey(const AlignedBuffer<SIZE>& other) : AlignedBuffer<SIZE>(other)

@ -8,224 +8,221 @@
#include <set>
#include <vector>
namespace llarp
namespace llarp::dht
{
namespace dht
template <typename Val_t>
struct Bucket
{
template <typename Val_t>
struct Bucket
{
using BucketStorage_t = std::map<Key_t, Val_t, XorMetric>;
using Random_t = std::function<uint64_t()>;
using BucketStorage_t = std::map<Key_t, Val_t, XorMetric>;
using Random_t = std::function<uint64_t()>;
Bucket(const Key_t& us, Random_t r) : nodes(XorMetric(us)), random(std::move(r))
{}
Bucket(const Key_t& us, Random_t r) : nodes(XorMetric(us)), random(std::move(r))
{}
util::StatusObject
ExtractStatus() const
util::StatusObject
ExtractStatus() const
{
util::StatusObject obj{};
for (const auto& item : nodes)
{
util::StatusObject obj{};
for (const auto& item : nodes)
{
obj[item.first.ToString()] = item.second.ExtractStatus();
}
return obj;
obj[item.first.ToString()] = item.second.ExtractStatus();
}
return obj;
}
size_t
size() const
{
return nodes.size();
}
size_t
size() const
{
return nodes.size();
}
struct SetIntersector
struct SetIntersector
{
bool
operator()(const typename BucketStorage_t::value_type& lhs, const Key_t& rhs)
{
bool
operator()(const typename BucketStorage_t::value_type& lhs, const Key_t& rhs)
{
return lhs.first < rhs;
}
bool
operator()(const Key_t& lhs, const typename BucketStorage_t::value_type& rhs)
{
return lhs < rhs.first;
}
};
return lhs.first < rhs;
}
bool
GetRandomNodeExcluding(Key_t& result, const std::set<Key_t>& exclude) const
operator()(const Key_t& lhs, const typename BucketStorage_t::value_type& rhs)
{
std::vector<typename BucketStorage_t::value_type> candidates;
std::set_difference(
nodes.begin(),
nodes.end(),
exclude.begin(),
exclude.end(),
std::back_inserter(candidates),
SetIntersector());
if (candidates.empty())
{
return false;
}
result = candidates[random() % candidates.size()].first;
return true;
return lhs < rhs.first;
}
};
bool
FindClosest(const Key_t& target, Key_t& result) const
bool
GetRandomNodeExcluding(Key_t& result, const std::set<Key_t>& exclude) const
{
std::vector<typename BucketStorage_t::value_type> candidates;
std::set_difference(
nodes.begin(),
nodes.end(),
exclude.begin(),
exclude.end(),
std::back_inserter(candidates),
SetIntersector());
if (candidates.empty())
{
Key_t mindist;
mindist.Fill(0xff);
for (const auto& item : nodes)
{
auto curDist = item.first ^ target;
if (curDist < mindist)
{
mindist = curDist;
result = item.first;
}
}
return nodes.size() > 0;
return false;
}
result = candidates[random() % candidates.size()].first;
return true;
}
bool
GetManyRandom(std::set<Key_t>& result, size_t N) const
bool
FindClosest(const Key_t& target, Key_t& result) const
{
Key_t mindist;
mindist.Fill(0xff);
for (const auto& item : nodes)
{
if (nodes.size() < N || nodes.empty())
auto curDist = item.first ^ target;
if (curDist < mindist)
{
llarp::LogWarn("Not enough dht nodes, have ", nodes.size(), " want ", N);
return false;
mindist = curDist;
result = item.first;
}
if (nodes.size() == N)
{
std::transform(
nodes.begin(), nodes.end(), std::inserter(result, result.end()), [](const auto& a) {
return a.first;
});
}
return nodes.size() > 0;
}
return true;
}
size_t expecting = N;
size_t sz = nodes.size();
while (N)
{
auto itr = nodes.begin();
std::advance(itr, random() % sz);
if (result.insert(itr->first).second)
{
--N;
}
}
return result.size() == expecting;
bool
GetManyRandom(std::set<Key_t>& result, size_t N) const
{
if (nodes.size() < N || nodes.empty())
{
llarp::LogWarn("Not enough dht nodes, have ", nodes.size(), " want ", N);
return false;
}
if (nodes.size() == N)
{
std::transform(
nodes.begin(), nodes.end(), std::inserter(result, result.end()), [](const auto& a) {
return a.first;
});
bool
FindCloseExcluding(const Key_t& target, Key_t& result, const std::set<Key_t>& exclude) const
return true;
}
size_t expecting = N;
size_t sz = nodes.size();
while (N)
{
Key_t maxdist;
maxdist.Fill(0xff);
Key_t mindist;
mindist.Fill(0xff);
for (const auto& item : nodes)
auto itr = nodes.begin();
std::advance(itr, random() % sz);
if (result.insert(itr->first).second)
{
if (exclude.count(item.first))
{
continue;
}
auto curDist = item.first ^ target;
if (curDist < mindist)
{
mindist = curDist;
result = item.first;
}
--N;
}
return mindist < maxdist;
}
return result.size() == expecting;
}
bool
GetManyNearExcluding(
const Key_t& target,
std::set<Key_t>& result,
size_t N,
const std::set<Key_t>& exclude) const
bool
FindCloseExcluding(const Key_t& target, Key_t& result, const std::set<Key_t>& exclude) const
{
Key_t maxdist;
maxdist.Fill(0xff);
Key_t mindist;
mindist.Fill(0xff);
for (const auto& item : nodes)
{
std::set<Key_t> s(exclude.begin(), exclude.end());
Key_t peer;
while (N--)
if (exclude.count(item.first))
{
if (!FindCloseExcluding(target, peer, s))
{
return false;
}
s.insert(peer);
result.insert(peer);
continue;
}
return true;
}
void
PutNode(const Val_t& val)
{
auto itr = nodes.find(val.ID);
if (itr == nodes.end() || itr->second < val)
auto curDist = item.first ^ target;
if (curDist < mindist)
{
nodes[val.ID] = val;
mindist = curDist;
result = item.first;
}
}
return mindist < maxdist;
}
bool
GetManyNearExcluding(
const Key_t& target,
std::set<Key_t>& result,
size_t N,
const std::set<Key_t>& exclude) const
{
std::set<Key_t> s(exclude.begin(), exclude.end());
void
DelNode(const Key_t& key)
Key_t peer;
while (N--)
{
auto itr = nodes.find(key);
if (itr != nodes.end())
if (!FindCloseExcluding(target, peer, s))
{
nodes.erase(itr);
return false;
}
s.insert(peer);
result.insert(peer);
}
return true;
}
bool
HasNode(const Key_t& key) const
void
PutNode(const Val_t& val)
{
auto itr = nodes.find(val.ID);
if (itr == nodes.end() || itr->second < val)
{
return nodes.find(key) != nodes.end();
nodes[val.ID] = val;
}
}
// remove all nodes who's key matches a predicate
template <typename Predicate>
void
RemoveIf(Predicate pred)
void
DelNode(const Key_t& key)
{
auto itr = nodes.find(key);
if (itr != nodes.end())
{
auto itr = nodes.begin();
while (itr != nodes.end())
{
if (pred(itr->first))
itr = nodes.erase(itr);
else
++itr;
}
nodes.erase(itr);
}
}
bool
HasNode(const Key_t& key) const
{
return nodes.find(key) != nodes.end();
}
template <typename Visit_t>
void
ForEachNode(Visit_t visit)
// remove all nodes who's key matches a predicate
template <typename Predicate>
void
RemoveIf(Predicate pred)
{
auto itr = nodes.begin();
while (itr != nodes.end())
{
for (const auto& item : nodes)
{
visit(item.second);
}
if (pred(itr->first))
itr = nodes.erase(itr);
else
++itr;
}
}
void
Clear()
template <typename Visit_t>
void
ForEachNode(Visit_t visit)
{
for (const auto& item : nodes)
{
nodes.clear();
visit(item.second);
}
}
BucketStorage_t nodes;
Random_t random;
};
} // namespace dht
} // namespace llarp
void
Clear()
{
nodes.clear();
}
BucketStorage_t nodes;
Random_t random;
};
} // namespace llarp::dht

@ -483,9 +483,7 @@ namespace llarp::dht
void
DHTMessageHandler::DHTSendTo(const RouterID& peer, AbstractDHTMessage* msg, bool)
{
DHTImmediateMessage m;
m.msgs.emplace_back(msg);
router->SendToOrQueue(peer, m);
router->SendToOrQueue(peer, msg);
auto now = Now();
router->PersistSessionUntil(peer, now + 1min);
}
@ -659,7 +657,7 @@ namespace llarp::dht
llarp_time_t
DHTMessageHandler::Now() const
{
return router->Now();
return router->now();
}
std::unique_ptr<AbstractDHTMessageHandler>

@ -1,211 +0,0 @@
#ifndef LLARP_DHT_CONTEXT
#define LLARP_DHT_CONTEXT
#include "bucket.hpp"
#include "dht.h"
#include "key.hpp"
#include "message.hpp"
#include <llarp/dht/messages/findintro.hpp>
#include "node.hpp"
#include "tx.hpp"
#include "txholder.hpp"
#include "txowner.hpp"
#include <llarp/service/intro_set.hpp>
#include <llarp/util/time.hpp>
#include <llarp/util/status.hpp>
#include <memory>
#include <set>
namespace llarp
{
struct Router;
namespace dht
{
/// number of routers to publish to
static constexpr size_t IntroSetRelayRedundancy = 2;
/// number of dht locations handled per relay
static constexpr size_t IntroSetRequestsPerRelay = 2;
static constexpr size_t IntroSetStorageRedundancy =
(IntroSetRelayRedundancy * IntroSetRequestsPerRelay);
struct AbstractDHTMessageHandler /* : public AbstractMessageHandler */
{
using PendingIntrosetLookups = TXHolder<TXOwner, service::EncryptedIntroSet>;
using PendingRouterLookups = TXHolder<RouterID, RouterContact>;
using PendingExploreLookups = TXHolder<RouterID, RouterID>;
virtual ~AbstractDHTMessageHandler() = 0;
virtual bool
LookupRouter(const RouterID& target, RouterLookupHandler result) = 0;
virtual void
LookupRouterRecursive(
const RouterID& target,
const Key_t& whoasked,
uint64_t whoaskedTX,
const Key_t& askpeer,
RouterLookupHandler result = nullptr) = 0;
/// Ask a Service Node to perform an Introset lookup for us
virtual void
LookupIntroSetRelayed(
const Key_t& target,
const Key_t& whoasked,
uint64_t whoaskedTX,
const Key_t& askpeer,
uint64_t relayOrder,
service::EncryptedIntroSetLookupHandler result =
service::EncryptedIntroSetLookupHandler()) = 0;
/// Directly as a Service Node for an Introset
virtual void
LookupIntroSetDirect(
const Key_t& target,
const Key_t& whoasked,
uint64_t whoaskedTX,
const Key_t& askpeer,
service::EncryptedIntroSetLookupHandler result =
service::EncryptedIntroSetLookupHandler()) = 0;
virtual bool
HasRouterLookup(const RouterID& target) const = 0;
/// issue dht lookup for router via askpeer and send reply to local path
virtual void
LookupRouterForPath(
const RouterID& target, uint64_t txid, const PathID_t& path, const Key_t& askpeer) = 0;
virtual void
LookupIntroSetForPath(
const Key_t& addr,
uint64_t txid,
const PathID_t& path,
const Key_t& askpeer,
uint64_t relayOrder) = 0;
virtual void
DHTSendTo(const RouterID& peer, AbstractDHTMessage* msg, bool keepalive = true) = 0;
/// get routers closest to target excluding requester
virtual bool
HandleExploritoryRouterLookup(
const Key_t& requester,
uint64_t txid,
const RouterID& target,
std::vector<std::unique_ptr<AbstractDHTMessage>>& reply) = 0;
/// handle rc lookup from requester for target
virtual void
LookupRouterRelayed(
const Key_t& requester,
uint64_t txid,
const Key_t& target,
bool recursive,
std::vector<std::unique_ptr<AbstractDHTMessage>>& replies) = 0;
virtual bool
RelayRequestForPath(const PathID_t& localPath, const AbstractDHTMessage& msg) = 0;
/// send introset to peer from source with S counter and excluding peers
virtual void
PropagateLocalIntroSet(
const PathID_t& path,
uint64_t sourceTX,
const service::EncryptedIntroSet& introset,
const Key_t& peer,
uint64_t relayOrder) = 0;
/// send introset to peer from source with S counter and excluding peers
virtual void
PropagateIntroSetTo(
const Key_t& source,
uint64_t sourceTX,
const service::EncryptedIntroSet& introset,
const Key_t& peer,
uint64_t relayOrder) = 0;
virtual void
Init(const Key_t& us, Router* router) = 0;
virtual std::optional<llarp::service::EncryptedIntroSet>
GetIntroSetByLocation(const Key_t& location) const = 0;
virtual llarp_time_t
Now() const = 0;
virtual void
ExploreNetworkVia(const Key_t& peer) = 0;
virtual llarp::Router*
GetRouter() const = 0;
virtual bool
GetRCFromNodeDB(const Key_t& k, llarp::RouterContact& rc) const = 0;
virtual const Key_t&
OurKey() const = 0;
virtual PendingIntrosetLookups&
pendingIntrosetLookups() = 0;
virtual const PendingIntrosetLookups&
pendingIntrosetLookups() const = 0;
virtual PendingRouterLookups&
pendingRouterLookups() = 0;
virtual const PendingRouterLookups&
pendingRouterLookups() const = 0;
virtual PendingExploreLookups&
pendingExploreLookups() = 0;
virtual const PendingExploreLookups&
pendingExploreLookups() const = 0;
virtual Bucket<ISNode>*
services() = 0;
virtual bool&
AllowTransit() = 0;
virtual const bool&
AllowTransit() const = 0;
virtual Bucket<RCNode>*
Nodes() const = 0;
virtual void
PutRCNodeAsync(const RCNode& val) = 0;
virtual void
DelRCNodeAsync(const Key_t& val) = 0;
virtual util::StatusObject
ExtractStatus() const = 0;
virtual void
StoreRC(const RouterContact rc) const = 0;
virtual bool
handle_message(
const AbstractDHTMessage&, std::vector<std::unique_ptr<dht::AbstractDHTMessage>>&) = 0;
};
std::unique_ptr<AbstractDHTMessageHandler>
make_handler();
} // namespace dht
} // namespace llarp
struct llarp_dht_context
{
std::unique_ptr<llarp::dht::AbstractDHTMessageHandler> impl;
llarp::Router* parent;
llarp_dht_context(llarp::Router* router);
};
#endif

@ -19,7 +19,7 @@ namespace llarp
auto router = parent->GetRouter();
if (router)
{
router->NotifyRouterEvent<tooling::FindRouterSentEvent>(router->pubkey(), *msg);
router->notify_router_event<tooling::FindRouterSentEvent>(router->pubkey(), *msg);
}
parent->DHTSendTo(peer.node.as_array(), msg);
}

@ -15,7 +15,7 @@ namespace llarp
explicit Key_t(const byte_t* buf) : AlignedBuffer<SIZE>(buf)
{}
explicit Key_t(const Data& data) : AlignedBuffer<SIZE>(data)
explicit Key_t(const std::array<byte_t, SIZE>& data) : AlignedBuffer<SIZE>(data)
{}
explicit Key_t(const AlignedBuffer<SIZE>& data) : AlignedBuffer<SIZE>(data)

@ -1,24 +0,0 @@
#pragma once
#include <llarp/dht/message.hpp>
#include <llarp/router_version.hpp>
namespace llarp::dht
{
struct ConsensusMessage
{
/// H
ShortHash m_Hash;
/// K
std::vector<RouterID> m_Keys;
/// N
uint64_t m_NumberOfEntries;
/// O
uint64_t m_EntryOffset;
/// T
uint64_t m_TxID;
/// U
llarp_time_t m_NextUpdateRequired;
/// V
RouterVersion m_RotuerVersion;
};
} // namespace llarp::dht

@ -84,7 +84,7 @@ namespace llarp::dht
// we are relaying this message for e.g. a client
if (relayed)
{
if (relayOrder >= IntroSetStorageRedundancy)
if (relayOrder >= INTROSET_STORAGE_REDUNDANCY)
{
llarp::LogWarn("Invalid relayOrder received: ", relayOrder);
replies.emplace_back(new GotIntroMessage({}, txID));

@ -44,10 +44,9 @@ namespace llarp::dht
bool
FindNameMessage::handle_message(
AbstractDHTMessageHandler& dht,
std::vector<std::unique_ptr<AbstractDHTMessage>>& replies) const
AbstractDHTMessageHandler&, std::vector<std::unique_ptr<AbstractDHTMessage>>&) const
{
(void)replies;
/* (void)replies;
auto router = dht.GetRouter();
if (pathID.IsZero() or not router->IsServiceNode())
return false;
@ -67,7 +66,7 @@ namespace llarp::dht
new GotNameMessage(dht::Key_t{}, TxID, service::EncryptedName{}));
}
path->SendRoutingMessage(msg, router);
});
}); */
return true;
}

@ -30,7 +30,7 @@ namespace llarp::dht
for (const auto& introset : found)
{
if (!introset.Verify(dht.Now()))
if (!introset.verify(dht.Now()))
{
LogWarn(
"Invalid introset while handling direct GotIntro "

@ -2,7 +2,6 @@
#include <llarp/dht/context.hpp>
#include "gotintro.hpp"
#include <llarp/messages/dht_immediate.hpp>
#include <llarp/router/router.hpp>
#include <llarp/routing/path_dht_message.hpp>
#include <llarp/nodedb.hpp>
@ -59,10 +58,10 @@ namespace llarp::dht
const llarp::dht::Key_t addr{introset.derivedSigningKey.data()};
auto router = dht.GetRouter();
router->NotifyRouterEvent<tooling::PubIntroReceivedEvent>(
router->notify_router_event<tooling::PubIntroReceivedEvent>(
router->pubkey(), Key_t(relayed ? router->pubkey() : From.data()), addr, txID, relayOrder);
if (!introset.Verify(now))
if (!introset.verify(now))
{
llarp::LogWarn("Received PublishIntroMessage with invalid introset: ", introset);
// don't propogate or store
@ -80,8 +79,8 @@ namespace llarp::dht
// identify closest 4 routers
auto closestRCs =
dht.GetRouter()->node_db()->FindManyClosestTo(addr, IntroSetStorageRedundancy);
if (closestRCs.size() != IntroSetStorageRedundancy)
dht.GetRouter()->node_db()->FindManyClosestTo(addr, INTROSET_STORAGE_REDUNDANCY);
if (closestRCs.size() != INTROSET_STORAGE_REDUNDANCY)
{
llarp::LogWarn("Received PublishIntroMessage but only know ", closestRCs.size(), " nodes");
replies.emplace_back(new GotIntroMessage({}, txID));
@ -92,7 +91,7 @@ namespace llarp::dht
// function to identify the closest 4 routers we know of for this introset
auto propagateIfNotUs = [&](size_t index) {
assert(index < IntroSetStorageRedundancy);
assert(index < INTROSET_STORAGE_REDUNDANCY);
const auto& rc = closestRCs[index];
const Key_t peer{rc.pubkey};
@ -120,7 +119,7 @@ namespace llarp::dht
if (relayed)
{
if (relayOrder >= IntroSetStorageRedundancy)
if (relayOrder >= INTROSET_STORAGE_REDUNDANCY)
{
llarp::LogWarn("Received PublishIntroMessage with invalid relayOrder: ", relayOrder);
replies.emplace_back(new GotIntroMessage({}, txID));
@ -179,7 +178,7 @@ namespace llarp::dht
try
{
btdp.append("A", "I");
btdp.append("T", introset.ToString());
btdp.append("I", introset.ToString());
btdp.append("O", relayOrder);
btdp.append("R", relayed ? 1 : 0);
btdp.append("T", txID);

@ -29,7 +29,7 @@ namespace llarp::dht
return false;
}
const llarp_time_t now = llarp::time_now_ms();
return value.Verify(now);
return value.verify(now);
}
void

@ -26,7 +26,7 @@ namespace llarp
bool
ServiceAddressLookup::Validate(const service::EncryptedIntroSet& value) const
{
if (!value.Verify(parent->Now()))
if (!value.verify(parent->Now()))
{
llarp::LogWarn("Got invalid introset from service lookup");
return false;

@ -10,7 +10,7 @@ namespace llarp
bool
TagLookup::Validate(const service::EncryptedIntroSet& introset) const
{
if (!introset.Verify(parent->Now()))
if (!introset.verify(parent->Now()))
{
llarp::LogWarn("got invalid introset from tag lookup");
return false;

@ -8,33 +8,24 @@ namespace llarp::routing
bool
ObtainExitMessage::Sign(const llarp::SecretKey& sk)
{
std::array<byte_t, 1024> tmp;
llarp_buffer_t buf(tmp);
pubkey = seckey_topublic(sk);
sig.Zero();
auto bte = bt_encode();
buf.write(bte.begin(), bte.end());
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->sign(sig, sk, buf);
return CryptoManager::instance()->sign(
sig, sk, reinterpret_cast<uint8_t*>(bte.data()), bte.size());
}
bool
ObtainExitMessage::Verify() const
{
std::array<byte_t, 1024> tmp;
llarp_buffer_t buf(tmp);
ObtainExitMessage copy;
copy = *this;
copy.sig.Zero();
auto bte = copy.bt_encode();
buf.write(bte.begin(), bte.end());
// rewind buffer
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->verify(pubkey, buf, sig);
return CryptoManager::instance()->verify(
pubkey, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), sig);
}
std::string
@ -150,32 +141,24 @@ namespace llarp::routing
bool
GrantExitMessage::Verify(const llarp::PubKey& pk) const
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
GrantExitMessage copy;
copy = *this;
copy.sig.Zero();
auto bte = copy.bt_encode();
buf.write(bte.begin(), bte.end());
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->verify(pk, buf, sig);
return CryptoManager::instance()->verify(
pk, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), sig);
}
bool
GrantExitMessage::Sign(const llarp::SecretKey& sk)
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
sig.Zero();
nonce.Randomize();
auto bte = bt_encode();
buf.write(bte.begin(), bte.end());
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->sign(sig, sk, buf);
return CryptoManager::instance()->sign(
sig, sk, reinterpret_cast<uint8_t*>(bte.data()), bte.size());
}
bool
@ -239,32 +222,24 @@ namespace llarp::routing
bool
RejectExitMessage::Sign(const llarp::SecretKey& sk)
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
sig.Zero();
nonce.Randomize();
auto bte = bt_encode();
buf.write(bte.begin(), bte.end());
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->sign(sig, sk, buf);
return CryptoManager::instance()->sign(
sig, sk, reinterpret_cast<uint8_t*>(bte.data()), bte.size());
}
bool
RejectExitMessage::Verify(const llarp::PubKey& pk) const
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
RejectExitMessage copy;
copy = *this;
copy.sig.Zero();
auto bte = copy.bt_encode();
buf.write(bte.begin(), bte.end());
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->verify(pk, buf, sig);
return CryptoManager::instance()->verify(
pk, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), sig);
}
bool
@ -314,33 +289,24 @@ namespace llarp::routing
bool
UpdateExitMessage::Verify(const llarp::PubKey& pk) const
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
UpdateExitMessage copy;
copy = *this;
copy.sig.Zero();
auto bte = copy.bt_encode();
buf.write(bte.begin(), bte.end());
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->verify(pk, buf, sig);
return CryptoManager::instance()->verify(
pk, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), sig);
}
bool
UpdateExitMessage::Sign(const llarp::SecretKey& sk)
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
nonce.Randomize();
auto bte = bt_encode();
buf.write(bte.begin(), bte.end());
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->sign(sig, sk, buf);
return CryptoManager::instance()->sign(
sig, sk, reinterpret_cast<uint8_t*>(bte.data()), bte.size());
}
bool
@ -427,32 +393,24 @@ namespace llarp::routing
bool
CloseExitMessage::Verify(const llarp::PubKey& pk) const
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
CloseExitMessage copy;
copy = *this;
copy.sig.Zero();
auto bte = copy.bt_encode();
buf.write(bte.begin(), bte.end());
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->verify(pk, buf, sig);
return CryptoManager::instance()->verify(
pk, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), sig);
}
bool
CloseExitMessage::Sign(const llarp::SecretKey& sk)
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
sig.Zero();
nonce.Randomize();
auto bte = bt_encode();
buf.write(bte.begin(), bte.end());
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->sign(sig, sk, buf);
return CryptoManager::instance()->sign(
sig, sk, reinterpret_cast<uint8_t*>(bte.data()), bte.size());
}
bool

@ -22,7 +22,7 @@ namespace llarp::exit
, m_ExitRouter{routerId}
, m_WritePacket{std::move(writepkt)}
, m_Counter{0}
, m_LastUse{r->Now()}
, m_LastUse{r->now()}
, m_BundleRC{false}
, m_Parent{parent}
{
@ -87,7 +87,7 @@ namespace llarp::exit
bool
BaseSession::CheckPathDead(path::Path_ptr, llarp_time_t dlt)
{
return dlt >= path::alive_timeout;
return dlt >= path::ALIVE_TIMEOUT;
}
void
@ -214,7 +214,7 @@ namespace llarp::exit
llarp::net::IPPacket pkt{buf.view_all()};
if (pkt.empty())
return false;
m_LastUse = m_router->Now();
m_LastUse = m_router->now();
m_Downstream.emplace(counter, pkt);
return true;
}
@ -225,7 +225,7 @@ namespace llarp::exit
BaseSession::HandleTrafficDrop(llarp::path::Path_ptr p, const PathID_t& path, uint64_t s)
{
llarp::LogError("dropped traffic on exit ", m_ExitRouter, " S=", s, " P=", path);
p->EnterState(path::ePathIgnore, m_router->Now());
p->EnterState(path::ePathIgnore, m_router->now());
return true;
}
@ -283,7 +283,7 @@ namespace llarp::exit
bool
BaseSession::FlushUpstream()
{
auto now = m_router->Now();
auto now = m_router->now();
auto path = PickEstablishedPath(llarp::path::ePathRoleExit);
if (path)
{

@ -27,7 +27,7 @@ namespace llarp
using SessionReadyFunc = std::function<void(BaseSession_ptr)>;
static constexpr auto LifeSpan = path::default_lifetime;
static constexpr auto LifeSpan = path::DEFAULT_LIFETIME;
/// a persisting exit session with an exit router
struct BaseSession : public llarp::path::Builder,

@ -354,7 +354,7 @@ namespace llarp::handlers
llarp_time_t
ExitEndpoint::Now() const
{
return router->Now();
return router->now();
}
bool
@ -650,7 +650,7 @@ namespace llarp::handlers
void
ExitEndpoint::MarkIPActive(huint128_t ip)
{
ip_activity[ip] = GetRouter()->Now();
ip_activity[ip] = GetRouter()->now();
}
void

@ -279,18 +279,18 @@ namespace llarp
{
if (conf.m_reachable)
{
m_PublishIntroSet = true;
_publish_introset = true;
LogInfo(Name(), " setting to be reachable by default");
}
else
{
m_PublishIntroSet = false;
_publish_introset = false;
LogInfo(Name(), " setting to be not reachable by default");
}
if (conf.m_AuthType == service::AuthType::eAuthTypeFile)
{
m_AuthPolicy = service::MakeFileAuthPolicy(m_router, conf.m_AuthFiles, conf.m_AuthFileType);
_auth_policy = service::MakeFileAuthPolicy(m_router, conf.m_AuthFiles, conf.m_AuthFileType);
}
else if (conf.m_AuthType != service::AuthType::eAuthTypeNone)
{
@ -308,7 +308,7 @@ namespace llarp
router()->lmq(),
shared_from_this());
auth->Start();
m_AuthPolicy = std::move(auth);
_auth_policy = std::move(auth);
}
m_DnsConfig = dnsConf;
@ -656,7 +656,7 @@ namespace llarp
if (HasExit())
{
std::string s;
m_ExitMap.ForEachEntry([&s](const auto& range, const auto& exit) {
_exit_map.ForEachEntry([&s](const auto& range, const auto& exit) {
fmt::format_to(std::back_inserter(s), "{}={}; ", range, exit);
});
msg.AddTXTReply(std::move(s));
@ -727,7 +727,7 @@ namespace llarp
const auto subdomain = msg.questions[0].Subdomains();
if (subdomain == "exit" and HasExit())
{
m_ExitMap.ForEachEntry(
_exit_map.ForEachEntry(
[&msg](const auto&, const auto& exit) { msg.AddCNAMEReply(exit.ToString(), 1); });
}
else
@ -783,7 +783,7 @@ namespace llarp
{
if (HasExit())
{
m_ExitMap.ForEachEntry(
_exit_map.ForEachEntry(
[&msg](const auto&, const auto& exit) { msg.AddCNAMEReply(exit.ToString()); });
msg.AddINReply(ip, isV6);
}
@ -794,7 +794,7 @@ namespace llarp
}
else
{
msg.AddCNAMEReply(m_Identity.pub.Name(), 1);
msg.AddCNAMEReply(_identity.pub.Name(), 1);
msg.AddINReply(ip, isV6);
}
}
@ -999,7 +999,7 @@ namespace llarp
llarp::LogInfo(Name(), " set ", m_IfName, " to have address ", m_OurIP);
llarp::LogInfo(Name(), " allocated up to ", m_MaxIP, " on range ", m_OurRange);
const service::Address ourAddr = m_Identity.pub.Addr();
const service::Address ourAddr = _identity.pub.Addr();
if (not MapAddress(ourAddr, GetIfAddr(), false))
{
@ -1138,7 +1138,7 @@ namespace llarp
// build up our candidates to choose
std::unordered_set<service::Address> candidates;
for (const auto& entry : m_ExitMap.FindAllEntries(ip))
for (const auto& entry : _exit_map.FindAllEntries(ip))
{
// in the event the exit's range is a bogon range, make sure the ip is located in that range
// to allow it
@ -1186,7 +1186,7 @@ namespace llarp
}
}
if (m_state->m_ExitEnabled)
if (_state->m_ExitEnabled)
{
dst = net::ExpandV4(net::TruncateV6(dst));
}
@ -1238,8 +1238,8 @@ namespace llarp
else
{
to = service::Address{itr->second.as_array()};
type = m_state->m_ExitEnabled and src != m_OurIP ? service::ProtocolType::Exit
: pkt.ServiceProtocol();
type = _state->m_ExitEnabled and src != m_OurIP ? service::ProtocolType::Exit
: pkt.ServiceProtocol();
}
// prepare packet for insertion into network
@ -1347,7 +1347,7 @@ namespace llarp
if (not pkt.Load(buf))
return false;
if (m_state->m_ExitEnabled)
if (_state->m_ExitEnabled)
{
// exit side from exit

@ -0,0 +1,4 @@
#include "link_endpoints.hpp"
namespace llarp
{} // namespace llarp

@ -0,0 +1,4 @@
#pragma once
namespace llarp
{} // namespace llarp

@ -127,6 +127,19 @@ namespace llarp
return ep.for_each_connection(func);
}
void
LinkManager::register_commands(std::shared_ptr<oxen::quic::BTRequestStream>& s)
{
for (const auto& [name, func] : rpc_commands)
{
s->register_command(name, [this, f = func](oxen::quic::message m) {
router.loop()->call([this, func = f, msg = std::move(m)]() mutable {
std::invoke(func, this, std::move(msg));
});
});
}
}
std::shared_ptr<oxen::quic::Endpoint>
LinkManager::startup_endpoint()
{
@ -136,7 +149,6 @@ namespace llarp
- connection close callback
- stream constructor callback
- will return a BTRequestStream on the first call to get_new_stream<BTRequestStream>
*/
return quic->endpoint(
router.public_ip(),
@ -150,8 +162,9 @@ namespace llarp
std::optional<int64_t> id) -> std::shared_ptr<oxen::quic::Stream> {
if (id && id == 0)
{
return std::make_shared<oxen::quic::BTRequestStream>(
c, e, [this](oxen::quic::message msg) { return recv_control_message(msg); });
auto s = std::make_shared<oxen::quic::BTRequestStream>();
register_commands(s);
return s;
}
return std::make_shared<oxen::quic::Stream>(c, e);
});
@ -168,20 +181,22 @@ namespace llarp
bool
LinkManager::send_control_message(
const RouterID& remote, std::string endpoint, std::string body, bool is_request)
const RouterID& remote,
std::string endpoint,
std::string body,
std::function<void(oxen::quic::message)> func)
{
if (is_stopping)
return false;
if (auto conn = ep.get_conn(remote); conn)
{
(is_request) ? conn->control_stream->request(endpoint, body)
: conn->control_stream->command(endpoint, body);
conn->control_stream->command(endpoint, body, std::move(func));
return true;
}
router.loop()->call([&]() {
auto pending = PendingControlMessage(body, endpoint);
auto pending = PendingControlMessage(body, endpoint, func);
auto [itr, b] = pending_conn_msg_queue.emplace(remote, MessageQueue());
itr->second.push_back(std::move(pending));
@ -304,8 +319,7 @@ namespace llarp
if (m.is_control)
{
auto& msg = reinterpret_cast<PendingControlMessage&>(m);
msg.is_request ? ep.conns[rid]->control_stream->request(msg.endpoint, msg.body)
: ep.conns[rid]->control_stream->command(msg.endpoint, msg.body);
ep.conns[rid]->control_stream->command(msg.endpoint, msg.body, msg.func);
}
else
{
@ -476,44 +490,204 @@ namespace llarp
}
void
LinkManager::recv_control_message(oxen::quic::message msg)
LinkManager::handle_find_name(oxen::quic::message m)
{
try
{
oxenc::bt_dict_consumer btdp{m.body()};
std::string name_hash, tx_id;
if (btdp.skip_until("H"))
name_hash = btdp.consume_string();
if (btdp.skip_until("T"))
tx_id = btdp.consume_string();
router.rpc_client()->LookupLNSNameHash(name_hash, [](auto /* maybe */) {
});
}
catch (const std::exception& e)
{
log::warning(link_cat, "Exception: {}", e.what());
m.respond("ERROR", true);
}
}
void
LinkManager::handle_find_router(oxen::quic::message)
{}
void
LinkManager::handle_publish_intro(oxen::quic::message m)
{
// if the message is not expired, it will pass this conditional
if (msg)
std::string introset, tx_id, derived_signing_key, sig;
uint64_t is_relayed, relay_order;
std::chrono::milliseconds signed_at;
try
{
std::string ep{msg.endpoint()}, body{msg.body()};
bool is_request = (msg.type() == "Q"sv) ? true : false;
oxenc::bt_dict_consumer btdc_a{m.body()};
if (btdc_a.skip_until("I"))
introset = btdc_a.consume_string();
if (btdc_a.skip_until("O"))
relay_order = btdc_a.consume_integer<uint64_t>();
if (auto itr = rpc_map.find(ep); itr != rpc_map.end())
if (btdc_a.skip_until("R"))
is_relayed = btdc_a.consume_integer<uint64_t>();
if (btdc_a.skip_until("T"))
tx_id = btdc_a.consume_string();
oxenc::bt_dict_consumer btdc_b{introset};
if (btdc_b.skip_until("d"))
derived_signing_key = btdc_b.consume_string();
if (btdc_b.skip_until("s"))
signed_at = std::chrono::milliseconds{btdc_b.consume_integer<int64_t>()};
if (btdc_b.skip_until("z"))
sig = btdc_b.consume_string();
}
catch (const std::exception& e)
{
log::warning(link_cat, "Exception: {}", e.what());
m.respond("ERROR", true);
return;
}
const auto now = router.now();
const auto addr = dht::Key_t{reinterpret_cast<uint8_t*>(derived_signing_key.data())};
const auto local_key = router.rc().pubkey;
if (not service::EncryptedIntroSet::verify(introset, derived_signing_key, sig))
{
log::error(link_cat, "Received PublishIntroMessage with invalid introset: {}", introset);
m.respond("INVALID INTROSET", true);
return;
}
if (now + service::MAX_INTROSET_TIME_DELTA > signed_at + path::DEFAULT_LIFETIME)
{
log::error(link_cat, "Received PublishIntroMessage with expired introset: {}", introset);
m.respond("EXPIRED INTROSET", true);
return;
}
auto closest_rcs = router.node_db()->FindManyClosestTo(addr, INTROSET_STORAGE_REDUNDANCY);
if (closest_rcs.size() != INTROSET_STORAGE_REDUNDANCY)
{
log::error(
link_cat, "Received PublishIntroMessage but only know {} nodes", closest_rcs.size());
m.respond("INSUFFICIENT NODES", true);
return;
}
if (is_relayed)
{
if (relay_order >= INTROSET_STORAGE_REDUNDANCY)
{
router.loop()->call([&]() {
// execute mapped callback
auto maybe_response = itr->second(body);
log::error(
link_cat, "Received PublishIntroMessage with invalide relay order: {}", relay_order);
m.respond("INVALID ORDER", true);
return;
}
if (is_request)
{
if (maybe_response)
{
// respond here
msg.respond(msg.rid(), *maybe_response);
}
// TODO: revisit the logic of these conditionals after defining the callback functions
// to see if returning/taking optionals makes sense
}
});
log::info(link_cat, "Relaying PublishIntroMessage for {} (TXID: {})", addr, tx_id);
const auto& peer_rc = closest_rcs[relay_order];
const auto& peer_key = peer_rc.pubkey;
if (peer_key == local_key)
{
log::info(
link_cat,
"Received PublishIntroMessage in which we are peer index {}.. storing introset",
relay_order);
// TODO: replace this concept
// dht->services()->PutNode(introset);
// TODO: should this be a call to send_control_message instead?
m.respond("got_intro");
}
else
{
msg.respond(msg.rid(), "INVALID REQUEST", true);
return;
log::info(
link_cat, "Received PublishIntroMessage; propagating to peer index {}", relay_order);
send_control_message(
peer_key, "publish_intro", std::move(introset), [this](oxen::quic::message m) {
return handle_got_intro(std::move(m));
});
}
return;
}
else
int rc_index = -1, index = 0;
for (const auto& rc : closest_rcs)
{
if (rc.pubkey == local_key)
{
rc_index = index;
break;
}
++index;
}
if (rc_index >= 0)
{
// RPC request was sent out but we received no response
log::info(link_cat, "RPC request (RID: {}) timed out", msg.rid());
log::info(link_cat, "Received PublishIntroMessage for {} (TXID: {}); we are candidate {}");
// TODO: should this be a call to send_control_message instead?
m.respond("got_intro");
}
else
log::warning(
link_cat,
"Received non-relayed PublishIntroMessage from {}; we are not the candidate",
addr);
}
void
LinkManager::handle_find_intro(oxen::quic::message)
{}
void
LinkManager::handle_path_confirm(oxen::quic::message)
{}
void
LinkManager::handle_path_latency(oxen::quic::message)
{}
void
LinkManager::handle_update_exit(oxen::quic::message)
{}
void
LinkManager::handle_obtain_exit(oxen::quic::message)
{}
void
LinkManager::handle_close_exit(oxen::quic::message)
{}
void
LinkManager::handle_got_intro(oxen::quic::message)
{}
void
LinkManager::handle_got_name(oxen::quic::message)
{}
void
LinkManager::handle_got_router(oxen::quic::message)
{}
} // namespace llarp

@ -1,6 +1,7 @@
#pragma once
#include "connection.hpp"
#include "link_endpoints.hpp"
#include <llarp/router/rc_lookup_handler.hpp>
#include <llarp/router_contact.hpp>
@ -122,10 +123,11 @@ namespace llarp
struct PendingControlMessage : PendingMessage
{
std::string endpoint;
bool is_request{false}; // true if request, false if command
std::function<void(oxen::quic::message)> func;
PendingControlMessage(std::string b, std::string e, bool request = true)
: PendingMessage(b, true), endpoint{std::move(e)}, is_request{request}
PendingControlMessage(
std::string b, std::string e, std::function<void(oxen::quic::message)> f = nullptr)
: PendingMessage(b, true), endpoint{std::move(e)}, func{std::move(f)}
{}
};
@ -141,7 +143,10 @@ namespace llarp
// set is_request to true for RPC requests, false for RPC commands
bool
send_control_message(
const RouterID& remote, std::string endpoint, std::string body, bool is_request = true);
const RouterID& remote,
std::string endpoint,
std::string body,
std::function<void(oxen::quic::message)> = nullptr);
bool
send_data_message(const RouterID& remote, std::string data);
@ -149,18 +154,6 @@ namespace llarp
private:
friend struct link::Endpoint;
const std::unordered_map<
std::string,
std::function<std::optional<std::string>(std::optional<std::string>)>>
rpc_map{
/** TODO:
key: RPC endpoint name
value: function that takes command body as parameter
returns: commands will return std::nullopt while requests will return a response
*/
};
std::atomic<bool> is_stopping;
// DISCUSS: is this necessary? can we reduce the amount of locking and nuke this
mutable util::Mutex m; // protects persisting_conns
@ -171,7 +164,7 @@ namespace llarp
// holds any messages we attempt to send while connections are establishing
std::unordered_map<RouterID, MessageQueue> pending_conn_msg_queue;
util::DecayingHashSet<RouterID> clients{path::default_lifetime};
util::DecayingHashSet<RouterID> clients{path::DEFAULT_LIFETIME};
RCLookupHandler* rc_lookup;
std::shared_ptr<NodeDB> node_db;
@ -202,6 +195,9 @@ namespace llarp
std::shared_ptr<oxen::quic::Endpoint>
startup_endpoint();
void
register_commands(std::shared_ptr<oxen::quic::BTRequestStream>& s);
public:
const link::Endpoint&
endpoint()
@ -276,6 +272,40 @@ namespace llarp
size_t min_connected_routers = 4;
/// hard upperbound limit on the number of router to router connections
size_t max_connected_routers = 6;
private:
// Control message
// Bridge (relay) message
void handle_publish_intro(oxen::quic::message);
void handle_find_intro(oxen::quic::message);
void handle_find_name(oxen::quic::message);
void handle_path_confirm(oxen::quic::message);
void handle_path_latency(oxen::quic::message);
void handle_update_exit(oxen::quic::message);
void handle_obtain_exit(oxen::quic::message);
void handle_close_exit(oxen::quic::message);
// Control and bridge message (separate into two type)
// Unsure
void handle_find_router(oxen::quic::message); // maybe both
std::unordered_map<std::string, void (LinkManager::*)(oxen::quic::message)> rpc_commands = {
{"find_name", &LinkManager::handle_find_name},
{"find_router", &LinkManager::handle_find_router},
{"publish_intro", &LinkManager::handle_publish_intro},
{"find_intro", &LinkManager::handle_find_intro},
{"path_confirm", &LinkManager::handle_path_confirm},
{"path_latency", &LinkManager::handle_path_latency},
{"update_exit", &LinkManager::handle_update_exit},
{"obtain_exit", &LinkManager::handle_obtain_exit},
{"close_exit", &LinkManager::handle_close_exit}};
// response handling functions
void handle_got_intro(oxen::quic::message);
void handle_got_name(oxen::quic::message);
void handle_got_router(oxen::quic::message);
};
namespace link
@ -335,4 +365,23 @@ namespace llarp
- if connection times out, flush queue
- TOCHECK: is priority used at all??
std::unordered_map<std::string, void (llarp::link::LinkManager::*)(oxen::quic::message)>
rpc_commands = {
{"find_name", &handle_find_name},
{"find_router", &handle_find_router},
// ...
};
for (const auto& [name, mfn] : rpc_commands)
bparser.add_command(name, [this, mfn] (oxen::quic::message m) {
router->call([this, mfn, m=std::move(m)] mutable {
try {
std::invoke(mfn, this, std::move(m));
} catch (const std::exception& e) {
m.respond("Error: "s + e.what(), true);
}
});
});
*/

@ -1,84 +0,0 @@
#include "dht_immediate.hpp"
#include <llarp/router/router.hpp>
#include <llarp/dht/context.hpp>
namespace llarp
{
void
DHTImmediateMessage::clear()
{
msgs.clear();
version = 0;
}
/** Note: this is where AbstractDHTMessage::bt_encode() is called. Contextually, this is a
bit confusing as it is within the ::bt_encode() method of DHTImmediateMessage, which is
not an AbstractDHTMessage, but an AbstractLinkMessage. To see why AbstractLinkMessage
overrides the ::bt_encode() that returns an std::string, see the comment in llarp/router/
outbound_message_handler.cpp above OutboundMessageHandler::EncodeBuffer(...).
In this context, there is already a bt_dict_producer being used by DHTImmediateMessage's
bt_encode() method. This allows us to easily choose the override of bt_encode() that returns
nothing, but takes a bt_dict_producer as a reference.
*/
std::string
DHTImmediateMessage::bt_encode() const
{
oxenc::bt_dict_producer btdp;
try
{
btdp.append("a", "m");
{
auto subdict = btdp.append_dict("m");
for (auto& m : msgs)
m->bt_encode(subdict);
}
btdp.append("v", llarp::constants::proto_version);
}
catch (...)
{
log::critical(link_cat, "Error: DHTImmediateMessage failed to bt encode contents!");
}
return std::move(btdp).str();
}
bool
DHTImmediateMessage::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
{
if (key.startswith("m"))
return llarp::dht::DecodeMessageList(dht::Key_t(conn->remote_rc.pubkey), buf, msgs);
if (key.startswith("v"))
{
if (!bencode_read_integer(buf, &version))
return false;
return version == llarp::constants::proto_version;
}
// bad key
return false;
}
bool
DHTImmediateMessage::handle_message(Router* router) const
{
DHTImmediateMessage reply;
reply.conn = conn;
bool result = true;
auto dht = router->dht();
for (const auto& msg : msgs)
{
result &= dht->handle_message(*msg, reply.msgs);
}
if (reply.msgs.size())
{
if (result)
{
result = router->SendToOrQueue(conn->remote_rc.pubkey, reply);
}
}
return true;
}
} // namespace llarp

@ -1,35 +0,0 @@
#pragma once
#include <llarp/dht/message.hpp>
#include "link_message.hpp"
#include <vector>
namespace llarp
{
struct DHTImmediateMessage final : public AbstractLinkMessage
{
DHTImmediateMessage() = default;
~DHTImmediateMessage() override = default;
std::vector<std::unique_ptr<dht::AbstractDHTMessage>> msgs;
std::string
bt_encode() const override;
bool
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
bool
handle_message(Router* router) const override;
void
clear() override;
const char*
name() const override
{
return "DHTImmediate";
}
};
} // namespace llarp

@ -145,7 +145,8 @@ namespace llarp
buf.cur = buf.base;
// outer signature
if (!CryptoManager::instance()->verify(rc.pubkey, buf, sig))
if (!CryptoManager::instance()->verify(
rc.pubkey, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), sig))
{
log::error(link_cat, "Error: outer signature failed!");
return false;

@ -70,9 +70,9 @@ namespace llarp
bool
LR_CommitMessage::handle_message(Router* router) const
{
if (frames.size() != path::max_len)
if (frames.size() != path::MAX_LEN)
{
llarp::LogError("LRCM invalid number of records, ", frames.size(), "!=", path::max_len);
llarp::LogError("LRCM invalid number of records, ", frames.size(), "!=", path::MAX_LEN);
return false;
}
if (!router->path_context().AllowingTransit())
@ -93,7 +93,7 @@ namespace llarp
return false;
if (!BEncodeWriteDictEntry("i", nextHop, buf))
return false;
if (lifetime > 10s && lifetime < path::default_lifetime)
if (lifetime > 10s && lifetime < path::DEFAULT_LIFETIME)
{
if (!BEncodeWriteDictInt("i", lifetime.count(), buf))
return false;
@ -370,7 +370,7 @@ namespace llarp
static void
HandleDecrypted(llarp_buffer_t* buf, std::shared_ptr<LRCMFrameDecrypt> self)
{
auto now = self->context->router()->Now();
auto now = self->context->router()->now();
auto& info = self->hop->info;
if (!buf)
{
@ -423,7 +423,7 @@ namespace llarp
info);
self->hop->lifetime += self->record.work->extendedLifetime;
}
else if (self->record.lifetime < path::default_lifetime && self->record.lifetime > 10s)
else if (self->record.lifetime < path::DEFAULT_LIFETIME && self->record.lifetime > 10s)
{
self->hop->lifetime = self->record.lifetime;
llarp::LogDebug(

@ -129,9 +129,9 @@ namespace llarp
LR_StatusMessage::handle_message(Router* router) const
{
llarp::LogDebug("Received LR_Status message from (", conn->remote_rc.pubkey, ")");
if (frames.size() != path::max_len)
if (frames.size() != path::MAX_LEN)
{
llarp::LogError("LRSM invalid number of records, ", frames.size(), "!=", path::max_len);
llarp::LogError("LRSM invalid number of records, ", frames.size(), "!=", path::MAX_LEN);
return false;
}

@ -272,7 +272,7 @@ namespace llarp::path
if (failedAt)
edge = *failedAt;
r->loop()->call([r, self = shared_from_this(), edge]() {
self->EnterState(ePathFailed, r->Now());
self->EnterState(ePathFailed, r->now());
if (auto parent = self->m_PathSet.lock())
{
parent->HandlePathBuildFailedAt(self, edge);
@ -412,7 +412,7 @@ namespace llarp::path
bool
Path::SendLatencyMessage(Router* r)
{
const auto now = r->Now();
const auto now = r->now();
// send path latency test
routing::PathLatencyMessage latency{};
latency.sent_time = randint();
@ -445,7 +445,7 @@ namespace llarp::path
if (now >= buildStarted)
{
const auto dlt = now - buildStarted;
if (dlt >= path::build_timeout)
if (dlt >= path::BUILD_TIMEOUT)
{
LogWarn(Name(), " waited for ", ToString(dlt), " and no path was built");
r->router_profiling().MarkPathFail(this);
@ -458,7 +458,7 @@ namespace llarp::path
if (_status == ePathEstablished)
{
auto dlt = now - m_LastLatencyTestTime;
if (dlt > path::latency_interval && m_LastLatencyTestID == 0)
if (dlt > path::LATENCY_INTERVAL && m_LastLatencyTestID == 0)
{
SendLatencyMessage(r);
// latency test FEC
@ -469,14 +469,14 @@ namespace llarp::path
return;
}
dlt = now - m_LastRecvMessage;
if (dlt >= path::alive_timeout)
if (dlt >= path::ALIVE_TIMEOUT)
{
LogWarn(Name(), " waited for ", ToString(dlt), " and path looks dead");
r->router_profiling().MarkPathFail(this);
EnterState(ePathTimeout, now);
}
}
if (_status == ePathIgnore and now - m_LastRecvMessage >= path::alive_timeout)
if (_status == ePathIgnore and now - m_LastRecvMessage >= path::ALIVE_TIMEOUT)
{
// clean up this path as we dont use it anymore
EnterState(ePathExpired, now);
@ -507,15 +507,18 @@ namespace llarp::path
size_t idx = 0;
for (auto& ev : msgs)
{
const llarp_buffer_t buf(ev.first);
TunnelNonce n = ev.second;
uint8_t* buf = ev.first.data();
size_t sz = ev.first.size();
for (const auto& hop : hops)
{
CryptoManager::instance()->xchacha20(buf, hop.shared, n);
CryptoManager::instance()->xchacha20(buf, sz, hop.shared, n);
n ^= hop.nonceXOR;
}
auto& msg = sendmsgs[idx];
msg.enc = buf;
std::memcpy(msg.enc.data(), buf, sz);
msg.nonce = ev.second;
msg.pathid = TXID();
++idx;
@ -581,14 +584,18 @@ namespace llarp::path
size_t idx = 0;
for (auto& ev : msgs)
{
const llarp_buffer_t buf(ev.first);
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;
CryptoManager::instance()->xchacha20(buf, hop.shared, sendMsgs[idx].nonce);
CryptoManager::instance()->xchacha20(buf, sz, hop.shared, sendMsgs[idx].nonce);
}
sendMsgs[idx].enc = buf;
std::memcpy(sendMsgs[idx].enc.data(), buf, sz);
++idx;
}
r->loop()->call([self = shared_from_this(), msgs = std::move(sendMsgs), r]() mutable {
@ -606,7 +613,7 @@ namespace llarp::path
if (HandleRoutingMessage(buf, r))
{
r->TriggerPump();
m_LastRecvMessage = r->Now();
m_LastRecvMessage = r->now();
}
}
}
@ -672,11 +679,11 @@ namespace llarp::path
N.Randomize();
buf.sz = buf.cur - buf.base;
// pad smaller messages
if (buf.sz < pad_size)
if (buf.sz < PAD_SIZE)
{
// randomize padding
CryptoManager::instance()->randbytes(buf.cur, pad_size - buf.sz);
buf.sz = pad_size;
CryptoManager::instance()->randbytes(buf.cur, PAD_SIZE - buf.sz);
buf.sz = PAD_SIZE;
}
buf.cur = buf.base;
LogDebug(
@ -699,7 +706,7 @@ namespace llarp::path
bool
Path::HandleDataDiscardMessage(const routing::DataDiscardMessage& msg, Router* r)
{
MarkActive(r->Now());
MarkActive(r->now());
if (m_DropHandler)
return m_DropHandler(shared_from_this(), msg.path_id, msg.sequence_number);
return true;
@ -760,7 +767,7 @@ namespace llarp::path
bool
Path::HandlePathLatencyMessage(const routing::PathLatencyMessage&, Router* r)
{
const auto now = r->Now();
const auto now = r->now();
MarkActive(now);
if (m_LastLatencyTestID)
{
@ -784,7 +791,7 @@ namespace llarp::path
bool
Path::HandleDHTMessage(const dht::AbstractDHTMessage& msg, Router* r)
{
MarkActive(r->Now());
MarkActive(r->now());
routing::PathDHTMessage reply;
if (not r->dht()->handle_message(msg, reply.dht_msgs))
return false;
@ -859,7 +866,7 @@ namespace llarp::path
return false;
}
LogInfo(Name(), " ", Endpoint(), " Rejected exit");
MarkActive(r->Now());
MarkActive(r->now());
return InformExitResult(llarp_time_t(msg.backoff_time));
}
LogError(Name(), " got unwarranted RXM");
@ -879,7 +886,7 @@ namespace llarp::path
// we now can send exit traffic
_role |= ePathRoleExit;
LogInfo(Name(), " ", Endpoint(), " Granted exit");
MarkActive(r->Now());
MarkActive(r->now());
return InformExitResult(0s);
}
LogError(Name(), " got unwarranted GXM");
@ -916,8 +923,8 @@ namespace llarp::path
if (m_ExitTrafficHandler(
self, llarp_buffer_t(pkt.data() + 8, pkt.size() - 8), counter, msg.protocol))
{
MarkActive(r->Now());
EnterState(ePathEstablished, r->Now());
MarkActive(r->now());
EnterState(ePathEstablished, r->now());
}
}
return sent;

@ -55,7 +55,7 @@ namespace llarp
/// nonce for key exchange
TunnelNonce nonce;
// lifetime
llarp_time_t lifetime = default_lifetime;
llarp_time_t lifetime = DEFAULT_LIFETIME;
util::StatusObject
ExtractStatus() const;

@ -73,7 +73,7 @@ namespace llarp
record.nextRC = std::make_unique<RouterContact>(path->hops[idx].rc);
}
// build record
record.lifetime = path::default_lifetime;
record.lifetime = path::DEFAULT_LIFETIME;
record.version = llarp::constants::proto_version;
record.txid = hop.txID;
record.rxid = hop.rxID;
@ -125,7 +125,7 @@ namespace llarp
result = func;
work = worker;
for (size_t i = 0; i < path::max_len; ++i)
for (size_t i = 0; i < path::MAX_LEN; ++i)
{
LRCM.frames[i].Randomize();
}
@ -148,7 +148,7 @@ namespace llarp
auto sentHandler = [router = ctx->router, path = ctx->path](auto status) {
if (status != SendStatus::Success)
{
path->EnterState(path::ePathFailed, router->Now());
path->EnterState(path::ePathFailed, router->now());
}
};
if (ctx->router->SendToOrQueue(remote, ctx->LRCM, sentHandler))
@ -237,28 +237,23 @@ namespace llarp
Builder::SelectFirstHop(const std::set<RouterID>& exclude) const
{
std::optional<RouterContact> found = std::nullopt;
m_router->ForEachPeer(
[&](const AbstractLinkSession* s, bool isOutbound) {
if (s && s->IsEstablished() && isOutbound && not found.has_value())
{
const RouterContact rc = s->GetRemoteRC();
m_router->for_each_connection([&](link::Connection& conn) {
const auto& rc = conn.remote_rc;
#ifndef TESTNET
if (m_router->IsBootstrapNode(rc.pubkey))
return;
if (m_router->IsBootstrapNode(rc.pubkey))
return;
#endif
if (exclude.count(rc.pubkey))
return;
if (exclude.count(rc.pubkey))
return;
if (BuildCooldownHit(rc.pubkey))
return;
if (BuildCooldownHit(rc.pubkey))
return;
if (m_router->router_profiling().IsBadForPath(rc.pubkey))
return;
if (m_router->router_profiling().IsBadForPath(rc.pubkey))
return;
found = rc;
}
},
true);
found = rc;
});
return found;
}
@ -422,7 +417,7 @@ namespace llarp
llarp_time_t
Builder::Now() const
{
return m_router->Now();
return m_router->now();
}
void

@ -269,7 +269,7 @@ namespace llarp
std::function<bool(const service::Introduction&)> filter) const;
virtual bool
PublishIntroSet(const service::EncryptedIntroSet&, Router*)
PublishIntroSet(const service::EncryptedIntroSet&)
{
return false;
}

@ -28,8 +28,8 @@ namespace llarp::path
TransitHop::TransitHop()
: AbstractHopHandler{}
, m_UpstreamGather{transit_hop_queue_size}
, m_DownstreamGather{transit_hop_queue_size}
, m_UpstreamGather{TRANSIT_HOP_QUEUE_SIZE}
, m_DownstreamGather{TRANSIT_HOP_QUEUE_SIZE}
{
m_UpstreamGather.enable();
m_DownstreamGather.enable();
@ -100,10 +100,10 @@ namespace llarp::path
N.Randomize();
buf.sz = buf.cur - buf.base;
// pad to nearest MESSAGE_PAD_SIZE bytes
auto dlt = buf.sz % pad_size;
auto dlt = buf.sz % PAD_SIZE;
if (dlt)
{
dlt = pad_size - dlt;
dlt = PAD_SIZE - dlt;
// randomize padding
CryptoManager::instance()->randbytes(buf.cur, dlt);
buf.sz += dlt;
@ -126,11 +126,17 @@ namespace llarp::path
for (auto& ev : msgs)
{
RelayDownstreamMessage msg;
const llarp_buffer_t buf(ev.first);
// 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;
CryptoManager::instance()->xchacha20(buf, pathKey, ev.second);
msg.enc = buf;
CryptoManager::instance()->xchacha20(buf, sz, pathKey, ev.second);
std::memcpy(msg.enc.data(), buf, sz);
llarp::LogDebug(
"relay ",
msg.enc.size(),
@ -153,12 +159,17 @@ namespace llarp::path
{
for (auto& ev : msgs)
{
const llarp_buffer_t buf(ev.first);
RelayUpstreamMessage msg;
CryptoManager::instance()->xchacha20(buf, pathKey, ev.second);
uint8_t* buf = ev.first.data();
size_t sz = ev.first.size();
CryptoManager::instance()->xchacha20(buf, sz, pathKey, ev.second);
msg.pathid = info.txID;
msg.nonce = ev.second ^ nonceXOR;
msg.enc = buf;
std::memcpy(msg.enc.data(), buf, sz);
if (m_UpstreamGather.tryPushBack(msg) != thread::QueueReturn::Success)
break;
}
@ -186,7 +197,7 @@ namespace llarp::path
{
LogWarn("invalid upstream data on endpoint ", info);
}
m_LastActivity = r->Now();
m_LastActivity = r->now();
}
FlushDownstream(r);
for (const auto& other : m_FlushOthers)

@ -63,7 +63,7 @@ namespace llarp
ShortHash nonceXOR;
llarp_time_t started = 0s;
// 10 minutes default
llarp_time_t lifetime = default_lifetime;
llarp_time_t lifetime = DEFAULT_LIFETIME;
llarp_proto_version_t version;
llarp_time_t m_LastActivity = 0s;

@ -365,3 +365,16 @@ namespace llarp
#endif
}; // namespace llarp
/*
- How does a SN/quorum of SN determine if an operator is acting in good faith on the network
- w/o the other operator knowing
- Observing and gathering data in a meaningful way
- What type of stats could you even collect off lokinet?
- Collecting network-wide data can't be automatically actioned unless the SN is collecting
- Redundancy model
- Don't rely on an individual, but a collection of them
*/

@ -54,7 +54,7 @@ namespace llarp
, _exit_context{this}
, _dht{dht::make_handler()}
, _disk_thread{_lmq->add_tagged_thread("disk")}
, m_RPCServer{nullptr}
, _rpc_server{nullptr}
, _randomStartDelay{platform::is_simulation ? std::chrono::milliseconds{(llarp::randint() % 1250) + 2000} : 0s}
, _hidden_service_context{this}
{
@ -280,15 +280,15 @@ namespace llarp
}
bool
Router::send_data_message(const RouterID &remote, const AbstractDataMessage &msg)
Router::send_data_message(const RouterID& remote, const AbstractDataMessage& msg)
{
return _link_manager.send_data_message(remote, msg.bt_encode());
return _link_manager.send_or_queue_data(remote, msg.bt_encode());
}
bool
Router::send_control_message(const RouterID &remote, const AbstractLinkMessage &msg)
Router::send_control_message(const RouterID& remote, const AbstractLinkMessage& msg)
{
return _link_manager.send_or_queue_data(remote, msg.bt_encode());
}
void
@ -300,7 +300,7 @@ namespace llarp
LogError("failure to decode or verify of remote RC");
return;
}
if (remote.Verify(Now()))
if (remote.Verify(now()))
{
LogDebug("verified signature");
_link_manager.connect_to(remote);
@ -455,7 +455,7 @@ namespace llarp
Router::SaveRC()
{
LogDebug("verify RC signature");
if (!router_contact.Verify(Now()))
if (!router_contact.Verify(now()))
{
Dump<MAX_RC_SIZE>(rc());
LogError("RC is invalid, not saving");
@ -503,8 +503,10 @@ namespace llarp
bool
Router::ParseRoutingMessageBuffer(
const llarp_buffer_t& buf, routing::AbstractRoutingMessageHandler* h, const PathID_t& rxid)
{}
const llarp_buffer_t&, routing::AbstractRoutingMessageHandler*, const PathID_t&)
{
return false;
}
bool
Router::appears_decommed() const
@ -710,7 +712,7 @@ namespace llarp
{
if (it->IsObsoleteBootstrap())
log::warning(logcat, "ignoring obsolete boostrap RC: {}", RouterID{it->pubkey});
else if (not it->Verify(Now()))
else if (not it->Verify(now()))
log::warning(logcat, "ignoring invalid bootstrap RC: {}", RouterID{it->pubkey});
else
{
@ -830,7 +832,7 @@ namespace llarp
void
Router::report_stats()
{
const auto now = Now();
const auto now = now();
LogInfo(node_db()->NumLoaded(), " RCs loaded");
LogInfo(bootstrap_rc_list.size(), " bootstrap peers");
LogInfo(NumberOfConnectedRouters(), " router connections");
@ -903,7 +905,7 @@ namespace llarp
if (is_stopping)
return;
// LogDebug("tick router");
const auto now = Now();
const auto now = now();
if (const auto delta = now - _last_tick; _last_tick != 0s and delta > TimeskipDetectedDuration)
{
// we detected a time skip into the futre, thaw the network
@ -1183,7 +1185,7 @@ namespace llarp
Router::StartRpcServer()
{
if (_config->api.m_enableRPCServer)
m_RPCServer = std::make_unique<rpc::RPCServer>(_lmq, *this);
_rpc_server = std::make_unique<rpc::RPCServer>(_lmq, *this);
return true;
}
@ -1283,7 +1285,7 @@ namespace llarp
_loop->call_every(ROUTER_TICK_INTERVAL, weak_from_this(), [this] { Tick(); });
_route_poker->start();
is_running.store(true);
_started_at = Now();
_started_at = now();
if (follow_whitelist)
{
// do service node testing if we are in service node whitelist mode
@ -1375,7 +1377,7 @@ namespace llarp
llarp_time_t
Router::Uptime() const
{
const llarp_time_t _now = Now();
const llarp_time_t _now = now();
if (_started_at > 0s && _now > _started_at)
return _now - _started_at;
return 0s;
@ -1602,9 +1604,7 @@ namespace llarp
for (auto& bind_addr : addrs)
{
AddressInfo ai;
ai.fromSockAddr(bind_addr);
_linkManager.connect_to({ai.IPString(), ai.port}, false);
_link_manager.connect_to({bind_addr.ToString()}, false);
}
}

@ -9,7 +9,6 @@
#include <llarp/config/key_manager.hpp>
#include <llarp/constants/link_layer.hpp>
#include <llarp/crypto/types.hpp>
#include <llarp/dht/context.hpp>
#include <llarp/ev/ev.hpp>
#include <llarp/exit/context.hpp>
#include <llarp/handlers/tun.hpp>
@ -48,10 +47,22 @@
/*
TONUKE:
- hidden_service_context
TODO:
- router should hold DHT nodes container? in either a class or a map
-
*/
namespace llarp
{
/// number of routers to publish to
static constexpr size_t INTROSET_RELAY_REDUNDANCY = 2;
/// number of dht locations handled per relay
static constexpr size_t INTROSET_REQS_PER_RELAY = 2;
static constexpr size_t INTROSET_STORAGE_REDUNDANCY =
(INTROSET_RELAY_REDUNDANCY * INTROSET_REQS_PER_RELAY);
class RouteManager final /* : public Router */
{
@ -120,7 +131,7 @@ namespace llarp
std::shared_ptr<Config> _config;
uint32_t _path_build_count = 0;
std::unique_ptr<rpc::RPCServer> m_RPCServer;
std::unique_ptr<rpc::RPCServer> _rpc_server;
const llarp_time_t _randomStartDelay;
@ -393,8 +404,8 @@ namespace llarp
bool
LooksAlive() const
{
const llarp_time_t now = Now();
return now <= _last_tick || (now - _last_tick) <= llarp_time_t{30000};
const llarp_time_t current = now();
return current <= _last_tick || (current - _last_tick) <= llarp_time_t{30000};
}
const std::shared_ptr<RoutePoker>&
@ -535,7 +546,7 @@ namespace llarp
Tick();
llarp_time_t
Now() const
now() const
{
return llarp::time_now_ms();
}

@ -230,7 +230,6 @@ namespace llarp
void
RouterContact::Clear()
{
addrs.clear();
signature.Zero();
nickname.Zero();
enckey.Zero();
@ -248,7 +247,7 @@ namespace llarp
{"lastUpdated", last_updated.count()},
{"publicRouter", IsPublicRouter()},
{"identity", pubkey.ToString()},
{"addresses", addrs}};
{"address", addr.to_string()}};
if (HasNick())
{
@ -344,7 +343,7 @@ namespace llarp
RouterContact::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictList("a", addrs, read, key, buf))
if (!BEncodeMaybeReadDictList("a", addr, read, key, buf))
return false;
if (!BEncodeMaybeReadDictEntry("i", netID, read, key, buf))
@ -406,7 +405,7 @@ namespace llarp
{
if (not routerVersion)
return false;
return !addrs.empty();
return addr.is_addressable();
}
bool
@ -496,14 +495,13 @@ namespace llarp
// TODO: make net* overridable
const auto* net = net::Platform::Default_ptr();
for (const auto& a : addrs)
if (net->IsBogon(addr.in4()) && BlockBogons)
{
if (net->IsBogon(a.ip) && BlockBogons)
{
log::error(logcat, "invalid address info: {}", a);
return false;
}
log::error(logcat, "invalid address info: {}", addr);
return false;
}
if (!VerifySignature())
{
log::error(logcat, "invalid signature: {}", *this);
@ -524,17 +522,18 @@ namespace llarp
llarp_buffer_t buf(tmp);
auto bte = copy.bt_encode();
buf.write(bte.begin(), bte.end());
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
return CryptoManager::instance()->verify(pubkey, buf, signature);
return CryptoManager::instance()->verify(
pubkey, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), signature);
}
/* else */
if (version == 1)
{
llarp_buffer_t buf{signed_bt_dict};
return CryptoManager::instance()->verify(pubkey, buf, signature);
return CryptoManager::instance()->verify(
pubkey,
reinterpret_cast<uint8_t*>(const_cast<char*>(signed_bt_dict.data())),
signed_bt_dict.size(),
signature);
}
return false;
@ -603,7 +602,7 @@ namespace llarp
last_updated.count(),
netID,
version,
fmt::format("{}", fmt::join(addrs, ",")),
fmt::format("{}", addr),
enckey,
signature);
}

@ -352,10 +352,11 @@ namespace llarp::rpc
void
LokidRpcClient::LookupLNSNameHash(
dht::Key_t namehash, std::function<void(std::optional<service::EncryptedName>)> resultHandler)
std::string namehash,
std::function<void(std::optional<service::EncryptedName>)> resultHandler)
{
LogDebug("Looking Up LNS NameHash ", namehash);
const nlohmann::json req{{"type", 2}, {"name_hash", namehash.ToHex()}};
const nlohmann::json req{{"type", 2}, {"name_hash", oxenc::to_hex(namehash)}};
Request(
"rpc.lns_resolve",
[this, resultHandler](bool success, std::vector<std::string> data) {

@ -37,7 +37,7 @@ namespace llarp
void
LookupLNSNameHash(
dht::Key_t namehash,
std::string namehash,
std::function<void(std::optional<service::EncryptedName>)> resultHandler);
/// inform that if connected to a router successfully

@ -103,7 +103,7 @@ namespace llarp::rpc
: m_LMQ{std::move(lmq)}, m_Router(r), log_subs{*m_LMQ, llarp::logRingBuffer}
{
// copied logic loop as placeholder
for (const auto& addr : r.get_config()->api.m_rpcBindAddresses)
for (const auto& addr : r.config()->api.m_rpcBindAddresses)
{
m_LMQ->listen_plain(addr.zmq_address());
LogInfo("Bound RPC server to ", addr.full_address());
@ -392,7 +392,7 @@ namespace llarp::rpc
try
{
for (auto& ip : unmapexit.request.ip_range)
m_Router.hiddenServiceContext().GetDefault()->UnmapExitRange(ip);
m_Router.hidden_service_context().GetDefault()->UnmapExitRange(ip);
}
catch (std::exception& e)
{

@ -45,7 +45,7 @@ namespace llarp
throw std::runtime_error("invalid address");
}
explicit Address(const Data& buf) : AlignedBuffer<32>(buf)
explicit Address(const std::array<byte_t, SIZE>& buf) : AlignedBuffer<32>(buf)
{}
Address(const Address& other)

File diff suppressed because it is too large Load Diff

@ -17,7 +17,6 @@
#include <llarp/service/sendcontext.hpp>
#include <llarp/service/protocol_type.hpp>
#include <llarp/service/session.hpp>
#include <llarp/service/lookup.hpp>
#include <llarp/service/endpoint_types.hpp>
#include <llarp/endpoint_base.hpp>
#include <llarp/service/auth.hpp>
@ -51,7 +50,7 @@ namespace llarp
struct OutboundContext;
/// minimum interval for publishing introsets
inline constexpr auto IntrosetPublishInterval = path::intro_path_spread / 2;
inline constexpr auto IntrosetPublishInterval = path::INTRO_PATH_SPREAD / 2;
/// how agressively should we retry publishing introset on failure
inline constexpr auto IntrosetPublishRetryCooldown = 1s;
@ -62,10 +61,7 @@ namespace llarp
/// number of unique snodes we want to talk to do to ons lookups
inline constexpr size_t MIN_ENDPOINTS_FOR_LNS_LOOKUP = 2;
struct Endpoint : public path::Builder,
public ILookupHolder,
public IDataHandler,
public EndpointBase
struct Endpoint : public path::Builder, public IDataHandler, public EndpointBase
{
Endpoint(Router* r, Context* parent);
~Endpoint() override;
@ -189,7 +185,7 @@ namespace llarp
}
bool
PublishIntroSet(const EncryptedIntroSet& i, Router* r) override;
PublishIntroSet(const EncryptedIntroSet& i) override;
bool
PublishIntroSetVia(
@ -275,7 +271,7 @@ namespace llarp
const Identity&
GetIdentity() const
{
return m_Identity;
return _identity;
}
void
@ -295,7 +291,7 @@ namespace llarp
std::function<void(bool, std::string)> result);
void
PutLookup(IServiceLookup* lookup, uint64_t txid) override;
PutLookup(IServiceLookup* lookup, uint64_t txid);
void
HandlePathBuilt(path::Path_ptr path) override;
@ -442,7 +438,7 @@ namespace llarp
std::optional<std::vector<RouterContact>>
GetHopsForBuildWithEndpoint(RouterID endpoint);
virtual void
void
PathBuildStarted(path::Path_ptr path) override;
virtual void
@ -502,7 +498,7 @@ namespace llarp
SupportsV6() const = 0;
void
RegenAndPublishIntroSet();
regen_and_publish_introset();
IServiceLookup*
GenerateLookupByTag(const Tag& tag);
@ -549,26 +545,26 @@ namespace llarp
path::Path::UniqueEndpointSet_t
GetUniqueEndpointsForLookup() const;
IDataHandler* m_DataHandler = nullptr;
Identity m_Identity;
net::IPRangeMap<service::Address> m_ExitMap;
bool m_PublishIntroSet = true;
std::unique_ptr<EndpointState> m_state;
std::shared_ptr<IAuthPolicy> m_AuthPolicy;
std::unordered_map<Address, AuthInfo> m_RemoteAuthInfos;
std::unique_ptr<quic::TunnelManager> m_quic;
IDataHandler* _data_handler = nullptr;
Identity _identity;
net::IPRangeMap<service::Address> _exit_map;
bool _publish_introset = true;
std::unique_ptr<EndpointState> _state;
std::shared_ptr<IAuthPolicy> _auth_policy;
std::unordered_map<Address, AuthInfo> _remote_auth_infos;
std::unique_ptr<quic::TunnelManager> _tunnel_manager;
/// (lns name, optional exit range, optional auth info) for looking up on startup
/// (ons name, optional exit range, optional auth info) for looking up on startup
std::unordered_map<std::string, std::pair<std::optional<IPRange>, std::optional<AuthInfo>>>
m_StartupLNSMappings;
_startup_ons_mappings;
RecvPacketQueue_t m_InboundTrafficQueue;
RecvPacketQueue_t _inbound_queue;
public:
SendMessageQueue_t m_SendQueue;
SendMessageQueue_t _send_queue;
private:
llarp_time_t m_LastIntrosetRegenAttempt = 0s;
llarp_time_t _last_introset_regen_attempt = 0s;
protected:
void
@ -577,17 +573,17 @@ namespace llarp
friend struct EndpointUtil;
// clang-format off
const IntroSet& introSet() const;
IntroSet& introSet();
const IntroSet& intro_set() const;
IntroSet& intro_set();
using ConvoMap = std::unordered_map<ConvoTag, Session>;
const ConvoMap& Sessions() const;
ConvoMap& Sessions();
// clang-format on
thread::Queue<RecvDataEvent> m_RecvQueue;
thread::Queue<RecvDataEvent> _recv_event_queue;
/// for rate limiting introset lookups
util::DecayingHashSet<Address> m_IntrosetLookupFilter;
util::DecayingHashSet<Address> _introset_lookup_filter;
};
using Endpoint_ptr = std::shared_ptr<Endpoint>;

@ -66,9 +66,9 @@ namespace llarp::service
}
bool
Identity::Sign(Signature& sig, const llarp_buffer_t& buf) const
Identity::Sign(Signature& sig, uint8_t* buf, size_t size) const
{
return CryptoManager::instance()->sign(sig, signkey, buf);
return CryptoManager::instance()->sign(sig, signkey, buf, size);
}
void
@ -151,12 +151,13 @@ namespace llarp::service
}
std::optional<EncryptedIntroSet>
Identity::EncryptAndSignIntroSet(const IntroSet& other_i, llarp_time_t now) const
Identity::encrypt_and_sign_introset(const IntroSet& other_i, llarp_time_t now) const
{
EncryptedIntroSet encrypted;
if (other_i.intros.empty())
return std::nullopt;
IntroSet i{other_i};
encrypted.nounce.Randomize();
// set timestamp
@ -164,21 +165,17 @@ namespace llarp::service
i.time_signed = now;
encrypted.signedAt = now;
// set service info
i.addressKeys = pub;
i.address_keys = pub;
// set public encryption key
i.sntru_pubkey = pq_keypair_to_public(pq);
std::array<byte_t, MAX_INTROSET_SIZE> tmp;
llarp_buffer_t buf{tmp};
auto bte = i.bt_encode();
buf.write(bte.begin(), bte.end());
// rewind and resize buffer
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
const SharedSecret k{i.addressKeys.Addr()};
CryptoManager::instance()->xchacha20(buf, k, encrypted.nounce);
encrypted.introsetPayload = buf.copy();
const SharedSecret k{i.address_keys.Addr()};
CryptoManager::instance()->xchacha20(
reinterpret_cast<uint8_t*>(bte.data()), bte.size(), k, encrypted.nounce);
std::memcpy(encrypted.introsetPayload.data(), bte.data(), bte.size());
if (not encrypted.Sign(derivedSignKey))
return std::nullopt;

@ -48,10 +48,10 @@ namespace llarp::service
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf);
std::optional<EncryptedIntroSet>
EncryptAndSignIntroSet(const IntroSet& i, llarp_time_t now) const;
encrypt_and_sign_introset(const IntroSet& i, llarp_time_t now) const;
bool
Sign(Signature& sig, const llarp_buffer_t& buf) const;
Sign(Signature& sig, uint8_t* buf, size_t size) const;
/// zero out all secret key members
void

@ -12,9 +12,9 @@
namespace llarp::service
{
bool
ServiceInfo::Verify(const llarp_buffer_t& payload, const Signature& sig) const
ServiceInfo::verify(uint8_t* buf, size_t size, const Signature& sig) const
{
return CryptoManager::instance()->verify(signkey, payload, sig);
return CryptoManager::instance()->verify(signkey, buf, size, sig);
}
bool

@ -34,7 +34,7 @@ namespace llarp::service
}
bool
Verify(const llarp_buffer_t& payload, const Signature& sig) const;
verify(uint8_t* buf, size_t size, const Signature& sig) const;
const PubKey&
EncryptionPublicKey() const

@ -14,24 +14,25 @@ namespace llarp::service
{"location", derivedSigningKey.ToString()}, {"signedAt", to_json(signedAt)}, {"size", sz}};
}
bool
EncryptedIntroSet::BEncode(llarp_buffer_t* buf) const
std::string
EncryptedIntroSet::bt_encode() const
{
if (not bencode_start_dict(buf))
return false;
if (not BEncodeWriteDictEntry("d", derivedSigningKey, buf))
return false;
if (not BEncodeWriteDictEntry("n", nounce, buf))
return false;
if (not BEncodeWriteDictInt("s", signedAt.count(), buf))
return false;
if (not bencode_write_bytestring(buf, "x", 1))
return false;
if (not bencode_write_bytestring(buf, introsetPayload.data(), introsetPayload.size()))
return false;
if (not BEncodeWriteDictEntry("z", sig, buf))
return false;
return bencode_end(buf);
oxenc::bt_dict_producer btdp;
try
{
btdp.append("d", derivedSigningKey.ToView());
btdp.append("n", nounce.ToView());
btdp.append("s", signedAt.count());
btdp.append("x", oxenc::bt_serialize(introsetPayload));
btdp.append("z", sig.ToView());
}
catch (...)
{
log::critical(net_cat, "Error: EncryptedIntroSet failed to bt encode contents!");
}
return std::move(btdp).str();
}
bool
@ -88,7 +89,8 @@ namespace llarp::service
IntroSet i;
std::vector<byte_t> payload = introsetPayload;
llarp_buffer_t buf(payload);
CryptoManager::instance()->xchacha20(buf, k, nounce);
CryptoManager::instance()->xchacha20(payload.data(), payload.size(), k, nounce);
if (not i.BDecode(&buf))
return {};
return i;
@ -97,7 +99,7 @@ namespace llarp::service
bool
EncryptedIntroSet::IsExpired(llarp_time_t now) const
{
return now >= signedAt + path::default_lifetime;
return now >= signedAt + path::DEFAULT_LIFETIME;
}
bool
@ -107,33 +109,37 @@ namespace llarp::service
if (not k.toPublic(derivedSigningKey))
return false;
sig.Zero();
std::array<byte_t, MAX_INTROSET_SIZE + 128> tmp;
llarp_buffer_t buf(tmp);
if (not BEncode(&buf))
return false;
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
if (not CryptoManager::instance()->sign(sig, k, buf))
auto bte = bt_encode();
if (not CryptoManager::instance()->sign(
sig, k, reinterpret_cast<uint8_t*>(bte.data()), bte.size()))
return false;
LogDebug("signed encrypted introset: ", *this);
return true;
}
bool
EncryptedIntroSet::Verify(llarp_time_t now) const
EncryptedIntroSet::verify(llarp_time_t now) const
{
if (IsExpired(now))
return false;
std::array<byte_t, MAX_INTROSET_SIZE + 128> tmp;
llarp_buffer_t buf(tmp);
EncryptedIntroSet copy(*this);
copy.sig.Zero();
if (not copy.BEncode(&buf))
return false;
LogDebug("verify encrypted introset: ", copy, " sig = ", sig);
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
return CryptoManager::instance()->verify(derivedSigningKey, buf, sig);
auto bte = copy.bt_encode();
return CryptoManager::instance()->verify(
derivedSigningKey, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), sig);
}
bool
EncryptedIntroSet::verify(std::string introset, std::string key, std::string sig)
{
return CryptoManager::instance()->verify(
reinterpret_cast<uint8_t*>(key.data()),
reinterpret_cast<uint8_t*>(introset.data()),
introset.size(),
reinterpret_cast<uint8_t*>(sig.data()));
}
util::StatusObject
@ -175,7 +181,7 @@ namespace llarp::service
IntroSet::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictEntry("a", addressKeys, read, key, buf))
if (!BEncodeMaybeReadDictEntry("a", address_keys, read, key, buf))
return false;
if (key.startswith("e"))
@ -262,7 +268,7 @@ namespace llarp::service
{
{
auto subdict = btdp.append_dict("a");
addressKeys.bt_encode(subdict);
address_keys.bt_encode(subdict);
}
if (exit_policy)
@ -350,21 +356,15 @@ namespace llarp::service
}
bool
IntroSet::Verify(llarp_time_t now) const
IntroSet::verify(llarp_time_t now) const
{
std::array<byte_t, MAX_INTROSET_SIZE> tmp;
llarp_buffer_t buf{tmp};
IntroSet copy;
copy = *this;
copy.signature.Zero();
auto bte = copy.bt_encode();
buf.write(bte.begin(), bte.end());
// rewind and resize buffer
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
if (!addressKeys.Verify(buf, signature))
if (!address_keys.verify(reinterpret_cast<uint8_t*>(bte.data()), bte.size(), signature))
{
return false;
}
@ -373,7 +373,7 @@ namespace llarp::service
now += MAX_INTROSET_TIME_DELTA;
for (const auto& intro : intros)
{
if (intro.expiry > now && intro.expiry - now > path::default_lifetime)
if (intro.expiry > now && intro.expiry - now > path::DEFAULT_LIFETIME)
{
return false;
}
@ -395,7 +395,7 @@ namespace llarp::service
{
return fmt::format(
"[IntroSet addressKeys={} intros={{{}}} sntrupKey={} topic={} signedAt={} v={} sig={}]",
addressKeys,
address_keys,
fmt::format("{}", fmt::join(intros, ",")),
sntru_pubkey,
topic,

@ -28,7 +28,7 @@ namespace llarp::service
struct IntroSet
{
ServiceInfo addressKeys;
ServiceInfo address_keys;
std::vector<Introduction> intros;
PQPubKey sntru_pubkey;
Tag topic;
@ -90,7 +90,7 @@ namespace llarp::service
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf);
bool
Verify(llarp_time_t now) const;
verify(llarp_time_t now) const;
util::StatusObject
ExtractStatus() const;
@ -99,14 +99,14 @@ namespace llarp::service
inline bool
operator<(const IntroSet& lhs, const IntroSet& rhs)
{
return lhs.addressKeys < rhs.addressKeys;
return lhs.address_keys < rhs.address_keys;
}
inline bool
operator==(const IntroSet& lhs, const IntroSet& rhs)
{
return std::tie(
lhs.addressKeys,
lhs.address_keys,
lhs.intros,
lhs.sntru_pubkey,
lhs.time_signed,
@ -114,7 +114,7 @@ namespace llarp::service
lhs.topic,
lhs.signature)
== std::tie(
rhs.addressKeys,
rhs.address_keys,
rhs.intros,
rhs.sntru_pubkey,
rhs.time_signed,
@ -132,11 +132,9 @@ namespace llarp::service
/// public version of the introset that is encrypted
struct EncryptedIntroSet
{
using Payload_t = std::vector<byte_t>;
PubKey derivedSigningKey;
llarp_time_t signedAt = 0s;
Payload_t introsetPayload;
std::vector<byte_t> introsetPayload;
TunnelNonce nounce;
std::optional<Tag> topic;
Signature sig;
@ -147,8 +145,8 @@ namespace llarp::service
bool
IsExpired(llarp_time_t now) const;
bool
BEncode(llarp_buffer_t* buf) const;
std::string
bt_encode() const;
bool
BDecode(llarp_buffer_t* buf)
@ -164,7 +162,10 @@ namespace llarp::service
/// verify signature and timestamp
bool
Verify(llarp_time_t now) const;
verify(llarp_time_t now) const;
static bool
verify(std::string introset, std::string key, std::string sig);
std::string
ToString() const;

@ -1,32 +0,0 @@
#include "lookup.hpp"
#include <llarp/path/path.hpp>
#include <llarp/util/time.hpp>
#include <llarp/router/router.hpp>
#include <utility>
namespace llarp
{
struct Router;
namespace service
{
IServiceLookup::IServiceLookup(
ILookupHolder* p, uint64_t tx, std::string n, llarp_time_t timeout)
: m_parent(p), txid(tx), name(std::move(n)), m_created{time_now_ms()}, m_timeout{timeout}
{
p->PutLookup(this, tx);
}
bool
IServiceLookup::SendRequestViaPath(path::Path_ptr path, Router* r)
{
auto msg = BuildRequestMessage();
if (!msg)
return false;
r->loop()->call(
[path = std::move(path), msg = std::move(msg), r] { path->SendRoutingMessage(*msg, r); });
return true;
}
} // namespace service
} // namespace llarp

@ -1,113 +0,0 @@
#pragma once
#include <llarp/routing/message.hpp>
#include "intro_set.hpp"
#include <llarp/path/pathset.hpp>
#include <llarp/endpoint_base.hpp>
#include <set>
namespace llarp
{
// forward declare
namespace path
{
struct Path;
}
namespace service
{
struct ILookupHolder;
constexpr size_t MaxConcurrentLookups = size_t(16);
struct IServiceLookup
{
IServiceLookup() = delete;
virtual ~IServiceLookup() = default;
/// handle lookup result for introsets
virtual bool
HandleIntrosetResponse(const std::set<EncryptedIntroSet>&)
{
return false;
}
/// handle lookup result for introsets
virtual bool
HandleNameResponse(std::optional<Address>)
{
return false;
}
virtual void
HandleTimeout()
{
HandleIntrosetResponse({});
}
/// determine if this request has timed out
bool
IsTimedOut(llarp_time_t now) const
{
return TimeLeft(now) == 0ms;
}
/// return how long this request has left to be fufilled
llarp_time_t
TimeLeft(llarp_time_t now) const
{
if (now > (m_created + m_timeout))
return 0s;
return now - (m_created + m_timeout);
}
/// build request message for service lookup
virtual std::shared_ptr<routing::AbstractRoutingMessage>
BuildRequestMessage() = 0;
/// build a new request message and send it via a path
virtual bool
SendRequestViaPath(path::Path_ptr p, Router* r);
ILookupHolder* m_parent;
uint64_t txid;
const std::string name;
RouterID endpoint;
/// return true if this lookup is for a remote address
virtual bool
IsFor(EndpointBase::AddressVariant_t) const
{
return false;
}
util::StatusObject
ExtractStatus() const
{
auto now = time_now_ms();
util::StatusObject obj{
{"txid", txid},
{"endpoint", endpoint.ToHex()},
{"name", name},
{"timedOut", IsTimedOut(now)},
{"createdAt", m_created.count()}};
return obj;
}
protected:
IServiceLookup(
ILookupHolder* parent, uint64_t tx, std::string name, llarp_time_t timeout = 10s);
const llarp_time_t m_created, m_timeout;
};
struct ILookupHolder
{
virtual void
PutLookup(IServiceLookup* l, uint64_t txid) = 0;
};
} // namespace service
} // namespace llarp

@ -54,9 +54,9 @@ namespace llarp::service
OutboundContext::OutboundContext(const IntroSet& introset, Endpoint* parent)
: path::Builder{parent->router(), OutboundContextNumPaths, parent->numHops}
, SendContext{introset.addressKeys, {}, this, parent}
, location{introset.addressKeys.Addr().ToKey()}
, addr{introset.addressKeys.Addr()}
, SendContext{introset.address_keys, {}, this, parent}
, location{introset.address_keys.Addr().ToKey()}
, addr{introset.address_keys.Addr()}
, currentIntroSet{introset}
{
@ -88,7 +88,7 @@ namespace llarp::service
if (remoteIntro != m_NextIntro)
{
remoteIntro = m_NextIntro;
m_DataHandler->PutSenderFor(currentConvoTag, currentIntroSet.addressKeys, false);
m_DataHandler->PutSenderFor(currentConvoTag, currentIntroSet.address_keys, false);
m_DataHandler->PutIntroFor(currentConvoTag, remoteIntro);
ShiftIntroRouter(m_NextIntro.router);
// if we have not made a handshake to the remote endpoint do so
@ -264,7 +264,7 @@ namespace llarp::service
frame->flag = 0;
generatedIntro = true;
// ensure we have a sender put for this convo tag
m_DataHandler->PutSenderFor(currentConvoTag, currentIntroSet.addressKeys, false);
m_DataHandler->PutSenderFor(currentConvoTag, currentIntroSet.address_keys, false);
// encrypt frame async
m_Endpoint->router()->queue_work([ex, frame] { return AsyncKeyExchange::Encrypt(ex, frame); });
@ -274,7 +274,7 @@ namespace llarp::service
std::string
OutboundContext::Name() const
{
return "OBContext:" + currentIntroSet.addressKeys.Addr().ToString();
return "OBContext:" + currentIntroSet.address_keys.Addr().ToString();
}
void
@ -366,8 +366,8 @@ namespace llarp::service
}
// check for stale intros
// update the introset if we think we need to
if (currentIntroSet.HasStaleIntros(now, path::intro_path_spread)
or remoteIntro.ExpiresSoon(now, path::intro_path_spread))
if (currentIntroSet.HasStaleIntros(now, path::INTRO_PATH_SPREAD)
or remoteIntro.ExpiresSoon(now, path::INTRO_PATH_SPREAD))
{
UpdateIntroSet();
ShiftIntroduction(false);
@ -381,7 +381,7 @@ namespace llarp::service
std::vector<Introduction> otherIntros;
ForEachPath([now, router = remoteIntro.router, &otherIntros](auto path) {
if (path and path->IsReady() and path->Endpoint() != router
and not path->ExpiresSoon(now, path::intro_path_spread))
and not path->ExpiresSoon(now, path::INTRO_PATH_SPREAD))
{
otherIntros.emplace_back(path->intro);
}
@ -485,7 +485,7 @@ namespace llarp::service
ForEachPath([now, this, &havePathToNextIntro, &numValidPaths](path::Path_ptr path) {
if (not path->IsReady())
return;
if (not path->intro.ExpiresSoon(now, path::default_lifetime - path::intro_path_spread))
if (not path->intro.ExpiresSoon(now, path::DEFAULT_LIFETIME - path::INTRO_PATH_SPREAD))
{
numValidPaths++;
if (path->intro.router == m_NextIntro.router)
@ -699,7 +699,7 @@ namespace llarp::service
{
LogWarn("invalidating convotag T=", frame.convo_tag);
m_Endpoint->RemoveConvoTag(frame.convo_tag);
m_Endpoint->m_SendQueue.tryPushBack(
m_Endpoint->_send_queue.tryPushBack(
SendEvent_t{std::make_shared<routing::PathTransferMessage>(f, frame.path_id), p});
}
}

@ -193,10 +193,10 @@ namespace llarp::service
ProtocolFrameMessage::DecryptPayloadInto(
const SharedSecret& sharedkey, ProtocolMessage& msg) const
{
Encrypted_t tmp = enc;
auto buf = tmp.Buffer();
CryptoManager::instance()->xchacha20(*buf, sharedkey, nonce);
return bencode_decode_dict(msg, buf);
Encrypted<2048> tmp = enc;
CryptoManager::instance()->xchacha20(tmp.data(), tmp.size(), sharedkey, nonce);
return bencode_decode_dict(msg, tmp.Buffer());
}
bool
@ -213,7 +213,7 @@ namespace llarp::service
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
// sign
return localIdent.Sign(sig, buf);
return localIdent.Sign(sig, reinterpret_cast<uint8_t*>(bte.data()), bte.size());
}
bool
@ -224,25 +224,18 @@ namespace llarp::service
llarp_buffer_t buf1(tmp);
// encode message
auto bte1 = msg.bt_encode();
buf1.write(bte1.begin(), bte1.end());
// rewind
buf1.sz = buf1.cur - buf1.base;
buf1.cur = buf1.base;
// encrypt
CryptoManager::instance()->xchacha20(buf1, sessionKey, nonce);
CryptoManager::instance()->xchacha20(
reinterpret_cast<uint8_t*>(bte1.data()), bte1.size(), sessionKey, nonce);
// put encrypted buffer
std::memcpy(enc.data(), bte1.data(), bte1.size());
enc = buf1;
// zero out signature
sig.Zero();
llarp_buffer_t buf2(tmp);
auto bte2 = bt_encode();
buf2.write(bte2.begin(), bte2.end());
// rewind
buf2.sz = buf2.cur - buf2.base;
buf2.cur = buf2.base;
// sign
if (!localIdent.Sign(sig, buf2))
if (!localIdent.Sign(sig, reinterpret_cast<uint8_t*>(bte2.data()), bte2.size()))
{
LogError("failed to sign? wtf?!");
return false;
@ -280,7 +273,7 @@ namespace llarp::service
{
auto crypto = CryptoManager::instance();
SharedSecret K;
SharedSecret sharedKey;
SharedSecret shared_key;
// copy
ProtocolFrameMessage frame(self->frame);
if (!crypto->pqe_decrypt(
@ -291,15 +284,21 @@ namespace llarp::service
return;
}
// decrypt
auto buf = frame.enc.Buffer();
crypto->xchacha20(*buf, K, self->frame.nonce);
if (!bencode_decode_dict(*self->msg, buf))
// auto buf = frame.enc.Buffer();
uint8_t* buf = frame.enc.data();
size_t sz = frame.enc.size();
crypto->xchacha20(buf, sz, K, self->frame.nonce);
auto bte = self->msg->bt_encode();
if (bte.empty())
{
LogError("failed to decode inner protocol message");
log::error(logcat, "Failed to decode inner protocol message");
DumpBuffer(*buf);
self->msg.reset();
return;
}
// verify signature of outer message after we parsed the inner message
if (!self->frame.Verify(self->msg->sender))
{
@ -323,23 +322,24 @@ namespace llarp::service
}
// PKE (A, B, N)
SharedSecret sharedSecret;
SharedSecret shared_secret;
path_dh_func dh_server = util::memFn(&Crypto::dh_server, CryptoManager::instance());
if (!self->m_LocalIdentity.KeyExchange(
dh_server, sharedSecret, self->msg->sender, self->frame.nonce))
dh_server, shared_secret, self->msg->sender, self->frame.nonce))
{
LogError("x25519 key exchange failed");
Dump<MAX_PROTOCOL_MESSAGE_SIZE>(self->frame);
self->msg.reset();
return;
}
std::array<byte_t, 64> tmp;
std::array<uint8_t, 64> tmp;
// K
std::copy(K.begin(), K.end(), tmp.begin());
std::memcpy(tmp.begin(), K.begin(), K.size());
// S = HS( K + PKE( A, B, N))
std::copy(sharedSecret.begin(), sharedSecret.end(), tmp.begin() + 32);
crypto->shorthash(sharedKey, llarp_buffer_t(tmp));
std::memcpy(tmp.begin() + 32, shared_secret.begin(), shared_secret.size());
crypto->shorthash(shared_key, tmp.data(), tmp.size());
std::shared_ptr<ProtocolMessage> msg = std::move(self->msg);
path::Path_ptr path = std::move(self->path);
@ -347,7 +347,7 @@ namespace llarp::service
msg->handler = self->handler;
self->handler->AsyncProcessAuthMessage(
msg,
[path, msg, from, handler = self->handler, fromIntro = self->fromIntro, sharedKey](
[path, msg, from, handler = self->handler, fromIntro = self->fromIntro, shared_key](
AuthResult result) {
if (result.code == AuthResultCode::eAuthAccepted)
{
@ -360,7 +360,7 @@ namespace llarp::service
handler->PutSenderFor(msg->tag, msg->sender, true);
}
handler->PutReplyIntroFor(msg->tag, msg->introReply);
handler->PutCachedSessionKeyFor(msg->tag, sharedKey);
handler->PutCachedSessionKeyFor(msg->tag, shared_key);
handler->SendAuthResult(path, from, msg->tag, result);
LogInfo("auth okay for T=", msg->tag, " from ", msg->sender.Addr());
ProtocolMessage::ProcessAsync(path, from, msg);
@ -489,21 +489,10 @@ namespace llarp::service
ProtocolFrameMessage::Verify(const ServiceInfo& svc) const
{
ProtocolFrameMessage copy(*this);
// save signature
// zero out signature for verify
copy.sig.Zero();
// serialize
std::array<byte_t, MAX_PROTOCOL_MESSAGE_SIZE> tmp;
llarp_buffer_t buf(tmp);
auto bte = copy.bt_encode();
buf.write(bte.begin(), bte.end());
// rewind buffer
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
// verify
return svc.Verify(buf, sig);
return svc.verify(reinterpret_cast<uint8_t*>(bte.data()), bte.size(), sig);
}
bool

@ -44,7 +44,7 @@ namespace llarp
~ProtocolMessage();
ProtocolType proto = ProtocolType::TrafficV4;
llarp_time_t queued = 0s;
std::vector<byte_t> payload; // encrypted AbstractLinkMessage
std::vector<byte_t> payload; // encrypted AbstractLinkMessage
Introduction introReply;
ServiceInfo sender;
Endpoint* handler = nullptr;
@ -78,9 +78,8 @@ namespace llarp
/// outer message
struct ProtocolFrameMessage final : public routing::AbstractRoutingMessage
{
using Encrypted_t = Encrypted<2048>;
PQCipherBlock cipher;
Encrypted_t enc;
Encrypted<2048> enc;
uint64_t flag; // set to indicate in plaintext a nack, aka "dont try again"
KeyExchangeNonce nonce;
Signature sig;

@ -48,7 +48,7 @@ namespace llarp::service
msg->sequence_number = path->NextSeqNo();
if (path->SendRoutingMessage(*msg, r))
{
lastGoodSend = r->Now();
lastGoodSend = r->now();
flushpaths.emplace(path);
m_Endpoint->ConvoTagTX(msg->protocol_frame_msg.convo_tag);
const auto rtt = (path->intro.latency + remoteIntro.latency) * 2;

@ -44,9 +44,9 @@ namespace llarp
uint64_t sequenceNo = 0;
llarp_time_t lastGoodSend = 0s;
const llarp_time_t createdAt;
llarp_time_t sendTimeout = path::build_timeout;
llarp_time_t connectTimeout = path::build_timeout * 2;
llarp_time_t shiftTimeout = (path::build_timeout * 5) / 2;
llarp_time_t sendTimeout = path::BUILD_TIMEOUT;
llarp_time_t connectTimeout = path::BUILD_TIMEOUT * 2;
llarp_time_t shiftTimeout = (path::BUILD_TIMEOUT * 5) / 2;
llarp_time_t estimatedRTT = 0s;
bool markedBad = false;
using Msg_ptr = std::shared_ptr<routing::PathTransferMessage>;

@ -11,7 +11,7 @@ namespace llarp
{
namespace service
{
static constexpr auto SessionLifetime = path::default_lifetime * 2;
static constexpr auto SessionLifetime = path::DEFAULT_LIFETIME * 2;
struct Session
{

@ -42,8 +42,6 @@ namespace llarp
static constexpr size_t SIZE = sz;
using Data = std::array<byte_t, SIZE>;
virtual ~AlignedBuffer() = default;
AlignedBuffer()
@ -56,7 +54,7 @@ namespace llarp
*this = data;
}
explicit AlignedBuffer(const Data& buf)
explicit AlignedBuffer(const std::array<byte_t, SIZE>& buf)
{
m_data = buf;
}
@ -159,13 +157,13 @@ namespace llarp
m_data.fill(f);
}
Data&
std::array<byte_t, SIZE>&
as_array()
{
return m_data;
}
const Data&
const std::array<byte_t, SIZE>&
as_array() const
{
return m_data;
@ -207,25 +205,25 @@ namespace llarp
randombytes(data(), SIZE);
}
typename Data::iterator
typename std::array<byte_t, SIZE>::iterator
begin()
{
return m_data.begin();
}
typename Data::iterator
typename std::array<byte_t, SIZE>::iterator
end()
{
return m_data.end();
}
typename Data::const_iterator
typename std::array<byte_t, SIZE>::const_iterator
begin() const
{
return m_data.cbegin();
}
typename Data::const_iterator
typename std::array<byte_t, SIZE>::const_iterator
end() const
{
return m_data.cend();
@ -294,7 +292,7 @@ namespace llarp
}
private:
Data m_data;
std::array<byte_t, SIZE> m_data;
};
namespace detail

@ -20,6 +20,8 @@
namespace llarp
{
using byte_view_t = std::basic_string_view<byte_t>;
using ustring = std::basic_string<uint8_t>;
using ustring_view = std::basic_string_view<uint8_t>;
using bstring = std::basic_string<std::byte>;
using bstring_view = std::basic_string_view<std::byte>;

Loading…
Cancel
Save