RC refactor layout

- Local and Remote RC's now implemented with discrete functionalities and uses
pull/2218/head
dr7ana 7 months ago
parent f7c18de0d4
commit 07271f9ae7

@ -41,7 +41,7 @@ if(APPLE)
set(LOKINET_APPLE_BUILD 5) set(LOKINET_APPLE_BUILD 5)
endif() endif()
set(RELEASE_MOTTO "Our Lord And Savior" CACHE STRING "Release motto") set(LOKINET_RELEASE_MOTTO "Anonymous, decentralized, IP-based overlay network" CACHE STRING "Release motto")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")

@ -86,7 +86,7 @@ main(int argc, char* argv[])
#else #else
cpr::Get( cpr::Get(
cpr::Url{bootstrap_url}, cpr::Url{bootstrap_url},
cpr::Header{{"User-Agent", std::string{llarp::VERSION_FULL}}}, cpr::Header{{"User-Agent", std::string{llarp::LOKINET_VERSION_FULL}}},
cpr::Ssl(cpr::ssl::CaPath{X509_get_default_cert_dir()})); cpr::Ssl(cpr::ssl::CaPath{X509_get_default_cert_dir()}));
#endif #endif
if (resp.status_code != 200) if (resp.status_code != 200)

@ -403,7 +403,7 @@ namespace
{ {
if (options.version) if (options.version)
{ {
std::cout << llarp::VERSION_FULL << std::endl; std::cout << llarp::LOKINET_VERSION_FULL << std::endl;
return 0; return 0;
} }
@ -548,7 +548,8 @@ namespace
static void static void
run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions opts) run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions opts)
{ {
llarp::LogInfo(fmt::format("starting up {} {}", llarp::VERSION_FULL, llarp::RELEASE_MOTTO)); llarp::LogInfo(fmt::format(
"starting up {} {}", llarp::LOKINET_VERSION_FULL, llarp::LOKINET_RELEASE_MOTTO));
try try
{ {
std::shared_ptr<llarp::Config> conf; std::shared_ptr<llarp::Config> conf;

@ -99,6 +99,8 @@ lokinet_add_library(lokinet-time-place
net/net_int.cpp net/net_int.cpp
net/sock_addr.cpp net/sock_addr.cpp
router_contact.cpp router_contact.cpp
router_contact_local.cpp
router_contact_remote.cpp
router_id.cpp router_id.cpp
router_version.cpp # to be deleted shortly router_version.cpp # to be deleted shortly
service/address.cpp service/address.cpp

@ -60,7 +60,7 @@ namespace llarp
else else
{ {
RouterContact rc; RouterContact rc;
if (not rc.Read(fpath)) if (not rc.read(fpath))
{ {
throw std::runtime_error{ throw std::runtime_error{
fmt::format("failed to decode bootstrap RC, file='{}', rc={}", fpath, rc)}; fmt::format("failed to decode bootstrap RC, file='{}', rc={}", fpath, rc)};

@ -58,9 +58,10 @@ namespace llarp
conf.defineOption<std::string>( conf.defineOption<std::string>(
"router", "router",
"netid", "netid",
Default{llarp::DEFAULT_NETID}, Default{llarp::LOKINET_DEFAULT_NETID},
Comment{ Comment{
"Network ID; this is '"s + llarp::DEFAULT_NETID + "' for mainnet, 'gamma' for testnet.", "Network ID; this is '"s + llarp::LOKINET_DEFAULT_NETID
+ "' for mainnet, 'gamma' for testnet.",
}, },
[this](std::string arg) { [this](std::string arg) {
if (arg.size() > NetID::size()) if (arg.size() > NetID::size())
@ -1329,7 +1330,7 @@ namespace llarp
std::set<IPRange> seenRanges; std::set<IPRange> seenRanges;
for (const auto& hop : rcs) for (const auto& hop : rcs)
{ {
const auto network_addr = net::In6ToHUInt(hop.addr.in6().sin6_addr) & netmask; const auto network_addr = net::In6ToHUInt(hop.addr6()->in6().sin6_addr) & netmask;
if (auto [it, inserted] = seenRanges.emplace(network_addr, netmask); not inserted) if (auto [it, inserted] = seenRanges.emplace(network_addr, netmask); not inserted)
{ {
return false; return false;
@ -1444,7 +1445,7 @@ namespace llarp
{ {
try try
{ {
ini = util::slurp_file(*fname); ini = util::file_to_string(*fname);
} }
catch (const std::exception&) catch (const std::exception&)
{ {

@ -15,7 +15,7 @@ namespace llarp
{ {
try try
{ {
m_Data = util::slurp_file(fname); m_Data = util::file_to_string(fname);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {

@ -52,7 +52,7 @@ namespace llarp
m_transportKeyPath = deriveFile(our_transport_key_filename, config.router.m_transportKeyFile); m_transportKeyPath = deriveFile(our_transport_key_filename, config.router.m_transportKeyFile);
RouterContact rc; RouterContact rc;
bool exists = rc.Read(m_rcPath); bool exists = rc.read(m_rcPath);
if (not exists and not genIfAbsent) if (not exists and not genIfAbsent)
{ {
LogError("Could not read RouterContact at path ", m_rcPath); LogError("Could not read RouterContact at path ", m_rcPath);
@ -61,7 +61,7 @@ namespace llarp
// we need to back up keys if our self.signed doesn't appear to have a // we need to back up keys if our self.signed doesn't appear to have a
// valid signature // valid signature
m_needBackup = (isSNode and not rc.VerifySignature()); m_needBackup = (isSNode and not rc.verify_signature());
// if our RC file can't be verified, assume it is out of date (e.g. uses // if our RC file can't be verified, assume it is out of date (e.g. uses
// older encryption) and needs to be regenerated. before doing so, backup // older encryption) and needs to be regenerated. before doing so, backup

@ -4,12 +4,11 @@
namespace llarp namespace llarp
{ {
// clang-format off // clang-format off
const std::array<uint16_t, 3> VERSION{{@lokinet_VERSION_MAJOR@, @lokinet_VERSION_MINOR@, @lokinet_VERSION_PATCH@}}; const std::array<uint16_t, 3> LOKINET_VERSION{{@lokinet_VERSION_MAJOR@, @lokinet_VERSION_MINOR@, @lokinet_VERSION_PATCH@}};
const std::array<uint64_t, 4> ROUTER_VERSION{{llarp::constants::proto_version, @lokinet_VERSION_MAJOR@, @lokinet_VERSION_MINOR@, @lokinet_VERSION_PATCH@}}; const char* const LOKINET_VERSION_TAG = "@VERSIONTAG@";
const char* const VERSION_TAG = "@VERSIONTAG@"; const char* const LOKINET_VERSION_FULL = "lokinet-@lokinet_VERSION_MAJOR@.@lokinet_VERSION_MINOR@.@lokinet_VERSION_PATCH@-@LOKINET_VERSION_TAG@";
const char* const VERSION_FULL = "lokinet-@lokinet_VERSION_MAJOR@.@lokinet_VERSION_MINOR@.@lokinet_VERSION_PATCH@-@VERSIONTAG@";
const char* const RELEASE_MOTTO = "@RELEASE_MOTTO@"; const char* const LOKINET_RELEASE_MOTTO = "@RELEASE_MOTTO@";
const char* const DEFAULT_NETID = "lokinet"; const char* const LOKINET_DEFAULT_NETID = "lokinet";
// clang-format on // clang-format on
} // namespace llarp } // namespace llarp

@ -6,11 +6,10 @@
namespace llarp namespace llarp
{ {
// Given a full lokinet version of: lokinet-1.2.3-abc these are: // Given a full lokinet version of: lokinet-1.2.3-abc these are:
extern const std::array<uint16_t, 3> VERSION; // [1, 2, 3] extern const std::array<uint16_t, 3> LOKINET_VERSION;
extern const std::array<uint64_t, 4> ROUTER_VERSION; // [proto, 1, 2, 3] extern const char* const LOKINET_VERSION_TAG;
extern const char* const VERSION_TAG; // "abc" extern const char* const LOKINET_VERSION_FULL;
extern const char* const VERSION_FULL; // "lokinet-1.2.3-abc"
extern const char* const RELEASE_MOTTO; extern const char* const LOKINET_RELEASE_MOTTO;
extern const char* const DEFAULT_NETID; extern const char* const LOKINET_DEFAULT_NETID;
} // namespace llarp } // namespace llarp

@ -59,7 +59,8 @@ namespace llarp
throw std::runtime_error("Cannot call Setup() on context without a Config"); throw std::runtime_error("Cannot call Setup() on context without a Config");
if (opts.showBanner) if (opts.showBanner)
llarp::LogInfo(fmt::format("{} {}", llarp::VERSION_FULL, llarp::RELEASE_MOTTO)); llarp::LogInfo(
fmt::format("{} {}", llarp::LOKINET_VERSION_FULL, llarp::LOKINET_RELEASE_MOTTO));
if (!loop) if (!loop)
{ {

@ -273,6 +273,14 @@ namespace llarp
return true; return true;
} }
bool
crypto::verify(const PubKey& pub, ustring_view data, ustring_view sig)
{
return (pub.size() == 32 && sig.size() == 64)
? crypto_sign_verify_detached(sig.data(), data.data(), data.size(), pub.data()) != -1
: false;
}
bool bool
crypto::verify(const PubKey& pub, uint8_t* buf, size_t size, const Signature& sig) crypto::verify(const PubKey& pub, uint8_t* buf, size_t size, const Signature& sig)
{ {
@ -428,11 +436,6 @@ namespace llarp
return true; 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 void
crypto::randomize(uint8_t* buf, size_t len) crypto::randomize(uint8_t* buf, size_t len)
{ {
@ -517,19 +520,19 @@ namespace llarp
#endif #endif
const byte_t* const byte_t*
seckey_topublic(const SecretKey& sec) seckey_to_pubkey(const SecretKey& sec)
{ {
return sec.data() + 32; return sec.data() + 32;
} }
const byte_t* const byte_t*
pq_keypair_to_public(const PQKeyPair& k) pq_keypair_to_pubkey(const PQKeyPair& k)
{ {
return k.data() + PQ_SECRETKEYSIZE; return k.data() + PQ_SECRETKEYSIZE;
} }
const byte_t* const byte_t*
pq_keypair_to_secret(const PQKeyPair& k) pq_keypair_to_seckey(const PQKeyPair& k)
{ {
return k.data(); return k.data();
} }

@ -64,8 +64,11 @@ namespace llarp
sign(Signature&, const PrivateKey&, uint8_t* buf, size_t size); sign(Signature&, const PrivateKey&, uint8_t* buf, size_t size);
/// ed25519 verify /// ed25519 verify
bool bool
verify(const PubKey&, ustring_view, ustring_view);
bool
verify(const PubKey&, uint8_t*, size_t, const Signature&); verify(const PubKey&, uint8_t*, size_t, const Signature&);
bool verify(ustring_view, ustring_view, ustring_view); bool
verify(ustring_view, ustring_view, ustring_view);
bool bool
verify(uint8_t*, uint8_t*, size_t, uint8_t*); verify(uint8_t*, uint8_t*, size_t, uint8_t*);
@ -87,9 +90,6 @@ namespace llarp
uint64_t key_n, uint64_t key_n,
const AlignedBuffer<32>* hash = nullptr); const AlignedBuffer<32>* hash = nullptr);
/// seed to secretkey
bool
seed_to_secretkey(llarp::SecretKey&, const llarp::IdentitySecret&);
/// randomize buffer /// randomize buffer
void void
randomize(uint8_t* buf, size_t len); randomize(uint8_t* buf, size_t len);
@ -124,13 +124,13 @@ namespace llarp
randint(); randint();
const byte_t* const byte_t*
seckey_topublic(const SecretKey& secret); seckey_to_pubkey(const SecretKey& secret);
const byte_t* const byte_t*
pq_keypair_to_public(const PQKeyPair& keypair); pq_keypair_to_pubkey(const PQKeyPair& keypair);
const byte_t* const byte_t*
pq_keypair_to_secret(const PQKeyPair& keypair); pq_keypair_to_seckey(const PQKeyPair& keypair);
/// rng type that uses llarp::randint(), which is cryptographically secure /// rng type that uses llarp::randint(), which is cryptographically secure
struct CSRNG struct CSRNG

@ -1,5 +1,6 @@
#include "types.hpp" #include "types.hpp"
#include <llarp/router_id.hpp>
#include <llarp/util/buffer.hpp> #include <llarp/util/buffer.hpp>
#include <llarp/util/file.hpp> #include <llarp/util/file.hpp>
@ -32,6 +33,36 @@ namespace llarp
return oxenc::to_hex(begin(), end()); return oxenc::to_hex(begin(), end());
} }
PubKey::operator RouterID() const
{
return {as_array()};
}
PubKey&
PubKey::operator=(const byte_t* ptr)
{
std::copy(ptr, ptr + SIZE, begin());
return *this;
}
bool
operator==(const PubKey& lhs, const PubKey& rhs)
{
return lhs.as_array() == rhs.as_array();
}
bool
operator==(const PubKey& lhs, const RouterID& rhs)
{
return lhs.as_array() == rhs.as_array();
}
bool
operator==(const RouterID& lhs, const PubKey& rhs)
{
return lhs.as_array() == rhs.as_array();
}
bool bool
SecretKey::LoadFromFile(const fs::path& fname) SecretKey::LoadFromFile(const fs::path& fname)
{ {
@ -39,7 +70,7 @@ namespace llarp
std::array<byte_t, 128> tmp; std::array<byte_t, 128> tmp;
try try
{ {
sz = util::slurp_file(fname, tmp.data(), tmp.size()); sz = util::file_to_buffer(fname, tmp.data(), tmp.size());
} }
catch (const std::exception&) catch (const std::exception&)
{ {
@ -107,53 +138,7 @@ namespace llarp
{ {
return false; return false;
} }
return true;
}
bool
IdentitySecret::LoadFromFile(const fs::path& fname)
{
std::array<byte_t, SIZE> buf;
size_t sz;
try
{
sz = util::slurp_file(fname, buf.data(), buf.size());
}
catch (const std::exception& e)
{
llarp::LogError("failed to load service node seed: ", e.what());
return false;
}
if (sz != SIZE)
{
llarp::LogError("service node seed size invalid: ", sz, " != ", SIZE);
return false;
}
std::copy(buf.begin(), buf.end(), begin());
return true; return true;
} }
byte_t*
Signature::Lo()
{
return data();
}
const byte_t*
Signature::Lo() const
{
return data();
}
byte_t*
Signature::Hi()
{
return data() + 32;
}
const byte_t*
Signature::Hi() const
{
return data() + 32;
}
} // namespace llarp } // namespace llarp

@ -2,7 +2,6 @@
#include "constants.hpp" #include "constants.hpp"
#include <llarp/router_id.hpp>
#include <llarp/util/aligned.hpp> #include <llarp/util/aligned.hpp>
#include <llarp/util/fs.hpp> #include <llarp/util/fs.hpp>
#include <llarp/util/types.hpp> #include <llarp/util/types.hpp>
@ -15,7 +14,9 @@ namespace llarp
using SharedSecret = AlignedBuffer<SHAREDKEYSIZE>; using SharedSecret = AlignedBuffer<SHAREDKEYSIZE>;
using KeyExchangeNonce = AlignedBuffer<32>; using KeyExchangeNonce = AlignedBuffer<32>;
struct PubKey final : public AlignedBuffer<PUBKEYSIZE> struct RouterID;
struct PubKey : public AlignedBuffer<PUBKEYSIZE>
{ {
PubKey() = default; PubKey() = default;
@ -37,36 +38,20 @@ namespace llarp
static PubKey static PubKey
from_string(const std::string& s); from_string(const std::string& s);
operator RouterID() const operator RouterID() const;
{
return {as_array()};
}
PubKey& PubKey&
operator=(const byte_t* ptr) operator=(const byte_t* ptr);
{
std::copy(ptr, ptr + SIZE, begin());
return *this;
}
}; };
inline bool bool
operator==(const PubKey& lhs, const PubKey& rhs) operator==(const PubKey& lhs, const PubKey& rhs);
{
return lhs.as_array() == rhs.as_array();
}
inline bool bool
operator==(const PubKey& lhs, const RouterID& rhs) operator==(const PubKey& lhs, const RouterID& rhs);
{
return lhs.as_array() == rhs.as_array();
}
inline bool bool
operator==(const RouterID& lhs, const PubKey& rhs) operator==(const RouterID& lhs, const PubKey& rhs);
{
return lhs.as_array() == rhs.as_array();
}
struct PrivateKey; struct PrivateKey;
@ -161,58 +146,24 @@ namespace llarp
toPublic(PubKey& pubkey) const; toPublic(PubKey& pubkey) const;
}; };
/// IdentitySecret is a secret key from a service node secret seed
struct IdentitySecret final : public AlignedBuffer<32>
{
IdentitySecret() : AlignedBuffer<32>()
{}
/// no copy constructor
explicit IdentitySecret(const IdentitySecret&) = delete;
// no byte data constructor
explicit IdentitySecret(const byte_t*) = delete;
/// load service node seed from file
bool
LoadFromFile(const fs::path& fname);
std::string_view
ToString() const
{
return "[IdentitySecret]";
}
};
template <> template <>
constexpr inline bool IsToStringFormattable<PubKey> = true; constexpr inline bool IsToStringFormattable<PubKey> = true;
template <> template <>
constexpr inline bool IsToStringFormattable<SecretKey> = true; constexpr inline bool IsToStringFormattable<SecretKey> = true;
template <> template <>
constexpr inline bool IsToStringFormattable<PrivateKey> = true; constexpr inline bool IsToStringFormattable<PrivateKey> = true;
template <>
constexpr inline bool IsToStringFormattable<IdentitySecret> = true;
using ShortHash = AlignedBuffer<SHORTHASHSIZE>; using ShortHash = AlignedBuffer<SHORTHASHSIZE>;
using LongHash = AlignedBuffer<HASHSIZE>; using LongHash = AlignedBuffer<HASHSIZE>;
struct Signature final : public AlignedBuffer<SIGSIZE> struct Signature final : public AlignedBuffer<SIGSIZE>
{ {
byte_t* //
Hi();
const byte_t*
Hi() const;
byte_t*
Lo();
const byte_t*
Lo() const;
}; };
using TunnelNonce = AlignedBuffer<TUNNONCESIZE>; using TunnelNonce = AlignedBuffer<TUNNONCESIZE>;
using SymmNonce = AlignedBuffer<NONCESIZE>; using SymmNonce = AlignedBuffer<NONCESIZE>;
using SymmKey = AlignedBuffer<32>; using SymmKey = AlignedBuffer<32>; // not used
using PQCipherBlock = AlignedBuffer<PQ_CIPHERTEXTSIZE + 1>; using PQCipherBlock = AlignedBuffer<PQ_CIPHERTEXTSIZE + 1>;
using PQPubKey = AlignedBuffer<PQ_PUBKEYSIZE>; using PQPubKey = AlignedBuffer<PQ_PUBKEYSIZE>;

@ -22,7 +22,7 @@ namespace llarp::dht
bool bool
operator()(const RouterContact& left, const RouterContact& right) const operator()(const RouterContact& left, const RouterContact& right) const
{ {
return (left.pubkey ^ us) < (right.pubkey ^ us); return (left.router_id() ^ us) < (right.router_id() ^ us);
} }
}; };
} // namespace llarp::dht } // namespace llarp::dht

@ -19,19 +19,19 @@ namespace llarp::dht
ID.Zero(); ID.Zero();
} }
RCNode(const RouterContact& other) : rc(other), ID(other.pubkey) RCNode(const RouterContact& other) : rc(other), ID(other.router_id())
{} {}
util::StatusObject util::StatusObject
ExtractStatus() const ExtractStatus() const
{ {
return rc.ExtractStatus(); return rc.extract_status();
} }
bool bool
operator<(const RCNode& other) const operator<(const RCNode& other) const
{ {
return rc.last_updated < other.rc.last_updated; return rc.timestamp() < other.rc.timestamp();
} }
}; };

@ -580,25 +580,29 @@ namespace llarp::handlers
{ {
router->modify_rc( router->modify_rc(
[srvRecords = SRVRecords()](RouterContact rc) -> std::optional<RouterContact> { [srvRecords = SRVRecords()](RouterContact rc) -> std::optional<RouterContact> {
// TODO: update this RouterContact handling
// check if there are any new srv records // check if there are any new srv records
bool shouldUpdate = false; // bool shouldUpdate = false;
for (const auto& rcSrv : rc.srvRecords) // for (const auto& rcSrv : rc.srvRecords)
{ // {
if (srvRecords.count(rcSrv) == 0) // if (srvRecords.count(rcSrv) == 0)
shouldUpdate = true; // shouldUpdate = true;
} // }
// // no new records so don't modify
// if (not shouldUpdate)
// return std::nullopt;
// // we got new entries so we clear the whole vector on the rc and recreate it
// rc.srvRecords.clear();
// no new records so don't modify // for (auto& record : srvRecords)
if (not shouldUpdate) // rc.srvRecords.emplace_back(record);
return std::nullopt;
// we got new entries so we clear the whole vector on the rc and recreate it // // set the verssion to 1 because we have srv records
rc.srvRecords.clear(); // rc.version = 1;
for (auto& record : srvRecords)
rc.srvRecords.emplace_back(record);
// set the verssion to 1 because we have srv records
rc.version = 1;
return rc; return rc;
}); });
} }

@ -39,7 +39,7 @@ namespace llarp
while (itr != nodes.end()) while (itr != nodes.end())
{ {
if (itr->second.rc.IsExpired(now)) if (itr->second.rc.is_expired(now))
itr = nodes.erase(itr); itr = nodes.erase(itr);
else else
++itr; ++itr;

@ -20,7 +20,7 @@ namespace llarp
std::shared_ptr<link::Connection> std::shared_ptr<link::Connection>
Endpoint::get_conn(const RouterContact& rc) const Endpoint::get_conn(const RouterContact& rc) const
{ {
if (auto itr = conns.find(rc.pubkey); itr != conns.end()) if (auto itr = conns.find(rc.router_id()); itr != conns.end())
return itr->second; return itr->second;
return nullptr; return nullptr;
@ -303,14 +303,14 @@ namespace llarp
void void
LinkManager::connect_to(const RouterContact& rc) LinkManager::connect_to(const RouterContact& rc)
{ {
if (auto conn = ep.get_conn(rc.pubkey); conn) if (auto conn = ep.get_conn(rc.router_id()); conn)
{ {
// TODO: should implement some connection failed logic, but not the same logic that // TODO: should implement some connection failed logic, but not the same logic that
// would be executed for another failure case // would be executed for another failure case
return; return;
} }
auto& remote_addr = rc.addr; const auto& remote_addr = rc.addr();
// TODO: confirm remote end is using the expected pubkey (RouterID). // TODO: confirm remote end is using the expected pubkey (RouterID).
// TODO: ALPN for "client" vs "relay" (could just be set on endpoint creation) // TODO: ALPN for "client" vs "relay" (could just be set on endpoint creation)
@ -491,9 +491,9 @@ namespace llarp
if (auto maybe_other = node_db->GetRandom(filter)) if (auto maybe_other = node_db->GetRandom(filter))
{ {
exclude.insert(maybe_other->pubkey); exclude.insert(maybe_other->router_id());
if (not rc_lookup->is_session_allowed(maybe_other->pubkey)) if (not rc_lookup->is_session_allowed(maybe_other->router_id()))
continue; continue;
connect_to(*maybe_other); connect_to(*maybe_other);
@ -609,23 +609,25 @@ namespace llarp
target_rid.FromString(target_key); target_rid.FromString(target_key);
const auto target_addr = dht::Key_t{reinterpret_cast<uint8_t*>(target_key.data())}; const auto target_addr = dht::Key_t{reinterpret_cast<uint8_t*>(target_key.data())};
const auto& local_rid = _router.rc().pubkey; const auto& local_rid = _router.rc().router_id();
const auto local_key = dht::Key_t{local_rid}; const auto local_key = dht::Key_t{local_rid};
if (is_exploratory) if (is_exploratory)
{ {
std::string neighbors{}; std::string neighbors{};
const auto closest_rcs = const auto closest_rcs =
_router.node_db()->find_many_closest_to(target_addr, RC_LOOKUP_STORAGE_REDUNDANCY); _router.node_db()->find_many_closest_to(target_addr, RC_LOOKUP_STORAGE_REDUNDANCY);
for (const auto& rc : closest_rcs) for (const auto& rc : closest_rcs)
{ {
const auto& rid = rc.pubkey; const auto& rid = rc.router_id();
if (_router.router_profiling().IsBadForConnect(rid) || target_rid == rid if (_router.router_profiling().IsBadForConnect(rid) || target_rid == rid
|| local_rid == rid) || local_rid == rid)
continue; continue;
neighbors += rid.bt_encode(); neighbors +=
rid.bt_encode(); // TODO: refactor to use reference to bt_dict_producer subdict
} }
m.respond( m.respond(
@ -635,12 +637,12 @@ namespace llarp
else else
{ {
const auto closest_rc = _router.node_db()->find_closest_to(target_addr); const auto closest_rc = _router.node_db()->find_closest_to(target_addr);
const auto& closest_rid = closest_rc.pubkey; const auto& closest_rid = closest_rc.router_id();
const auto closest_key = dht::Key_t{closest_rid}; const auto closest_key = dht::Key_t{closest_rid};
if (target_addr == closest_key) if (target_addr == closest_key)
{ {
if (closest_rc.ExpiresSoon(llarp::time_now_ms())) if (closest_rc.expires_within_delta(llarp::time_now_ms()))
{ {
send_control_message( send_control_message(
target_rid, target_rid,
@ -837,7 +839,7 @@ namespace llarp
const auto now = _router.now(); const auto now = _router.now();
const auto addr = dht::Key_t{reinterpret_cast<uint8_t*>(derived_signing_key.data())}; const auto addr = dht::Key_t{reinterpret_cast<uint8_t*>(derived_signing_key.data())};
const auto local_key = _router.rc().pubkey; const auto local_key = _router.rc().router_id();
if (not service::EncryptedIntroSet::verify(introset, derived_signing_key, sig)) if (not service::EncryptedIntroSet::verify(introset, derived_signing_key, sig))
{ {
@ -878,7 +880,7 @@ namespace llarp
log::info(link_cat, "Relaying PublishIntroMessage for {}", addr); log::info(link_cat, "Relaying PublishIntroMessage for {}", addr);
const auto& peer_rc = closest_rcs[relay_order]; const auto& peer_rc = closest_rcs[relay_order];
const auto& peer_key = peer_rc.pubkey; const auto& peer_key = peer_rc.router_id();
if (peer_key == local_key) if (peer_key == local_key)
{ {
@ -908,7 +910,7 @@ namespace llarp
for (const auto& rc : closest_rcs) for (const auto& rc : closest_rcs)
{ {
if (rc.pubkey == local_key) if (rc.router_id() == local_key)
{ {
rc_index = index; rc_index = index;
break; break;
@ -1024,7 +1026,7 @@ namespace llarp
log::info(link_cat, "Relaying FindIntroMessage for {}", addr); log::info(link_cat, "Relaying FindIntroMessage for {}", addr);
const auto& peer_rc = closest_rcs[relay_order]; const auto& peer_rc = closest_rcs[relay_order];
const auto& peer_key = peer_rc.pubkey; const auto& peer_key = peer_rc.router_id();
send_control_message( send_control_message(
peer_key, peer_key,

@ -372,8 +372,8 @@ namespace llarp
endpoint->connect(remote, link_manager.tls_creds, std::forward<Opt>(opts)...); endpoint->connect(remote, link_manager.tls_creds, std::forward<Opt>(opts)...);
// emplace immediately for connection open callback to find scid // emplace immediately for connection open callback to find scid
connid_map.emplace(conn_interface->scid(), rc.pubkey); connid_map.emplace(conn_interface->scid(), rc.router_id());
auto [itr, b] = conns.emplace(rc.pubkey, nullptr); auto [itr, b] = conns.emplace(rc.router_id(), nullptr);
auto control_stream = auto control_stream =
conn_interface->template get_new_stream<oxen::quic::BTRequestStream>(); conn_interface->template get_new_stream<oxen::quic::BTRequestStream>();

@ -22,7 +22,7 @@ namespace llarp
hop.nonce.Randomize(); hop.nonce.Randomize();
// do key exchange // do key exchange
if (!crypto::dh_client(hop.shared, hop.rc.pubkey, hop.commkey, hop.nonce)) if (!crypto::dh_client(hop.shared, hop.rc._pubkey, hop.commkey, hop.nonce))
{ {
auto err = fmt::format("Failed to generate shared key for path build!"); auto err = fmt::format("Failed to generate shared key for path build!");
log::warning(path_cat, err); log::warning(path_cat, err);
@ -60,7 +60,7 @@ namespace llarp
outer_nonce.Randomize(); outer_nonce.Randomize();
// derive (outer) shared key // derive (outer) shared key
if (!crypto::dh_client(shared, hop.rc.pubkey, framekey, outer_nonce)) if (!crypto::dh_client(shared, hop.rc._pubkey, framekey, outer_nonce))
{ {
log::warning(path_cat, "DH client failed during hop info encryption!"); log::warning(path_cat, "DH client failed during hop info encryption!");
throw std::runtime_error{"DH failed during hop info encryption"}; throw std::runtime_error{"DH failed during hop info encryption"};

@ -54,7 +54,7 @@ namespace llarp
bool bool
RelayUpstreamMessage::handle_message(Router* r) const RelayUpstreamMessage::handle_message(Router* r) const
{ {
auto path = r->path_context().GetByDownstream(conn->remote_rc.pubkey, pathid); auto path = r->path_context().GetByDownstream(conn->remote_rc._pubkey, pathid);
if (path) if (path)
{ {
return path->HandleUpstream(llarp_buffer_t(enc), nonce, r); return path->HandleUpstream(llarp_buffer_t(enc), nonce, r);
@ -110,7 +110,7 @@ namespace llarp
bool bool
RelayDownstreamMessage::handle_message(Router* r) const RelayDownstreamMessage::handle_message(Router* r) const
{ {
auto path = r->path_context().GetByUpstream(conn->remote_rc.pubkey, pathid); auto path = r->path_context().GetByUpstream(conn->remote_rc._pubkey, pathid);
if (path) if (path)
{ {
return path->HandleDownstream(llarp_buffer_t(enc), nonce, r); return path->HandleDownstream(llarp_buffer_t(enc), nonce, r);

@ -14,8 +14,6 @@ static const std::string RC_FILE_EXT = ".signed";
namespace llarp namespace llarp
{ {
static auto logcat = log::Cat("nodedb");
NodeDB::Entry::Entry(RouterContact value) : rc(std::move(value)), insertedAt(llarp::time_now_ms()) NodeDB::Entry::Entry(RouterContact value) : rc(std::move(value)), insertedAt(llarp::time_now_ms())
{} {}
@ -79,7 +77,7 @@ namespace llarp
// TODO: split this up? idk maybe some day... // TODO: split this up? idk maybe some day...
disk([this, data = std::move(copy)]() { disk([this, data = std::move(copy)]() {
for (const auto& rc : data) for (const auto& rc : data)
rc.Write(get_path_by_pubkey(rc.pubkey)); rc.write(get_path_by_pubkey(rc.router_id()));
}); });
}); });
} }
@ -123,20 +121,14 @@ namespace llarp
RouterContact rc{}; RouterContact rc{};
if (not rc.Read(f)) if (not rc.read(f))
{ {
// try loading it, purge it if it is junk // try loading it, purge it if it is junk
purge.emplace(f); purge.emplace(f);
return true; return true;
} }
if (not rc.FromOurNetwork()) if (rc.is_expired(time_now_ms()))
{
// skip entries that are not from our network
return true;
}
if (rc.IsExpired(time_now_ms()))
{ {
// rc expired dont load it and purge it later // rc expired dont load it and purge it later
purge.emplace(f); purge.emplace(f);
@ -145,8 +137,8 @@ namespace llarp
// validate signature and purge entries with invalid signatures // validate signature and purge entries with invalid signatures
// load ones with valid signatures // load ones with valid signatures
if (rc.VerifySignature()) if (rc.verify_signature()) // TODO: fix this after RouterContact -> RemoteRC
entries.emplace(rc.pubkey, rc); entries.emplace(rc.router_id(), rc);
else else
purge.emplace(f); purge.emplace(f);
@ -172,7 +164,7 @@ namespace llarp
router.loop()->call([this]() { router.loop()->call([this]() {
for (const auto& item : entries) for (const auto& item : entries)
item.second.rc.Write(get_path_by_pubkey(item.first)); item.second.rc.write(get_path_by_pubkey(item.first));
}); });
} }
@ -213,9 +205,9 @@ namespace llarp
auto itr = entries.begin(); auto itr = entries.begin();
while (itr != entries.end()) while (itr != entries.end())
{ {
if (itr->second.insertedAt < cutoff and keep.count(itr->second.rc.pubkey) == 0) if (itr->second.insertedAt < cutoff and keep.count(itr->second.rc.router_id()) == 0)
{ {
removed.insert(itr->second.rc.pubkey); removed.insert(itr->second.rc.router_id());
itr = entries.erase(itr); itr = entries.erase(itr);
} }
else else
@ -230,8 +222,8 @@ namespace llarp
NodeDB::put_rc(RouterContact rc) NodeDB::put_rc(RouterContact rc)
{ {
router.loop()->call([this, rc]() { router.loop()->call([this, rc]() {
entries.erase(rc.pubkey); entries.erase(rc.router_id());
entries.emplace(rc.pubkey, rc); entries.emplace(rc.router_id(), rc);
}); });
} }
@ -245,14 +237,14 @@ namespace llarp
NodeDB::put_rc_if_newer(RouterContact rc) NodeDB::put_rc_if_newer(RouterContact rc)
{ {
router.loop()->call([this, rc]() { router.loop()->call([this, rc]() {
auto itr = entries.find(rc.pubkey); auto itr = entries.find(rc.router_id());
if (itr == entries.end() or itr->second.rc.OtherIsNewer(rc)) if (itr == entries.end() or itr->second.rc.other_is_newer(rc))
{ {
// delete if existing // delete if existing
if (itr != entries.end()) if (itr != entries.end())
entries.erase(itr); entries.erase(itr);
// add new entry // add new entry
entries.emplace(rc.pubkey, rc); entries.emplace(rc.router_id(), rc);
} }
}); });
} }
@ -282,14 +274,14 @@ namespace llarp
llarp::RouterContact rc; llarp::RouterContact rc;
const llarp::dht::XorMetric compare(location); const llarp::dht::XorMetric compare(location);
VisitAll([&rc, compare](const auto& otherRC) { VisitAll([&rc, compare](const auto& otherRC) {
if (rc.pubkey.IsZero()) if (rc.router_id().IsZero())
{ {
rc = otherRC; rc = otherRC;
return; return;
} }
if (compare( if (compare(
llarp::dht::Key_t{otherRC.pubkey.as_array()}, llarp::dht::Key_t{otherRC.pubkey.as_array()},
llarp::dht::Key_t{rc.pubkey.as_array()})) llarp::dht::Key_t{rc.router_id().as_array()}))
rc = otherRC; rc = otherRC;
}); });
return rc; return rc;

@ -150,7 +150,7 @@ namespace llarp
{ {
if (visit(itr->second.rc)) if (visit(itr->second.rc))
{ {
removed.insert(itr->second.rc.pubkey); removed.insert(itr->second.rc.router_id());
itr = entries.erase(itr); itr = entries.erase(itr);
} }
else else

@ -40,7 +40,7 @@ namespace llarp::path
hops[idx].txID = hops[idx + 1].rxID; hops[idx].txID = hops[idx + 1].rxID;
} }
// initialize parts of the introduction // initialize parts of the introduction
intro.router = hops[hsz - 1].rc.pubkey; intro.router = hops[hsz - 1].rc._router_id;
intro.path_id = hops[hsz - 1].txID; intro.path_id = hops[hsz - 1].txID;
if (auto parent = m_PathSet.lock()) if (auto parent = m_PathSet.lock())
EnterState(ePathBuilding, parent->Now()); EnterState(ePathBuilding, parent->Now());
@ -152,13 +152,13 @@ namespace llarp::path
RouterID RouterID
Path::Endpoint() const Path::Endpoint() const
{ {
return hops[hops.size() - 1].rc.pubkey; return hops[hops.size() - 1].rc._router_id;
} }
PubKey PubKey
Path::EndpointPubKey() const Path::EndpointPubKey() const
{ {
return hops[hops.size() - 1].rc.pubkey; return hops[hops.size() - 1].rc._router_id;
} }
PathID_t PathID_t
@ -184,13 +184,13 @@ namespace llarp::path
bool bool
Path::is_endpoint(const RouterID& r, const PathID_t& id) const Path::is_endpoint(const RouterID& r, const PathID_t& id) const
{ {
return hops[hops.size() - 1].rc.pubkey == r && hops[hops.size() - 1].txID == id; return hops[hops.size() - 1].rc._router_id == r && hops[hops.size() - 1].txID == id;
} }
RouterID RouterID
Path::upstream() const Path::upstream() const
{ {
return hops[0].rc.pubkey; return hops[0].rc._router_id;
} }
const std::string& const std::string&
@ -208,7 +208,7 @@ namespace llarp::path
{ {
if (!hops.empty()) if (!hops.empty())
hops_str += " -> "; hops_str += " -> ";
hops_str += RouterID(hop.rc.pubkey).ToString(); hops_str += RouterID(hop.rc._router_id).ToString();
} }
return hops_str; return hops_str;
} }
@ -262,9 +262,9 @@ namespace llarp::path
PathHopConfig::ExtractStatus() const PathHopConfig::ExtractStatus() const
{ {
util::StatusObject obj{ util::StatusObject obj{
{"ip", rc.addr.to_string()}, {"ip", rc._addr.to_string()},
{"lifetime", to_json(lifetime)}, {"lifetime", to_json(lifetime)},
{"router", rc.pubkey.ToHex()}, {"router", rc._router_id.ToHex()},
{"txid", txID.ToHex()}, {"txid", txID.ToHex()},
{"rxid", rxID.ToHex()}}; {"rxid", rxID.ToHex()}};
return obj; return obj;

@ -83,7 +83,7 @@ namespace llarp
hop.nonce.Randomize(); hop.nonce.Randomize();
// do key exchange // do key exchange
if (!crypto::dh_client(hop.shared, hop.rc.pubkey, hop.commkey, hop.nonce)) if (!crypto::dh_client(hop.shared, hop.rc._pubkey, hop.commkey, hop.nonce))
{ {
auto err = fmt::format("{} failed to generate shared key for path build!", Name()); auto err = fmt::format("{} failed to generate shared key for path build!", Name());
log::error(path_cat, err); log::error(path_cat, err);
@ -121,7 +121,7 @@ namespace llarp
outer_nonce.Randomize(); outer_nonce.Randomize();
// derive (outer) shared key // derive (outer) shared key
if (!crypto::dh_client(shared, hop.rc.pubkey, framekey, outer_nonce)) if (!crypto::dh_client(shared, hop.rc._pubkey, framekey, outer_nonce))
{ {
log::error(path_cat, "DH client failed during hop info encryption!"); log::error(path_cat, "DH client failed during hop info encryption!");
throw std::runtime_error{"DH failed during hop info encryption"}; throw std::runtime_error{"DH failed during hop info encryption"};
@ -218,16 +218,16 @@ namespace llarp
router->for_each_connection([&](link::Connection& conn) { router->for_each_connection([&](link::Connection& conn) {
const auto& rc = conn.remote_rc; const auto& rc = conn.remote_rc;
#ifndef TESTNET #ifndef TESTNET
if (router->IsBootstrapNode(rc.pubkey)) if (router->IsBootstrapNode(rc._pubkey))
return; return;
#endif #endif
if (exclude.count(rc.pubkey)) if (exclude.count(rc._pubkey))
return; return;
if (BuildCooldownHit(rc.pubkey)) if (BuildCooldownHit(rc._pubkey))
return; return;
if (router->router_profiling().IsBadForPath(rc.pubkey)) if (router->router_profiling().IsBadForPath(rc._pubkey))
return; return;
found = rc; found = rc;
@ -243,7 +243,7 @@ namespace llarp
}; };
if (const auto maybe = router->node_db()->GetRandom(filter)) if (const auto maybe = router->node_db()->GetRandom(filter))
{ {
return GetHopsAlignedToForBuild(maybe->pubkey); return GetHopsAlignedToForBuild(maybe->_pubkey);
} }
return std::nullopt; return std::nullopt;
} }
@ -353,7 +353,7 @@ namespace llarp
return false; return false;
for (const auto& hop : hopsSet) for (const auto& hop : hopsSet)
{ {
if (hop.pubkey == rc.pubkey) if (hop._pubkey == rc.pubkey)
return false; return false;
} }
@ -362,7 +362,7 @@ namespace llarp
if (not pathConfig.Acceptable(hopsSet)) if (not pathConfig.Acceptable(hopsSet))
return false; return false;
#endif #endif
return rc.pubkey != endpointRC.pubkey; return rc.pubkey != endpointRC._pubkey;
}; };
if (const auto maybe = router->node_db()->GetRandom(filter)) if (const auto maybe = router->node_db()->GetRandom(filter))
@ -402,7 +402,7 @@ namespace llarp
} }
lastBuild = llarp::time_now_ms(); lastBuild = llarp::time_now_ms();
const RouterID edge{hops[0].pubkey}; const RouterID edge{hops[0]._pubkey};
if (not router->pathbuild_limiter().Attempt(edge)) if (not router->pathbuild_limiter().Attempt(edge))
{ {
@ -429,7 +429,7 @@ namespace llarp
{ {
bool lastHop = (i == (n_hops - 1)); bool lastHop = (i == (n_hops - 1));
const auto& nextHop = lastHop ? path_hops[i].rc.pubkey : path_hops[i + 1].rc.pubkey; const auto& nextHop = lastHop ? path_hops[i].rc._pubkey : path_hops[i + 1].rc._pubkey;
PathBuildMessage::setup_hop_keys(path_hops[i], nextHop); PathBuildMessage::setup_hop_keys(path_hops[i], nextHop);
auto frame_str = PathBuildMessage::serialize(path_hops[i]); auto frame_str = PathBuildMessage::serialize(path_hops[i]);
@ -533,7 +533,7 @@ namespace llarp
DoPathBuildBackoff(); DoPathBuildBackoff();
for (const auto& hop : p->hops) for (const auto& hop : p->hops)
{ {
const RouterID target{hop.rc.pubkey}; const RouterID target{hop.rc._pubkey};
// look up router and see if it's still on the network // look up router and see if it's still on the network
log::info(path_cat, "Looking up RouterID {} due to path build timeout", target); log::info(path_cat, "Looking up RouterID {} due to path build timeout", target);

@ -10,8 +10,6 @@ using oxenc::bt_dict_producer;
namespace llarp namespace llarp
{ {
static auto logcat = log::Cat("profiling");
RouterProfile::RouterProfile(bt_dict_consumer dict) RouterProfile::RouterProfile(bt_dict_consumer dict)
{ {
BDecode(std::move(dict)); BDecode(std::move(dict));
@ -204,7 +202,7 @@ namespace llarp
first = false; first = false;
else else
{ {
auto& profile = m_Profiles[hop.rc.pubkey]; auto& profile = m_Profiles[hop.rc.router_id()];
profile.pathFailCount += 1; profile.pathFailCount += 1;
profile.lastUpdated = llarp::time_now_ms(); profile.lastUpdated = llarp::time_now_ms();
} }
@ -217,7 +215,7 @@ namespace llarp
util::Lock lock{m_ProfilesMutex}; util::Lock lock{m_ProfilesMutex};
for (const auto& hop : p->hops) for (const auto& hop : p->hops)
{ {
auto& profile = m_Profiles[hop.rc.pubkey]; auto& profile = m_Profiles[hop.rc.router_id()];
profile.pathTimeoutCount += 1; profile.pathTimeoutCount += 1;
profile.lastUpdated = llarp::time_now_ms(); profile.lastUpdated = llarp::time_now_ms();
} }
@ -230,7 +228,7 @@ namespace llarp
const auto sz = p->hops.size(); const auto sz = p->hops.size();
for (const auto& hop : p->hops) for (const auto& hop : p->hops)
{ {
auto& profile = m_Profiles[hop.rc.pubkey]; auto& profile = m_Profiles[hop.rc.router_id()];
// redeem previous fails by halfing the fail count and setting timeout to zero // redeem previous fails by halfing the fail count and setting timeout to zero
profile.pathFailCount /= 2; profile.pathFailCount /= 2;
profile.pathTimeoutCount = 0; profile.pathTimeoutCount = 0;
@ -299,7 +297,7 @@ namespace llarp
{ {
try try
{ {
std::string data = util::slurp_file(fname); std::string data = util::file_to_string(fname);
util::Lock lock{m_ProfilesMutex}; util::Lock lock{m_ProfilesMutex};
BDecode(bt_dict_consumer{data}); BDecode(bt_dict_consumer{data});
} }

@ -30,7 +30,7 @@ namespace llarp
bool bool
RCGossiper::IsOurRC(const RouterContact& rc) const RCGossiper::IsOurRC(const RouterContact& rc) const
{ {
return rc.pubkey == rid; return rc.router_id() == rid;
} }
void void
@ -67,11 +67,11 @@ namespace llarp
RCGossiper::GossipRC(const RouterContact& rc) RCGossiper::GossipRC(const RouterContact& rc)
{ {
// only distribute public routers // only distribute public routers
if (not rc.IsPublicRouter()) if (not rc.is_public_router())
return false; return false;
if (link_manager == nullptr) if (link_manager == nullptr)
return false; return false;
const RouterID pubkey(rc.pubkey); const RouterID pubkey(rc.router_id());
// filter check // filter check
if (filter.Contains(pubkey)) if (filter.Contains(pubkey))
return false; return false;

@ -101,6 +101,7 @@ namespace llarp
throw; throw;
} }
// TODO: replace this with construction of RemoteRC
RouterContact result{std::move(payload)}; RouterContact result{std::move(payload)};
if (callback) if (callback)
@ -204,22 +205,22 @@ namespace llarp
bool bool
RCLookupHandler::check_rc(const RouterContact& rc) const RCLookupHandler::check_rc(const RouterContact& rc) const
{ {
if (not is_session_allowed(rc.pubkey)) if (not is_session_allowed(rc.router_id()))
{ {
contacts->delete_rc_node_async(dht::Key_t{rc.pubkey}); contacts->delete_rc_node_async(dht::Key_t{rc.router_id()});
return false; return false;
} }
if (not rc.Verify(llarp::time_now_ms())) if (not rc.verify(llarp::time_now_ms())) // TODO: fix this call after RouterContact -> RemoteRC
{ {
LogWarn("RC for ", RouterID(rc.pubkey), " is invalid"); LogWarn("RC for ", RouterID(rc.router_id()), " is invalid");
return false; return false;
} }
// update nodedb if required // update nodedb if required
if (rc.IsPublicRouter()) if (rc.is_public_router())
{ {
LogDebug("Adding or updating RC for ", RouterID(rc.pubkey), " to nodedb and dht."); LogDebug("Adding or updating RC for ", RouterID(rc.router_id()), " to nodedb and dht.");
node_db->put_rc_if_newer(rc); node_db->put_rc_if_newer(rc);
contacts->put_rc_node_async(rc); contacts->put_rc_node_async(rc);
} }
@ -252,17 +253,17 @@ namespace llarp
RCLookupHandler::check_renegotiate_valid(RouterContact newrc, RouterContact oldrc) RCLookupHandler::check_renegotiate_valid(RouterContact newrc, RouterContact oldrc)
{ {
// mismatch of identity ? // mismatch of identity ?
if (newrc.pubkey != oldrc.pubkey) if (newrc.router_id() != oldrc.router_id())
return false; return false;
if (!is_session_allowed(newrc.pubkey)) if (!is_session_allowed(newrc.router_id()))
return false; return false;
auto func = [this, newrc] { check_rc(newrc); }; auto func = [this, newrc] { check_rc(newrc); };
work_func(func); work_func(func);
// update dht if required // update dht if required
if (contacts->rc_nodes()->HasNode(dht::Key_t{newrc.pubkey})) if (contacts->rc_nodes()->HasNode(dht::Key_t{newrc.router_id()}))
{ {
contacts->rc_nodes()->PutNode(newrc); contacts->rc_nodes()->PutNode(newrc);
} }
@ -278,15 +279,15 @@ namespace llarp
std::unordered_set<RouterID> routersToLookUp; std::unordered_set<RouterID> routersToLookUp;
node_db->VisitInsertedBefore( node_db->VisitInsertedBefore(
[&](const RouterContact& rc) { routersToLookUp.insert(rc.pubkey); }, [&](const RouterContact& rc) { routersToLookUp.insert(rc.router_id()); },
now - RouterContact::UpdateInterval); now - RouterContact::REPUBLISH);
for (const auto& router : routersToLookUp) for (const auto& router : routersToLookUp)
{ {
get_rc(router, nullptr, true); get_rc(router, nullptr, true);
} }
node_db->remove_stale_rcs(boostrap_rid_list, now - RouterContact::StaleInsertionAge); node_db->remove_stale_rcs(boostrap_rid_list, now - RouterContact::STALE);
} }
void void
@ -301,7 +302,7 @@ namespace llarp
{ {
for (const auto& rc : bootstrap_rc_list) for (const auto& rc : bootstrap_rc_list)
{ {
log::info(link_cat, "Doing explore via bootstrap node: {}", RouterID(rc.pubkey)); log::info(link_cat, "Doing explore via bootstrap node: {}", RouterID(rc.router_id()));
// TODO: replace this concept // TODO: replace this concept
// dht->ExploreNetworkVia(dht::Key_t{rc.pubkey}); // dht->ExploreNetworkVia(dht::Key_t{rc.pubkey});
@ -383,7 +384,7 @@ namespace llarp
for (const auto& rc : bootstrap_rc_list) for (const auto& rc : bootstrap_rc_list)
{ {
boostrap_rid_list.insert(rc.pubkey); boostrap_rid_list.insert(rc.router_id());
} }
} }
@ -392,7 +393,7 @@ namespace llarp
{ {
for (const auto& rc : bootstrap_rc_list) for (const auto& rc : bootstrap_rc_list)
{ {
if (rc.pubkey == remote) if (rc.router_id() == remote)
{ {
return true; return true;
} }

@ -6,8 +6,6 @@
namespace llarp namespace llarp
{ {
static auto logcat = log::Cat("route-poker");
void void
RoutePoker::add_route(oxen::quic::Address ip) RoutePoker::add_route(oxen::quic::Address ip)
{ {
@ -221,7 +219,7 @@ namespace llarp
// explicit route pokes for first hops // explicit route pokes for first hops
router.for_each_connection( router.for_each_connection(
[this](link::Connection conn) { add_route(conn.remote_rc.addr); }); [this](link::Connection conn) { add_route(conn.remote_rc.addr()); });
add_route(router.link_manager().local()); add_route(router.link_manager().local());
// add default route // add default route
@ -240,7 +238,7 @@ namespace llarp
{ {
// unpoke routes for first hops // unpoke routes for first hops
router.for_each_connection( router.for_each_connection(
[this](link::Connection conn) { delete_route(conn.remote_rc.addr); }); [this](link::Connection conn) { delete_route(conn.remote_rc.addr()); });
if (is_enabled() and is_up) if (is_enabled() and is_up)
{ {
vpn::AbstractRouteManager& route = router.vpn_platform()->RouteManager(); vpn::AbstractRouteManager& route = router.vpn_platform()->RouteManager();

@ -36,8 +36,6 @@ static constexpr std::chrono::milliseconds ROUTER_TICK_INTERVAL = 250ms;
namespace llarp namespace llarp
{ {
static auto logcat = log::Cat("router");
Router::Router(EventLoop_ptr loop, std::shared_ptr<vpn::Platform> vpnPlatform) Router::Router(EventLoop_ptr loop, std::shared_ptr<vpn::Platform> vpnPlatform)
: _route_poker{std::make_shared<RoutePoker>(*this)} : _route_poker{std::make_shared<RoutePoker>(*this)}
, _lmq{std::make_shared<oxenmq::OxenMQ>()} , _lmq{std::make_shared<oxenmq::OxenMQ>()}
@ -178,7 +176,7 @@ namespace llarp
util::StatusObject stats{ util::StatusObject stats{
{"running", true}, {"running", true},
{"version", llarp::VERSION_FULL}, {"version", llarp::LOKINET_VERSION_FULL},
{"uptime", to_json(Uptime())}, {"uptime", to_json(Uptime())},
{"numPathsBuilt", pathsCount}, {"numPathsBuilt", pathsCount},
{"numPeersConnected", peers}, {"numPeersConnected", peers},
@ -216,8 +214,9 @@ namespace llarp
std::unordered_set<RouterID> peer_pubkeys; std::unordered_set<RouterID> peer_pubkeys;
for_each_connection( for_each_connection([&peer_pubkeys](link::Connection& conn) {
[&peer_pubkeys](link::Connection& conn) { peer_pubkeys.emplace(conn.remote_rc.pubkey); }); peer_pubkeys.emplace(conn.remote_rc.router_id());
});
loop()->call([this, &peer_pubkeys]() { loop()->call([this, &peer_pubkeys]() {
for (auto& pk : peer_pubkeys) for (auto& pk : peer_pubkeys)
@ -253,7 +252,7 @@ namespace llarp
if (auto maybe = node_db()->GetRandom([](const auto&) -> bool { return true; })) if (auto maybe = node_db()->GetRandom([](const auto&) -> bool { return true; }))
{ {
router = maybe->pubkey; router = maybe->router_id();
return true; return true;
} }
return false; return false;
@ -444,16 +443,16 @@ namespace llarp
Router::HandleSaveRC() const Router::HandleSaveRC() const
{ {
std::string fname = our_rc_file.string(); std::string fname = our_rc_file.string();
router_contact.Write(fname.c_str()); router_contact.write(fname.c_str());
} }
bool bool
Router::SaveRC() Router::SaveRC()
{ {
LogDebug("verify RC signature"); LogDebug("verify RC signature");
if (!router_contact.Verify(now())) if (!router_contact.verify(now())) // TODO: RouterContact -> RemoteRC
{ {
Dump<MAX_RC_SIZE>(rc()); Dump<RouterContact::MAX_RC_SIZE>(rc());
LogError("RC is invalid, not saving"); LogError("RC is invalid, not saving");
return false; return false;
} }
@ -569,9 +568,7 @@ namespace llarp
{ {
SecretKey nextOnionKey; SecretKey nextOnionKey;
RouterContact nextRC = router_contact; RouterContact nextRC = router_contact;
if (!nextRC.Sign(identity())) if (!nextRC.sign(identity())) // TODO: RouterContact -> LocalRC
return false;
if (!nextRC.Verify(time_now_ms(), false))
return false; return false;
router_contact = std::move(nextRC); router_contact = std::move(nextRC);
if (IsServiceNode()) if (IsServiceNode())
@ -585,20 +582,17 @@ namespace llarp
// Set netid before anything else // Set netid before anything else
log::debug(logcat, "Network ID set to {}", conf.router.m_netId); log::debug(logcat, "Network ID set to {}", conf.router.m_netId);
if (!conf.router.m_netId.empty() if (!conf.router.m_netId.empty()
&& strcmp(conf.router.m_netId.c_str(), llarp::DEFAULT_NETID) != 0) && strcmp(conf.router.m_netId.c_str(), llarp::LOKINET_DEFAULT_NETID) != 0)
{ {
const auto& netid = conf.router.m_netId; const auto& netid = conf.router.m_netId;
llarp::LogWarn( llarp::LogWarn(
"!!!! you have manually set netid to be '", "!!!! you have manually set netid to be '",
netid, netid,
"' which does not equal '", "' which does not equal '",
llarp::DEFAULT_NETID, llarp::LOKINET_DEFAULT_NETID,
"' you will run as a different network, good luck " "' you will run as a different network, good luck "
"and don't forget: something something MUH traffic " "and don't forget: something something MUH traffic "
"shape correlation !!!!"); "shape correlation !!!!");
NetID::DefaultValue() = NetID(reinterpret_cast<const byte_t*>(netid.c_str()));
// reset netid in our rc
router_contact.netID = llarp::NetID();
} }
// Router config // Router config
@ -628,7 +622,7 @@ namespace llarp
else else
log::debug(logcat, "No explicit public address given; will auto-detect during link setup"); log::debug(logcat, "No explicit public address given; will auto-detect during link setup");
RouterContact::BlockBogons = conf.router.m_blockBogons; RouterContact::BLOCK_BOGONS = conf.router.m_blockBogons;
auto& networkConfig = conf.network; auto& networkConfig = conf.network;
@ -679,10 +673,10 @@ namespace llarp
auto clearBadRCs = [this]() { auto clearBadRCs = [this]() {
for (auto it = bootstrap_rc_list.begin(); it != bootstrap_rc_list.end();) for (auto it = bootstrap_rc_list.begin(); it != bootstrap_rc_list.end();)
{ {
if (it->IsObsoleteBootstrap()) if (it->is_obsolete_bootstrap())
log::warning(logcat, "ignoring obsolete boostrap RC: {}", RouterID{it->pubkey}); log::warning(logcat, "ignoring obsolete boostrap RC: {}", RouterID{it->router_id()});
else if (not it->Verify(now())) else if (not it->verify(now())) // TODO: RouterContact -> RemoteRC
log::warning(logcat, "ignoring invalid bootstrap RC: {}", RouterID{it->pubkey}); log::warning(logcat, "ignoring invalid bootstrap RC: {}", RouterID{it->router_id()});
else else
{ {
++it; ++it;
@ -785,7 +779,7 @@ namespace llarp
return std::count_if( return std::count_if(
bootstrap_rc_list.begin(), bootstrap_rc_list.begin(),
bootstrap_rc_list.end(), bootstrap_rc_list.end(),
[r](const RouterContact& rc) -> bool { return rc.pubkey == r; }) [r](const RouterContact& rc) -> bool { return rc.router_id() == r; })
> 0; > 0;
} }
@ -806,8 +800,8 @@ namespace llarp
if (IsServiceNode()) if (IsServiceNode())
{ {
LogInfo(NumberOfConnectedClients(), " client connections"); LogInfo(NumberOfConnectedClients(), " client connections");
LogInfo(ToString(router_contact.Age(now)), " since we last updated our RC"); LogInfo(ToString(router_contact.age(now)), " since we last updated our RC");
LogInfo(ToString(router_contact.TimeUntilExpires(now)), " until our RC expires"); LogInfo(ToString(router_contact.time_to_expiry(now)), " until our RC expires");
} }
if (_last_stats_report > 0s) if (_last_stats_report > 0s)
LogInfo(ToString(now - _last_stats_report), " last reported stats"); LogInfo(ToString(now - _last_stats_report), " last reported stats");
@ -819,7 +813,7 @@ namespace llarp
{ {
std::string status; std::string status;
auto out = std::back_inserter(status); auto out = std::back_inserter(status);
fmt::format_to(out, "v{}", fmt::join(llarp::VERSION, ".")); fmt::format_to(out, "v{}", fmt::join(llarp::LOKINET_VERSION, "."));
if (IsServiceNode()) if (IsServiceNode())
{ {
fmt::format_to( fmt::format_to(
@ -901,7 +895,8 @@ namespace llarp
bool should_gossip = appears_funded(); bool should_gossip = appears_funded();
if (is_snode if (is_snode
and (router_contact.ExpiresSoon(now, std::chrono::milliseconds(randint() % 10000)) or (now - router_contact.last_updated) > rc_regen_interval)) and (router_contact.expires_within_delta(now, std::chrono::milliseconds(randint() % 10000))
or (now - router_contact.timestamp().time_since_epoch()) > rc_regen_interval))
{ {
LogInfo("regenerating RC"); LogInfo("regenerating RC");
if (update_rc()) if (update_rc())
@ -923,22 +918,22 @@ namespace llarp
// remove RCs for nodes that are no longer allowed by network policy // remove RCs for nodes that are no longer allowed by network policy
node_db()->RemoveIf([&](const RouterContact& rc) -> bool { node_db()->RemoveIf([&](const RouterContact& rc) -> bool {
// don't purge bootstrap nodes from nodedb // don't purge bootstrap nodes from nodedb
if (IsBootstrapNode(rc.pubkey)) if (IsBootstrapNode(rc.router_id()))
{ {
log::trace(logcat, "Not removing {}: is bootstrap node", rc.pubkey); log::trace(logcat, "Not removing {}: is bootstrap node", rc.router_id());
return false; return false;
} }
// if for some reason we stored an RC that isn't a valid router // if for some reason we stored an RC that isn't a valid router
// purge this entry // purge this entry
if (not rc.IsPublicRouter()) if (not rc.is_public_router())
{ {
log::debug(logcat, "Removing {}: not a valid router", rc.pubkey); log::debug(logcat, "Removing {}: not a valid router", rc.router_id());
return true; return true;
} }
/// clear out a fully expired RC /// clear out a fully expired RC
if (rc.IsExpired(now)) if (rc.is_expired(now))
{ {
log::debug(logcat, "Removing {}: RC is expired", rc.pubkey); log::debug(logcat, "Removing {}: RC is expired", rc.router_id());
return true; return true;
} }
// clients have no notion of a whilelist // clients have no notion of a whilelist
@ -946,23 +941,23 @@ namespace llarp
// routers that are not whitelisted for first hops // routers that are not whitelisted for first hops
if (not is_snode) if (not is_snode)
{ {
log::trace(logcat, "Not removing {}: we are a client and it looks fine", rc.pubkey); log::trace(logcat, "Not removing {}: we are a client and it looks fine", rc.router_id());
return false; return false;
} }
// if we don't have the whitelist yet don't remove the entry // if we don't have the whitelist yet don't remove the entry
if (not has_whitelist) if (not has_whitelist)
{ {
log::debug(logcat, "Skipping check on {}: don't have whitelist yet", rc.pubkey); log::debug(logcat, "Skipping check on {}: don't have whitelist yet", rc.router_id());
return false; return false;
} }
// if we have no whitelist enabled or we have // if we have no whitelist enabled or we have
// the whitelist enabled and we got the whitelist // the whitelist enabled and we got the whitelist
// check against the whitelist and remove if it's not // check against the whitelist and remove if it's not
// in the whitelist OR if there is no whitelist don't remove // in the whitelist OR if there is no whitelist don't remove
if (has_whitelist and not _rc_lookup_handler.is_session_allowed(rc.pubkey)) if (has_whitelist and not _rc_lookup_handler.is_session_allowed(rc.router_id()))
{ {
log::debug(logcat, "Removing {}: not a valid router", rc.pubkey); log::debug(logcat, "Removing {}: not a valid router", rc.router_id());
return true; return true;
} }
return false; return false;
@ -971,10 +966,10 @@ namespace llarp
if (not is_snode or not has_whitelist) if (not is_snode or not has_whitelist)
{ {
// find all deregistered relays // find all deregistered relays
std::unordered_set<PubKey> close_peers; std::unordered_set<RouterID> close_peers;
for_each_connection([this, &close_peers](link::Connection& conn) { for_each_connection([this, &close_peers](link::Connection& conn) {
const auto& pk = conn.remote_rc.pubkey; const auto& pk = conn.remote_rc.router_id();
if (conn.remote_is_relay and not _rc_lookup_handler.is_session_allowed(pk)) if (conn.remote_is_relay and not _rc_lookup_handler.is_session_allowed(pk))
close_peers.insert(pk); close_peers.insert(pk);
@ -1051,7 +1046,7 @@ namespace llarp
std::set<dht::Key_t> peer_keys; std::set<dht::Key_t> peer_keys;
for_each_connection( for_each_connection(
[&peer_keys](link::Connection& conn) { peer_keys.emplace(conn.remote_rc.pubkey); }); [&peer_keys](link::Connection& conn) { peer_keys.emplace(conn.remote_rc.router_id()); });
_contacts->rc_nodes()->RemoveIf( _contacts->rc_nodes()->RemoveIf(
[&peer_keys](const dht::Key_t& k) -> bool { return peer_keys.count(k) == 0; }); [&peer_keys](const dht::Key_t& k) -> bool { return peer_keys.count(k) == 0; });
@ -1112,15 +1107,18 @@ namespace llarp
if (is_running || is_stopping) if (is_running || is_stopping)
return false; return false;
// set public signing key // TODO: replace all this logic with construction of LocalRC
router_contact.pubkey = seckey_topublic(identity());
/* // set public signing key
router_contact._router_id = seckey_topublic(identity());
// set router version if service node // set router version if service node
if (IsServiceNode()) if (IsServiceNode())
{ {
router_contact.routerVersion = RouterVersion(llarp::VERSION, llarp::constants::proto_version); router_contact.routerVersion =
RouterVersion(llarp::LOKINET_VERSION, llarp::constants::proto_version);
} }
if (IsServiceNode() and not router_contact.IsPublicRouter()) if (IsServiceNode() and not router_contact.is_public_router())
{ {
LogError("we are configured as relay but have no reachable addresses"); LogError("we are configured as relay but have no reachable addresses");
return false; return false;
@ -1130,7 +1128,7 @@ namespace llarp
router_contact.enckey = seckey_topublic(encryption()); router_contact.enckey = seckey_topublic(encryption());
LogInfo("Signing rc..."); LogInfo("Signing rc...");
if (!router_contact.Sign(identity())) if (!router_contact.sign(identity()))
{ {
LogError("failed to sign rc"); LogError("failed to sign rc");
return false; return false;
@ -1166,14 +1164,14 @@ namespace llarp
// regenerate keys and resign rc before everything else // regenerate keys and resign rc before everything else
crypto::identity_keygen(_identity); crypto::identity_keygen(_identity);
crypto::encryption_keygen(_encryption); crypto::encryption_keygen(_encryption);
router_contact.pubkey = seckey_topublic(identity()); router_contact._router_id = seckey_topublic(identity());
router_contact.enckey = seckey_topublic(encryption()); router_contact.enckey = seckey_topublic(encryption());
if (!router_contact.Sign(identity())) if (!router_contact.sign(identity()))
{ {
LogError("failed to regenerate keys and sign RC"); LogError("failed to regenerate keys and sign RC");
return false; return false;
} }
} } */
LogInfo("starting hidden service context..."); LogInfo("starting hidden service context...");
if (!hidden_service_context().StartAll()) if (!hidden_service_context().StartAll())
@ -1193,7 +1191,7 @@ namespace llarp
{ {
node_db()->put_rc(rc); node_db()->put_rc(rc);
_contacts->rc_nodes()->PutNode(rc); _contacts->rc_nodes()->PutNode(rc);
LogInfo("added bootstrap node ", RouterID{rc.pubkey}); LogInfo("added bootstrap node ", RouterID{rc.router_id()});
} }
LogInfo("have ", _node_db->num_loaded(), " routers"); LogInfo("have ", _node_db->num_loaded(), " routers");

@ -465,7 +465,7 @@ namespace llarp
const byte_t* const byte_t*
pubkey() const pubkey() const
{ {
return seckey_topublic(_identity); return seckey_to_pubkey(_identity);
} }
/// send to remote router or queue for sending /// send to remote router or queue for sending

@ -12,407 +12,276 @@
namespace llarp namespace llarp
{ {
static auto logcat = log::Cat("RC");
NetID& // RouterContact::RouterContact(std::string buf)
NetID::DefaultValue() // {
{ // try
static NetID defaultID(reinterpret_cast<const byte_t*>(llarp::DEFAULT_NETID)); // {
return defaultID; // oxenc::bt_list_consumer btlc{buf};
}
bool RouterContact::BlockBogons = true;
/// 1 day rc lifespan // // signature.from_string(btlc.consume_string());
constexpr auto rc_lifetime = 24h; // signed_bt_dict = btlc.consume_string();
/// an RC inserted long enough ago (4 hrs) is considered stale and is removed
constexpr auto rc_stale_age = 4h;
/// window of time in which a router wil try to update their RC before it is marked stale
constexpr auto rc_update_window = 5min;
/// update RCs shortly before they are about to expire
constexpr auto rc_update_interval = rc_stale_age - rc_update_window;
llarp_time_t RouterContact::Lifetime = rc_lifetime; // // TODO: parse bt dict
llarp_time_t RouterContact::StaleInsertionAge = rc_stale_age; // }
llarp_time_t RouterContact::UpdateInterval = rc_update_interval; // catch (...)
// {
// log::warning(llarp_cat, "Error: RouterContact failed to populate bt encoded contents!");
// }
// }
/// how many rc lifetime intervals should we wait until purging an rc std::string
constexpr auto expiration_lifetime_generations = 10; RouterContact::bt_encode() const
/// the max age of an rc before we want to expire it
constexpr auto rc_expire_age = rc_lifetime * expiration_lifetime_generations;
NetID::NetID(const byte_t* val)
{ {
const size_t len = strnlen(reinterpret_cast<const char*>(val), size()); oxenc::bt_dict_producer btdp;
std::copy(val, val + len, begin()); bt_encode(btdp);
return std::move(btdp).str();
} }
NetID::NetID() : NetID(DefaultValue().data()) void
{} RouterContact::bt_load(oxenc::bt_dict_consumer& data)
bool
NetID::operator==(const NetID& other) const
{ {
return ToString() == other.ToString(); if (int rc_ver = data.require<uint8_t>(""); rc_ver != RC_VERSION)
} throw std::runtime_error{"Invalid RC: do not know how to parse v{} RCs"_format(rc_ver)};
std::string auto ipv4_port = data.require<std::string_view>("4");
NetID::ToString() const
{
return {begin(), std::find(begin(), end(), '\0')};
}
bool if (ipv4_port.size() != 6)
NetID::BDecode(llarp_buffer_t* buf) throw std::runtime_error{
{ "Invalid RC address: expected 6-byte IPv4 IP/port, got {}"_format(ipv4_port.size())};
Zero();
llarp_buffer_t strbuf;
if (!bencode_read_string(buf, &strbuf))
return false;
if (strbuf.sz > size()) sockaddr_in s4;
return false; s4.sin_family = AF_INET;
std::copy(strbuf.base, strbuf.base + strbuf.sz, begin()); std::memcpy(&s4.sin_addr.s_addr, ipv4_port.data(), 4);
return true; std::memcpy(&s4.sin_port, ipv4_port.data() + 4, 2);
}
bool _addr = oxen::quic::Address{&s4};
NetID::BEncode(llarp_buffer_t* buf) const
{
auto term = std::find(begin(), end(), '\0');
return bencode_write_bytestring(buf, data(), std::distance(begin(), term));
}
RouterContact::RouterContact(std::string buf) if (!_addr.is_public())
{ throw std::runtime_error{"Invalid RC: IPv4 address is not a publicly routable IP"};
try
if (auto ipv6_port = data.maybe<std::string_view>("6"))
{ {
oxenc::bt_list_consumer btlc{buf}; if (ipv6_port->size() != 18)
throw std::runtime_error{
"Invalid RC address: expected 18-byte IPv6 IP/port, got {}"_format(ipv6_port->size())};
sockaddr_in6 s6{};
s6.sin6_family = AF_INET6;
signature.from_string(btlc.consume_string()); std::memcpy(&s6.sin6_addr.s6_addr, ipv6_port->data(), 16);
signed_bt_dict = btlc.consume_string(); std::memcpy(&s6.sin6_port, ipv6_port->data() + 16, 2);
// TODO: parse bt dict _addr6.emplace(&s6);
if (!_addr6->is_public())
throw std::runtime_error{"Invalid RC: IPv6 address is not a publicly routable IP"};
} }
catch (...) else
{ {
log::warning(llarp_cat, "Error: RouterContact failed to populate bt encoded contents!"); _addr6.reset();
} }
}
std::string auto netid = data.maybe<std::string_view>("i").value_or(llarp::LOKINET_DEFAULT_NETID);
RouterContact::bt_encode() const if (netid != ACTIVE_NETID)
{ throw std::runtime_error{
oxenc::bt_list_producer btlp; "Invalid RC netid: expected {}, got {}; this is an RC for a different network!"_format(
ACTIVE_NETID, netid)};
try auto pk = data.require<std::string_view>("p");
{
btlp.append(signature.ToView());
btlp.append(signed_bt_dict);
}
catch (...)
{
log::warning(llarp_cat, "Error: RouterContact failed to bt encode contents!");
}
return std::move(btlp).str(); if (pk.size() != RouterID::SIZE)
} throw std::runtime_error{"Invalid RC: router id has invalid size {}"_format(pk.size())};
void std::memcpy(_router_id.data(), pk.data(), RouterID::SIZE);
RouterContact::bt_encode_subdict(oxenc::bt_list_producer& btlp) const
{
btlp.append(signature.ToView());
btlp.append(signed_bt_dict);
}
std::string _timestamp = rc_time{std::chrono::seconds{data.require<int64_t>("t")}};
RouterContact::ToTXTRecord() const
{
std::string result;
auto out = std::back_inserter(result);
fmt::format_to(out, "addr={}; pk={}", addr.to_string(), pubkey);
fmt::format_to(out, "updated={}; onion_pk={}; ", last_updated.count(), enckey.ToHex());
if (routerVersion.has_value())
fmt::format_to(out, "router_version={}; ", *routerVersion);
return result;
}
bool auto ver = data.require<ustring_view>("v");
RouterContact::FromOurNetwork() const
{
return netID == NetID::DefaultValue();
}
std::string if (ver.size() != 3)
RouterContact::bencode_signed_section() const throw std::runtime_error{
{ "Invalid RC router version: received {} bytes (!= 3)"_format(ver.size())};
oxenc::bt_dict_producer btdp;
btdp.append("a", addr.to_string()); for (int i = 0; i < 3; i++)
btdp.append("i", netID.ToView()); _router_version[i] = ver[i];
btdp.append("k", pubkey.bt_encode()); }
btdp.append("p", enckey.ToView());
btdp.append("r", routerVersion->ToString());
if (not srvRecords.empty()) // std::string
{ // RouterContact::bencode_signed_section() const
auto sublist = btdp.append_list("s"); // {
// oxenc::bt_dict_producer btdp;
for (auto& s : srvRecords) // btdp.append("4", _addr.to_string());
sublist.append(s.bt_encode());
}
btdp.append("u", last_updated.count()); // if (_addr6)
// btdp.append("6", _addr6->to_string());
return std::move(btdp).str(); // if (ACTIVE_NETID != llarp::LOKINET_DEFAULT_NETID)
} // btdp.append("i", ACTIVE_NETID);
void // btdp.append("p", _router_id.bt_encode());
RouterContact::Clear() // btdp.append("t", _timestamp.time_since_epoch().count());
{ // btdp.append("v", _router_version.data());
signature.Zero();
enckey.Zero(); // return std::move(btdp).str();
pubkey.Zero(); // }
routerVersion = std::optional<RouterVersion>{};
last_updated = 0s;
srvRecords.clear();
version = llarp::constants::proto_version;
}
util::StatusObject util::StatusObject
RouterContact::ExtractStatus() const RouterContact::extract_status() const
{ {
util::StatusObject obj{ util::StatusObject obj{
{"lastUpdated", last_updated.count()}, {"lastUpdated", _timestamp.time_since_epoch().count()},
{"publicRouter", IsPublicRouter()}, {"publicRouter", is_public_router()},
{"identity", pubkey.ToString()}, {"identity", _router_id.ToString()},
{"address", addr.to_string()}}; {"address", _addr.to_string()}};
// if (routerVersion)
// {
// obj["routerVersion"] = routerVersion->ToString();
// }
// std::vector<util::StatusObject> srv;
// for (const auto& record : srvRecords)
// {
// srv.emplace_back(record.ExtractStatus());
// }
// obj["srvRecords"] = srv;
if (routerVersion)
{
obj["routerVersion"] = routerVersion->ToString();
}
std::vector<util::StatusObject> srv;
for (const auto& record : srvRecords)
{
srv.emplace_back(record.ExtractStatus());
}
obj["srvRecords"] = srv;
return obj; return obj;
} }
bool bool
RouterContact::BDecode(llarp_buffer_t* buf) RouterContact::BDecode(llarp_buffer_t* buf)
{ {
Clear(); // TODO: unfuck all of this
if (*buf->cur == 'd') // old format
{
return DecodeVersion_0(buf);
}
else if (*buf->cur != 'l') // if not dict, should be new format and start with list
{
return false;
}
try
{
std::string_view buf_view(reinterpret_cast<char*>(buf->cur), buf->size_left());
oxenc::bt_list_consumer btlist(buf_view);
uint64_t outer_version = btlist.consume_integer<uint64_t>();
if (outer_version == 1)
{
bool decode_result = DecodeVersion_1(btlist);
// advance the llarp_buffer_t since lokimq serialization is unaware of it.
// FIXME: this is broken (current_buffer got dropped), but the whole thing is getting
// replaced.
// buf->cur += btlist.
// current_buffer().data() - buf_view.data() + 1;
return decode_result;
}
else
{
log::warning(logcat, "Received RouterContact with unkown version ({})", outer_version);
return false;
}
}
catch (const std::exception& e)
{
log::debug(logcat, "RouterContact::BDecode failed: {}", e.what());
}
return false;
}
bool
RouterContact::DecodeVersion_0(llarp_buffer_t* buf)
{
return bencode_decode_dict(*this, buf);
}
bool (void)buf;
RouterContact::DecodeVersion_1(oxenc::bt_list_consumer& btlist)
{
auto signature_string = btlist.consume_string_view();
signed_bt_dict = btlist.consume_dict_data();
if (not btlist.is_finished()) // clear();
{
log::debug(logcat, "RouterContact serialized list too long for specified version.");
return false;
}
llarp_buffer_t sigbuf(signature_string.data(), signature_string.size()); // if (*buf->cur == 'd') // old format
if (not signature.FromBytestring(&sigbuf)) // {
{ // return DecodeVersion_0(buf);
log::debug(logcat, "RouterContact serialized signature had invalid length."); // }
return false; // else if (*buf->cur != 'l') // if not dict, should be new format and start with list
} // {
// return false;
// }
// try
// {
// std::string_view buf_view(reinterpret_cast<char*>(buf->cur), buf->size_left());
// oxenc::bt_list_consumer btlist(buf_view);
// uint64_t outer_version = btlist.consume_integer<uint64_t>();
// if (outer_version == 1)
// {
// bool decode_result = DecodeVersion_1(btlist);
// // advance the llarp_buffer_t since lokimq serialization is unaware of it.
// // FIXME: this is broken (current_buffer got dropped), but the whole thing is getting
// // replaced.
// // buf->cur += btlist.
// // current_buffer().data() - buf_view.data() + 1;
// return decode_result;
// }
// else
// {
// log::warning(logcat, "Received RouterContact with unkown version ({})", outer_version);
// return false;
// }
// }
// catch (const std::exception& e)
// {
// log::debug(logcat, "RouterContact::BDecode failed: {}", e.what());
// }
llarp_buffer_t data_dict_buf(signed_bt_dict.data(), signed_bt_dict.size()); return false;
return bencode_decode_dict(*this, &data_dict_buf);
} }
bool bool
RouterContact::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) RouterContact::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
{ {
bool read = false; bool read = false;
(void)key;
// TOFIX: fuck everything about llarp_buffer_t // TOFIX: fuck everything about llarp_buffer_t
// if (!BEncodeMaybeReadDictEntry("a", addr, read, key, buf)) // if (!BEncodeMaybeReadDictEntry("a", addr, read, key, buf))
// return false; // return false;
if (!BEncodeMaybeReadDictEntry("i", netID, read, key, buf)) // if (!BEncodeMaybeReadDictEntry("i", netID, read, key, buf))
return false; // return false;
if (!BEncodeMaybeReadDictEntry("k", pubkey, read, key, buf)) // if (!BEncodeMaybeReadDictEntry("k", _router_id, read, key, buf))
return false; // return false;
if (key.startswith("r")) // if (key.startswith("r"))
{ // {
RouterVersion r; // RouterVersion r;
if (not r.BDecode(buf)) // if (not r.BDecode(buf))
return false; // return false;
routerVersion = r; // routerVersion = r;
return true; // return true;
} // }
if (not BEncodeMaybeReadDictList("s", srvRecords, read, key, buf)) // if (not BEncodeMaybeReadDictList("s", srvRecords, read, key, buf))
return false; // return false;
if (!BEncodeMaybeReadDictEntry("p", enckey, read, key, buf)) // if (!BEncodeMaybeReadDictEntry("p", enckey, read, key, buf))
return false; // return false;
if (!BEncodeMaybeReadDictInt("u", last_updated, read, key, buf)) // if (!BEncodeMaybeReadDictInt("u", _timestamp, read, key, buf))
return false; // return false;
if (!BEncodeMaybeReadDictInt("v", version, read, key, buf)) // if (!BEncodeMaybeReadDictInt("v", version, read, key, buf))
return false; // return false;
if (key.startswith("x") and serializeExit) // if (key.startswith("x") and serializeExit)
{ // {
return bencode_discard(buf); // return bencode_discard(buf);
} // }
if (!BEncodeMaybeReadDictEntry("z", signature, read, key, buf)) // if (!BEncodeMaybeReadDictEntry("z", signature, read, key, buf))
return false; // return false;
return read or bencode_discard(buf); return read or bencode_discard(buf);
} }
bool bool
RouterContact::IsPublicRouter() const RouterContact::is_public_router() const
{ {
if (not routerVersion) if (_router_version.empty())
return false; return false;
return addr.is_addressable(); return _addr.is_addressable();
} }
bool bool
RouterContact::IsExpired(llarp_time_t now) const RouterContact::is_expired(llarp_time_t now) const
{ {
return Age(now) >= rc_expire_age; return age(now) >= _timestamp.time_since_epoch() + LIFETIME;
} }
llarp_time_t llarp_time_t
RouterContact::TimeUntilExpires(llarp_time_t now) const RouterContact::time_to_expiry(llarp_time_t now) const
{ {
const auto expiresAt = last_updated + Lifetime; const auto expiry = _timestamp.time_since_epoch() + LIFETIME;
return now < expiresAt ? expiresAt - now : 0s; return now < expiry ? expiry - now : 0s;
} }
llarp_time_t llarp_time_t
RouterContact::Age(llarp_time_t now) const RouterContact::age(llarp_time_t now) const
{
return now > last_updated ? now - last_updated : 0s;
}
bool
RouterContact::ExpiresSoon(llarp_time_t now, llarp_time_t dlt) const
{
return TimeUntilExpires(now) <= dlt;
}
bool
RouterContact::Sign(const SecretKey& secretkey)
{ {
pubkey = llarp::seckey_topublic(secretkey); auto delta = now - _timestamp.time_since_epoch();
signature.Zero(); return delta > 0s ? delta : 0s;
last_updated = time_now_ms();
signed_bt_dict = bencode_signed_section();
return crypto::sign(
signature,
secretkey,
reinterpret_cast<uint8_t*>(signed_bt_dict.data()),
signed_bt_dict.size());
}
bool
RouterContact::Verify(llarp_time_t now, bool allowExpired) const
{
if (netID != NetID::DefaultValue())
{
log::error(
logcat, "netid mismatch: '{}' (theirs) != '{}' (ours)", netID, NetID::DefaultValue());
return false;
}
if (IsExpired(now) and not allowExpired)
return false;
// TODO: make net* overridable
const auto* net = net::Platform::Default_ptr();
if (net->IsBogon(addr.in4()) && BlockBogons)
{
log::error(logcat, "invalid address info: {}", addr);
return false;
}
if (!VerifySignature())
{
log::error(logcat, "invalid signature: {}", *this);
return false;
}
return true;
} }
bool bool
RouterContact::VerifySignature() const RouterContact::expires_within_delta(llarp_time_t now, llarp_time_t dlt) const
{ {
RouterContact copy; return time_to_expiry(now) <= dlt;
copy = *this;
copy.signature.Zero();
auto bte = copy.bt_encode();
return crypto::verify(pubkey, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), signature);
} }
static constexpr std::array obsolete_bootstraps = { static constexpr std::array obsolete_bootstraps = {
@ -421,18 +290,18 @@ namespace llarp
}; };
bool bool
RouterContact::IsObsoleteBootstrap() const RouterContact::is_obsolete_bootstrap() const
{ {
for (const auto& k : obsolete_bootstraps) for (const auto& k : obsolete_bootstraps)
{ {
if (pubkey.ToHex() == k) if (_router_id.ToHex() == k)
return true; return true;
} }
return false; return false;
} }
bool bool
RouterContact::Write(const fs::path& fname) const RouterContact::write(const fs::path& fname) const
{ {
std::array<byte_t, MAX_RC_SIZE> tmp; std::array<byte_t, MAX_RC_SIZE> tmp;
llarp_buffer_t buf(tmp); llarp_buffer_t buf(tmp);
@ -453,13 +322,13 @@ namespace llarp
} }
bool bool
RouterContact::Read(const fs::path& fname) RouterContact::read(const fs::path& fname)
{ {
std::array<byte_t, MAX_RC_SIZE> tmp; std::array<byte_t, MAX_RC_SIZE> tmp;
llarp_buffer_t buf(tmp); llarp_buffer_t buf(tmp);
try try
{ {
util::slurp_file(fname, tmp.data(), tmp.size()); util::file_to_buffer(fname, tmp.data(), tmp.size());
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
@ -468,19 +337,4 @@ namespace llarp
} }
return BDecode(&buf); return BDecode(&buf);
} }
std::string
RouterContact::ToString() const
{
return fmt::format(
"[RC k={} updated={} netid={} v={} ai={{{}}} e={} z={}]",
pubkey,
last_updated.count(),
netID,
version,
fmt::format("{}", addr),
enckey,
signature);
}
} // namespace llarp } // namespace llarp

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "router_id.hpp"
#include "router_version.hpp" #include "router_version.hpp"
#include <llarp/constants/version.hpp> #include <llarp/constants/version.hpp>
@ -17,128 +18,169 @@
#include <functional> #include <functional>
#include <vector> #include <vector>
#define MAX_RC_SIZE (1024)
namespace oxenc namespace oxenc
{ {
class bt_list_consumer; class bt_list_consumer;
} // namespace oxenc } // namespace oxenc
/*
- figure out where we do bt_encoding of RC's
- maybe dump secret key from bt_encode
- it's weird to pass the secret key contextually
- suspicion we will need optional signature as an optional(?) string with serialized data
- resetting signature would be string::clear() instead
- ::sign() will cache serialized value
- do timestamp stuff
- bt_encode that takes bt_dict_producer requires reference to subdict
- presumably to be done in endpoints
- will be used for get_multi_rc endpoint
*/
namespace llarp namespace llarp
{ {
/// NetID static auto logcat = log::Cat("RC");
struct NetID final : public AlignedBuffer<8>
using rc_time = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
/// RouterContact
struct RouterContact
{ {
static NetID& static constexpr uint8_t RC_VERSION = 0;
DefaultValue();
NetID(); /// Constructs an empty RC
RouterContact() = default;
RouterContact(std::string)
{
log::error(logcat, "ERROR: SUPPLANT THIS CONSTRUCTOR");
}
explicit NetID(const byte_t* val); // RouterContact(std::string buf);
NetID(const NetID& other) = default; /// RC version that we support; we fail to load RCs that don't have the same version as that
NetID& /// means they are incompatible with us.
operator=(const NetID& other) = default; // static constexpr uint8_t VERSION = 0;
bool /// Unit tests disable this to allow private IP ranges in RCs, which normally get rejected.
operator==(const NetID& other) const; static inline bool BLOCK_BOGONS = true;
bool static inline std::string ACTIVE_NETID{LOKINET_DEFAULT_NETID};
operator!=(const NetID& other) const
{
return !(*this == other);
}
std::string static inline constexpr size_t MAX_RC_SIZE = 1024;
ToString() const;
bool /// Timespans for RCs:
BDecode(llarp_buffer_t* buf);
bool /// How long (relative to its timestamp) before an RC becomes stale. Stale records are used
BEncode(llarp_buffer_t* buf) const; /// (e.g. for path building) only if there are no non-stale records available, such as might be
}; /// the case when a client has been turned off for a while.
static constexpr auto STALE = 12h;
/// RouterContact /// How long before an RC becomes invalid (and thus deleted).
struct RouterContact static constexpr auto LIFETIME = 30 * 24h;
{
/// for unit tests /// How long before a relay updates and re-publish its RC to the network. (Relays can
static bool BlockBogons; /// re-publish more frequently than this if needed; this is meant to apply only if there are no
/// changes i.e. just to push out a new confirmation of the details).
static constexpr auto REPUBLISH = STALE / 2 - 5min;
static llarp_time_t Lifetime; /// Getters for private attributes
static llarp_time_t UpdateInterval; const oxen::quic::Address&
static llarp_time_t StaleInsertionAge; addr() const
{
return _addr;
}
RouterContact() const std::optional<oxen::quic::Address>&
addr6() const
{ {
Clear(); return _addr6;
} }
RouterContact(std::string buf); const RouterID&
router_id() const
{
return _router_id;
}
const rc_time&
timestamp() const
{
return _timestamp;
}
protected:
// advertised addresses // advertised addresses
oxen::quic::Address addr; oxen::quic::Address _addr; // refactor all 15 uses to use addr() method
// network identifier std::optional<oxen::quic::Address> _addr6; // optional ipv6
NetID netID;
// public encryption public key
llarp::PubKey enckey;
// public signing public key // public signing public key
llarp::PubKey pubkey; RouterID _router_id; // refactor all 103 uses to use router_id() method
// signature
llarp::Signature signature; rc_time _timestamp{};
llarp_time_t last_updated = 0s; // Lokinet version at the time the RC was produced
uint64_t version = llarp::constants::proto_version; std::array<uint8_t, 3> _router_version;
std::optional<RouterVersion> routerVersion;
public:
/// should we serialize the exit info? /// should we serialize the exit info?
const static bool serializeExit = true; const static bool serializeExit = true;
std::string signed_bt_dict; ustring _signed_payload;
std::vector<dns::SRVData> srvRecords;
util::StatusObject util::StatusObject
ExtractStatus() const; extract_status() const;
RouterID nlohmann::json
router_id() const to_json() const
{ {
return pubkey; return extract_status();
} }
nlohmann::json virtual std::string
ToJson() const to_string() const
{ {
return ExtractStatus(); return fmt::format(
"[RC k={} updated={} v={} addr={}]",
_router_id,
_timestamp,
RC_VERSION,
_addr.to_string());
} }
std::string /// On the wire we encode the data as a dict containing:
ToString() const; /// "" -- the RC format version, which must be == RouterContact::Version for us to attempt to
/// parse the reset of the fields. (Future versions might have backwards-compat support
/// for lower versions).
/// "4" -- 6 byte packed IPv4 address & port: 4 bytes of IPv4 address followed by 2 bytes of
/// port, both encoded in network (i.e. big-endian) order.
/// "6" -- optional 18 byte IPv6 address & port: 16 byte raw IPv6 address followed by 2 bytes
/// of port in network order.
/// "i" -- optional network ID string of up to 8 bytes; this is omitted for the default network
/// ID ("lokinet") but included for others (such as "gamma" for testnet).
/// "p" -- 32-byte router pubkey
/// "t" -- timestamp when this RC record was created (which also implicitly determines when it
/// goes stale and when it expires).
/// "v" -- lokinet version of the router; this is a three-byte packed value of
/// MAJOR, MINOR, PATCH, e.g. \x00\x0a\x03 for 0.10.3.
/// "~" -- signature of all of the previous serialized data, signed by "p"
std::string std::string
bt_encode() const; bt_encode() const;
void virtual void
bt_encode_subdict(oxenc::bt_list_producer& btlp) const; bt_encode(oxenc::bt_dict_producer& btdp) const
{
std::string (void)btdp;
bencode_signed_section() const; }
std::string
ToTXTRecord() const;
bool bool
operator==(const RouterContact& other) const operator==(const RouterContact& other) const
{ {
return addr == other.addr && enckey == other.enckey && pubkey == other.pubkey return _router_id == other._router_id and _addr == other._addr and _addr6 == other._addr6
&& signature == other.signature && last_updated == other.last_updated and _timestamp == other._timestamp and _router_version == other._router_version;
&& netID == other.netID;
} }
bool bool
operator<(const RouterContact& other) const operator<(const RouterContact& other) const
{ {
return pubkey < other.pubkey; return _router_id < other._router_id;
} }
bool bool
@ -147,14 +189,9 @@ namespace llarp
return !(*this == other); return !(*this == other);
} }
void virtual void
Clear(); clear()
{}
bool
IsExit() const
{
return false;
}
bool bool
BDecode(llarp_buffer_t* buf); BDecode(llarp_buffer_t* buf);
@ -163,64 +200,170 @@ namespace llarp
decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf); decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf);
bool bool
IsPublicRouter() const; is_public_router() const;
bool
Verify(llarp_time_t now, bool allowExpired = true) const;
bool
Sign(const llarp::SecretKey& secret);
/// does this RC expire soon? default delta is 1 minute /// does this RC expire soon? default delta is 1 minute
bool bool
ExpiresSoon(llarp_time_t now, llarp_time_t dlt = 1min) const; expires_within_delta(llarp_time_t now, llarp_time_t dlt = 1min) const;
/// returns true if this RC is expired and should be removed /// returns true if this RC is expired and should be removed
bool bool
IsExpired(llarp_time_t now) const; is_expired(llarp_time_t now) const;
/// returns time in ms until we expire or 0 if we have expired /// returns time in ms until we expire or 0 if we have expired
llarp_time_t llarp_time_t
TimeUntilExpires(llarp_time_t now) const; time_to_expiry(llarp_time_t now) const;
/// get the age of this RC in ms /// get the age of this RC in ms
llarp_time_t llarp_time_t
Age(llarp_time_t now) const; age(llarp_time_t now) const;
bool bool
OtherIsNewer(const RouterContact& other) const other_is_newer(const RouterContact& other) const
{ {
return last_updated < other.last_updated; return _timestamp < other._timestamp;
} }
bool bool
Read(const fs::path& fname); read(const fs::path& fname);
bool bool
Write(const fs::path& fname) const; write(const fs::path& fname) const;
bool bool
VerifySignature() const; is_obsolete_bootstrap() const;
/// return true if the netid in this rc is for the network id we are using void
bool bt_load(oxenc::bt_dict_consumer& data);
FromOurNetwork() const;
bool };
IsObsoleteBootstrap() const;
/// Extension of RouterContact used to store a local "RC," and inserts a RouterContact by
/// re-parsing and sending it out. This sub-class contains a pubkey and all the other attributes
/// required for signing and serialization
///
/// Note: this class may be entirely superfluous, so it is used here as a placeholder until its
/// marginal utility is determined. It may end up as a free-floating method that reads in
/// parameters and outputs a bt-serialized string
struct LocalRC : public RouterContact
{
private: private:
bool ustring _signature;
DecodeVersion_0(llarp_buffer_t* buf);
const SecretKey _secret_key;
void
bt_sign(oxenc::bt_dict_consumer& btdc);
void
resign();
public:
LocalRC(std::string payload, const SecretKey sk);
void
bt_encode(oxenc::bt_dict_producer& btdp) const override;
void
clear() override
{
_addr = {};
_addr6.reset();
_router_id.Zero();
_timestamp = {};
_router_version.fill(0);
_signature.clear();
}
bool bool
DecodeVersion_1(oxenc::bt_list_consumer& btlist); operator==(const LocalRC& other) const
{
return _router_id == other._router_id and _addr == other._addr and _addr6 == other._addr6
and _timestamp == other._timestamp and _router_version == other._router_version
and _signature == other._signature;
}
/// Mutators for the private member attributes. Calling on the mutators
/// will clear the current signature and re-sign the RC
void
set_addr(oxen::quic::Address new_addr)
{
resign();
_addr = std::move(new_addr);
}
void
set_addr6(oxen::quic::Address new_addr)
{
resign();
_addr6 = std::move(new_addr);
}
void
set_router_id(RouterID rid)
{
resign();
_router_id = std::move(rid);
}
void
set_timestamp(llarp_time_t ts)
{
set_timestamp(rc_time{std::chrono::duration_cast<std::chrono::seconds>(ts)});
}
void
set_timestamp(rc_time ts)
{
_timestamp = ts;
}
/// Sets RC timestamp to current system clock time
void
set_systime_timestamp()
{
set_timestamp(
std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()));
}
};
/// Extension of RouterContact used in a "read-only" fashion. Parses the incoming RC to query
/// the data in the constructor, eliminating the need for a ::verify method/
struct RemoteRC : public RouterContact
{
private:
//
void
bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired = false);
public:
RemoteRC(std::string payload);
// TODO: this method could use oxenc's append_encoded
void
bt_encode(oxenc::bt_dict_producer& btdp) const override;
void
clear() override
{
_addr = {};
_addr6.reset();
_router_id.Zero();
_timestamp = {};
_router_version.fill(0);
}
}; };
template <>
constexpr inline bool IsToStringFormattable<NetID> = true;
template <> template <>
constexpr inline bool IsToStringFormattable<RouterContact> = true; constexpr inline bool IsToStringFormattable<RouterContact> = true;
template <>
constexpr inline bool IsToStringFormattable<RemoteRC> = true;
template <>
constexpr inline bool IsToStringFormattable<LocalRC> = true;
using RouterLookupHandler = std::function<void(const std::vector<RouterContact>&)>; using RouterLookupHandler = std::function<void(const std::vector<RouterContact>&)>;
} // namespace llarp } // namespace llarp
@ -233,7 +376,7 @@ namespace std
size_t size_t
operator()(const llarp::RouterContact& r) const operator()(const llarp::RouterContact& r) const
{ {
return std::hash<llarp::PubKey>{}(r.pubkey); return std::hash<llarp::PubKey>{}(r.router_id());
} }
}; };
} // namespace std } // namespace std

@ -0,0 +1,108 @@
#include "constants/version.hpp"
#include "crypto/crypto.hpp"
#include "net/net.hpp"
#include "router_contact.hpp"
#include "util/bencode.hpp"
#include "util/buffer.hpp"
#include "util/file.hpp"
#include "util/time.hpp"
#include <oxenc/bt_serialize.h>
namespace llarp
{
LocalRC::LocalRC(std::string payload, const SecretKey sk) :
_secret_key{std::move(sk)}
{
_router_id = llarp::seckey_to_pubkey(_secret_key);
try
{
oxenc::bt_dict_consumer btdc{payload};
bt_load(btdc);
bt_sign(btdc);
}
catch (const std::exception& e)
{
log::warning(logcat, "Failed to parse LocalRC: {}", e.what());
throw;
}
}
void
LocalRC::bt_sign(oxenc::bt_dict_consumer& btdc)
{
_signature.clear();
btdc.require_signature("~", [&](ustring_view msg, ustring_view s) {
if (!crypto::sign(const_cast<unsigned char*>(s.data()), _secret_key, msg))
throw std::runtime_error{"Failed to sign RC"};
_signature = s;
_signed_payload = msg;
});
}
void
LocalRC::bt_encode(oxenc::bt_dict_producer& btdp) const
{
btdp.append("", RC_VERSION);
std::array<unsigned char, 18> buf;
{
if (not _addr.is_ipv4())
throw std::runtime_error{"Unable to encode RC: addr is not IPv4"};
auto in4 = _addr.in4();
std::memcpy(buf.data(), &in4.sin_addr.s_addr, 4);
std::memcpy(buf.data() + 4, &in4.sin_port, 2);
btdp.append("4", ustring_view{buf.data(), 6});
}
if (_addr6)
{
if (not _addr.is_ipv6())
throw std::runtime_error{"Unable to encode RC: addr6 is set but is not IPv6"};
auto in6 = _addr.in6();
std::memcpy(buf.data(), &in6.sin6_addr.s6_addr, 16);
std::memcpy(buf.data() + 16, &in6.sin6_port, 2);
btdp.append("6", ustring_view{buf.data(), 18});
}
if (ACTIVE_NETID != llarp::LOKINET_DEFAULT_NETID)
btdp.append("i", ACTIVE_NETID);
btdp.append("p", _router_id.ToView());
btdp.append("t", _timestamp.time_since_epoch().count());
static_assert(llarp::LOKINET_VERSION.size() == 3);
btdp.append(
"v", std::string_view{reinterpret_cast<const char*>(llarp::LOKINET_VERSION.data()), 3});
btdp.append_signature("~", [&](ustring_view to_sign) {
std::array<unsigned char, 64> sig;
if (!crypto::sign(sig.data(), _secret_key, to_sign))
throw std::runtime_error{"Failed to sign LocalRC"};
return sig;
});
}
void
LocalRC::resign()
{
oxenc::bt_dict_consumer btdc{_signed_payload};
bt_sign(btdc);
// DISCUSS: should we also update the timestamp when we re-sign?
// -> Is the timestamp referring to signing time or time the RC was originally created?
}
} // namespace llarp

@ -0,0 +1,69 @@
#include "constants/version.hpp"
#include "crypto/crypto.hpp"
#include "net/net.hpp"
#include "router_contact.hpp"
#include "util/bencode.hpp"
#include "util/buffer.hpp"
#include "util/file.hpp"
#include "util/time.hpp"
#include <oxenc/bt_serialize.h>
namespace llarp
{
RemoteRC::RemoteRC(std::string payload)
{
try
{
oxenc::bt_dict_consumer btdc{payload};
bt_load(btdc);
bt_verify(btdc);
}
catch (const std::exception& e)
{
log::warning(logcat, "Failed to parse RemoteRC: {}", e.what());
throw;
}
}
void
RemoteRC::bt_encode(oxenc::bt_dict_producer& btdp) const
{
(void)btdp;
// TODO: implement append_encoded in oxenc so we can finish this implementation.
// It is almost identical to the implementation of LocalRC::bt_encode, except the
// call to append_signature is append_encoded.
//
// When that is done, we can take the common logic and move it into the base class
// ::bt_encode, and then have each derived class call into a different virtual method
// that calls append_signature in the LocalRC and append_encoded in the RemoteRC
}
void
RemoteRC::bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired)
{
data.require_signature("~", [this, reject_expired](ustring_view msg, ustring_view sig) {
if (sig.size() != 64)
throw std::runtime_error{"Invalid signature: not 64 bytes"};
if (is_expired(time_now_ms()) and reject_expired)
throw std::runtime_error{"Unable to verify expired RemoteRC!"};
// TODO: revisit if this is needed; detail from previous implementation
const auto* net = net::Platform::Default_ptr();
if (net->IsBogon(addr().in4()) and BLOCK_BOGONS)
{
auto err = "Unable to verify expired RemoteRC!";
log::info(logcat, err);
throw std::runtime_error{err};
}
if (not crypto::verify(router_id(), msg, sig))
throw std::runtime_error{"Failed to verify RemoteRC"};
_signed_payload = msg;
});
}
} // namespace llarp

@ -3,9 +3,11 @@
#include "util/aligned.hpp" #include "util/aligned.hpp"
#include "util/status.hpp" #include "util/status.hpp"
#include <llarp/crypto/types.hpp>
namespace llarp namespace llarp
{ {
struct RouterID : public AlignedBuffer<32> struct RouterID : public PubKey
{ {
static constexpr size_t SIZE = 32; static constexpr size_t SIZE = 32;
@ -13,10 +15,10 @@ namespace llarp
RouterID() = default; RouterID() = default;
RouterID(const byte_t* buf) : AlignedBuffer<SIZE>(buf) RouterID(const byte_t* buf) : PubKey(buf)
{} {}
RouterID(const Data& data) : AlignedBuffer<SIZE>(data) RouterID(const Data& data) : PubKey(data)
{} {}
util::StatusObject util::StatusObject

@ -181,7 +181,7 @@ namespace llarp::rpc
nlohmann::json payload = { nlohmann::json payload = {
{"pubkey_ed25519", oxenc::to_hex(pk.begin(), pk.end())}, {"pubkey_ed25519", oxenc::to_hex(pk.begin(), pk.end())},
{"version", {VERSION[0], VERSION[1], VERSION[2]}}}; {"version", {LOKINET_VERSION[0], LOKINET_VERSION[1], LOKINET_VERSION[2]}}};
if (auto err = r->OxendErrorState()) if (auto err = r->OxendErrorState())
payload["error"] = *err; payload["error"] = *err;

@ -154,7 +154,7 @@ namespace llarp::rpc
RPCServer::invoke(Version& version) RPCServer::invoke(Version& version)
{ {
util::StatusObject result{ util::StatusObject result{
{"version", llarp::VERSION_FULL}, {"uptime", to_json(m_Router.Uptime())}}; {"version", llarp::LOKINET_VERSION_FULL}, {"uptime", to_json(m_Router.Uptime())}};
SetJSONResponse(result, version.response); SetJSONResponse(result, version.response);
} }

@ -223,10 +223,12 @@ namespace llarp::service
// handles when we resolved a .snode // handles when we resolved a .snode
auto handleResolvedSNodeName = [resultHandler, nodedb = router()->node_db()](auto router_id) { auto handleResolvedSNodeName = [resultHandler, nodedb = router()->node_db()](auto router_id) {
std::vector<dns::SRVData> result{}; std::vector<dns::SRVData> result{};
if (auto maybe_rc = nodedb->get_rc(router_id)) if (auto maybe_rc = nodedb->get_rc(router_id))
{ {
result = maybe_rc->srvRecords; result = maybe_rc->srvRecords; // TODO: RouterContact has no SRV records
} }
resultHandler(std::move(result)); resultHandler(std::move(result));
}; };
@ -766,7 +768,7 @@ namespace llarp::service
}); });
if (not maybe.has_value()) if (not maybe.has_value())
return std::nullopt; return std::nullopt;
return GetHopsForBuildWithEndpoint(maybe->pubkey); return GetHopsForBuildWithEndpoint(maybe->router_id());
} }
std::optional<std::vector<RouterContact>> std::optional<std::vector<RouterContact>>

@ -44,7 +44,7 @@ namespace llarp::service
{ {
crypto::identity_keygen(signkey); crypto::identity_keygen(signkey);
crypto::encryption_keygen(enckey); crypto::encryption_keygen(enckey);
pub.Update(seckey_topublic(signkey), seckey_topublic(enckey)); pub.Update(seckey_to_pubkey(signkey), seckey_to_pubkey(enckey));
crypto::pqe_keygen(pq); crypto::pqe_keygen(pq);
if (not crypto::derive_subkey_private(derivedSignKey, signkey, 1)) if (not crypto::derive_subkey_private(derivedSignKey, signkey, 1))
{ {
@ -114,7 +114,7 @@ namespace llarp::service
// read file // read file
try try
{ {
util::slurp_file(fname, tmp.data(), tmp.size()); util::file_to_buffer(fname, tmp.data(), tmp.size());
} }
catch (const std::length_error&) catch (const std::length_error&)
{ {
@ -139,7 +139,7 @@ namespace llarp::service
if (!vanity.IsZero()) if (!vanity.IsZero())
van = vanity; van = vanity;
// update pubkeys // update pubkeys
pub.Update(seckey_topublic(signkey), seckey_topublic(enckey), van); pub.Update(seckey_to_pubkey(signkey), seckey_to_pubkey(enckey), van);
if (not crypto::derive_subkey_private(derivedSignKey, signkey, 1)) if (not crypto::derive_subkey_private(derivedSignKey, signkey, 1))
{ {
throw std::runtime_error("failed to derive subkey"); throw std::runtime_error("failed to derive subkey");
@ -163,7 +163,7 @@ namespace llarp::service
// set service info // set service info
i.address_keys = pub; i.address_keys = pub;
// set public encryption key // set public encryption key
i.sntru_pubkey = pq_keypair_to_public(pq); i.sntru_pubkey = pq_keypair_to_pubkey(pq);
auto bte = i.bt_encode(); auto bte = i.bt_encode();

@ -256,7 +256,7 @@ namespace llarp::service
// copy // copy
ProtocolFrameMessage frame(self->frame); ProtocolFrameMessage frame(self->frame);
if (!crypto::pqe_decrypt( if (!crypto::pqe_decrypt(
self->frame.cipher, K, pq_keypair_to_secret(self->m_LocalIdentity.pq))) self->frame.cipher, K, pq_keypair_to_seckey(self->m_LocalIdentity.pq)))
{ {
LogError("pqke failed C=", self->frame.cipher); LogError("pqke failed C=", self->frame.cipher);
self->msg.reset(); self->msg.reset();

@ -365,7 +365,7 @@ namespace llarp
std::string content; std::string content;
try try
{ {
content = util::slurp_file(fpath); content = util::file_to_string(fpath);
} }
catch (const std::exception&) catch (const std::exception&)
{ {

@ -19,7 +19,7 @@
namespace llarp::util namespace llarp::util
{ {
static std::streampos static std::streampos
slurp_file_open(const fs::path& filename, fs::ifstream& in) file_reader_impl(const fs::path& filename, fs::ifstream& in)
{ {
in.exceptions(std::ifstream::failbit | std::ifstream::badbit); in.exceptions(std::ifstream::failbit | std::ifstream::badbit);
in.open(filename, std::ios::binary | std::ios::in); in.open(filename, std::ios::binary | std::ios::in);
@ -30,21 +30,21 @@ namespace llarp::util
} }
std::string std::string
slurp_file(const fs::path& filename) file_to_string(const fs::path& filename)
{ {
fs::ifstream in; fs::ifstream in;
std::string contents; std::string contents;
auto size = slurp_file_open(filename, in); auto size = file_reader_impl(filename, in);
contents.resize(size); contents.resize(size);
in.read(contents.data(), size); in.read(contents.data(), size);
return contents; return contents;
} }
size_t size_t
slurp_file(const fs::path& filename, char* buffer, size_t buffer_size) file_to_buffer(const fs::path& filename, char* buffer, size_t buffer_size)
{ {
fs::ifstream in; fs::ifstream in;
auto size = slurp_file_open(filename, in); auto size = file_reader_impl(filename, in);
if (static_cast<size_t>(size) > buffer_size) if (static_cast<size_t>(size) > buffer_size)
throw std::length_error{"file is too large for buffer"}; throw std::length_error{"file is too large for buffer"};
in.read(buffer, size); in.read(buffer, size);

@ -14,21 +14,21 @@ namespace llarp::util
{ {
/// Reads a binary file from disk into a string. Throws on error. /// Reads a binary file from disk into a string. Throws on error.
std::string std::string
slurp_file(const fs::path& filename); file_to_string(const fs::path& filename);
/// Reads a binary file from disk directly into a buffer. Throws a std::length_error if the /// Reads a binary file from disk directly into a buffer. Throws a std::length_error if the
/// file is bigger than the buffer. Returns the bytes copied on success. /// file is bigger than the buffer. Returns the bytes copied on success.
size_t size_t
slurp_file(const fs::path& filename, char* buffer, size_t buffer_size); file_to_buffer(const fs::path& filename, char* buffer, size_t buffer_size);
/// Same, but for some non-char but single-byte char type (e.g. byte_t, std::byte, unsigned char). /// Same, but for some non-char but single-byte char type (e.g. byte_t, std::byte, unsigned char).
template < template <
typename Char, typename Char,
std::enable_if_t<sizeof(Char) == 1 and not std::is_same_v<Char, char>, int> = 1> std::enable_if_t<sizeof(Char) == 1 and not std::is_same_v<Char, char>, int> = 1>
inline size_t inline size_t
slurp_file(const fs::path& filename, Char* buffer, size_t buffer_size) file_to_buffer(const fs::path& filename, Char* buffer, size_t buffer_size)
{ {
return slurp_file(filename, reinterpret_cast<char*>(buffer), buffer_size); return file_to_buffer(filename, reinterpret_cast<char*>(buffer), buffer_size);
} }
/// Dumps binary string contents to disk. The file is overwritten if it already exists. Throws /// Dumps binary string contents to disk. The file is overwritten if it already exists. Throws

Loading…
Cancel
Save