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)
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")

@ -86,7 +86,7 @@ main(int argc, char* argv[])
#else
cpr::Get(
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()}));
#endif
if (resp.status_code != 200)

@ -403,7 +403,7 @@ namespace
{
if (options.version)
{
std::cout << llarp::VERSION_FULL << std::endl;
std::cout << llarp::LOKINET_VERSION_FULL << std::endl;
return 0;
}
@ -548,7 +548,8 @@ namespace
static void
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
{
std::shared_ptr<llarp::Config> conf;

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

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

@ -58,9 +58,10 @@ namespace llarp
conf.defineOption<std::string>(
"router",
"netid",
Default{llarp::DEFAULT_NETID},
Default{llarp::LOKINET_DEFAULT_NETID},
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) {
if (arg.size() > NetID::size())
@ -1329,7 +1330,7 @@ namespace llarp
std::set<IPRange> seenRanges;
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)
{
return false;
@ -1444,7 +1445,7 @@ namespace llarp
{
try
{
ini = util::slurp_file(*fname);
ini = util::file_to_string(*fname);
}
catch (const std::exception&)
{

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

@ -52,7 +52,7 @@ namespace llarp
m_transportKeyPath = deriveFile(our_transport_key_filename, config.router.m_transportKeyFile);
RouterContact rc;
bool exists = rc.Read(m_rcPath);
bool exists = rc.read(m_rcPath);
if (not exists and not genIfAbsent)
{
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
// 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
// older encryption) and needs to be regenerated. before doing so, backup

@ -4,12 +4,11 @@
namespace llarp
{
// clang-format off
const std::array<uint16_t, 3> 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 VERSION_TAG = "@VERSIONTAG@";
const char* const VERSION_FULL = "lokinet-@lokinet_VERSION_MAJOR@.@lokinet_VERSION_MINOR@.@lokinet_VERSION_PATCH@-@VERSIONTAG@";
const std::array<uint16_t, 3> LOKINET_VERSION{{@lokinet_VERSION_MAJOR@, @lokinet_VERSION_MINOR@, @lokinet_VERSION_PATCH@}};
const char* const LOKINET_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 RELEASE_MOTTO = "@RELEASE_MOTTO@";
const char* const DEFAULT_NETID = "lokinet";
const char* const LOKINET_RELEASE_MOTTO = "@RELEASE_MOTTO@";
const char* const LOKINET_DEFAULT_NETID = "lokinet";
// clang-format on
} // namespace llarp

@ -6,11 +6,10 @@
namespace llarp
{
// 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<uint64_t, 4> ROUTER_VERSION; // [proto, 1, 2, 3]
extern const char* const VERSION_TAG; // "abc"
extern const char* const VERSION_FULL; // "lokinet-1.2.3-abc"
extern const std::array<uint16_t, 3> LOKINET_VERSION;
extern const char* const LOKINET_VERSION_TAG;
extern const char* const LOKINET_VERSION_FULL;
extern const char* const RELEASE_MOTTO;
extern const char* const DEFAULT_NETID;
extern const char* const LOKINET_RELEASE_MOTTO;
extern const char* const LOKINET_DEFAULT_NETID;
} // namespace llarp

@ -59,7 +59,8 @@ namespace llarp
throw std::runtime_error("Cannot call Setup() on context without a Config");
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)
{

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

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

@ -1,5 +1,6 @@
#include "types.hpp"
#include <llarp/router_id.hpp>
#include <llarp/util/buffer.hpp>
#include <llarp/util/file.hpp>
@ -32,6 +33,36 @@ namespace llarp
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
SecretKey::LoadFromFile(const fs::path& fname)
{
@ -39,7 +70,7 @@ namespace llarp
std::array<byte_t, 128> tmp;
try
{
sz = util::slurp_file(fname, tmp.data(), tmp.size());
sz = util::file_to_buffer(fname, tmp.data(), tmp.size());
}
catch (const std::exception&)
{
@ -107,53 +138,7 @@ namespace llarp
{
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;
}
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

@ -2,7 +2,6 @@
#include "constants.hpp"
#include <llarp/router_id.hpp>
#include <llarp/util/aligned.hpp>
#include <llarp/util/fs.hpp>
#include <llarp/util/types.hpp>
@ -15,7 +14,9 @@ namespace llarp
using SharedSecret = AlignedBuffer<SHAREDKEYSIZE>;
using KeyExchangeNonce = AlignedBuffer<32>;
struct PubKey final : public AlignedBuffer<PUBKEYSIZE>
struct RouterID;
struct PubKey : public AlignedBuffer<PUBKEYSIZE>
{
PubKey() = default;
@ -37,36 +38,20 @@ namespace llarp
static PubKey
from_string(const std::string& s);
operator RouterID() const
{
return {as_array()};
}
operator RouterID() const;
PubKey&
operator=(const byte_t* ptr)
{
std::copy(ptr, ptr + SIZE, begin());
return *this;
}
operator=(const byte_t* ptr);
};
inline bool
operator==(const PubKey& lhs, const PubKey& rhs)
{
return lhs.as_array() == rhs.as_array();
}
bool
operator==(const PubKey& lhs, const PubKey& rhs);
inline bool
operator==(const PubKey& lhs, const RouterID& rhs)
{
return lhs.as_array() == rhs.as_array();
}
bool
operator==(const PubKey& lhs, const RouterID& rhs);
inline bool
operator==(const RouterID& lhs, const PubKey& rhs)
{
return lhs.as_array() == rhs.as_array();
}
bool
operator==(const RouterID& lhs, const PubKey& rhs);
struct PrivateKey;
@ -161,58 +146,24 @@ namespace llarp
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 <>
constexpr inline bool IsToStringFormattable<PubKey> = true;
template <>
constexpr inline bool IsToStringFormattable<SecretKey> = true;
template <>
constexpr inline bool IsToStringFormattable<PrivateKey> = true;
template <>
constexpr inline bool IsToStringFormattable<IdentitySecret> = true;
using ShortHash = AlignedBuffer<SHORTHASHSIZE>;
using LongHash = AlignedBuffer<HASHSIZE>;
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 SymmNonce = AlignedBuffer<NONCESIZE>;
using SymmKey = AlignedBuffer<32>;
using SymmKey = AlignedBuffer<32>; // not used
using PQCipherBlock = AlignedBuffer<PQ_CIPHERTEXTSIZE + 1>;
using PQPubKey = AlignedBuffer<PQ_PUBKEYSIZE>;

@ -22,7 +22,7 @@ namespace llarp::dht
bool
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

@ -19,19 +19,19 @@ namespace llarp::dht
ID.Zero();
}
RCNode(const RouterContact& other) : rc(other), ID(other.pubkey)
RCNode(const RouterContact& other) : rc(other), ID(other.router_id())
{}
util::StatusObject
ExtractStatus() const
{
return rc.ExtractStatus();
return rc.extract_status();
}
bool
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(
[srvRecords = SRVRecords()](RouterContact rc) -> std::optional<RouterContact> {
// TODO: update this RouterContact handling
// check if there are any new srv records
bool shouldUpdate = false;
// bool shouldUpdate = false;
for (const auto& rcSrv : rc.srvRecords)
{
if (srvRecords.count(rcSrv) == 0)
shouldUpdate = true;
}
// for (const auto& rcSrv : rc.srvRecords)
// {
// if (srvRecords.count(rcSrv) == 0)
// 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
if (not shouldUpdate)
return std::nullopt;
// for (auto& record : srvRecords)
// rc.srvRecords.emplace_back(record);
// we got new entries so we clear the whole vector on the rc and recreate it
rc.srvRecords.clear();
for (auto& record : srvRecords)
rc.srvRecords.emplace_back(record);
// set the verssion to 1 because we have srv records
rc.version = 1;
// // set the verssion to 1 because we have srv records
// rc.version = 1;
return rc;
});
}

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

@ -20,7 +20,7 @@ namespace llarp
std::shared_ptr<link::Connection>
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 nullptr;
@ -303,14 +303,14 @@ namespace llarp
void
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
// would be executed for another failure case
return;
}
auto& remote_addr = rc.addr;
const auto& remote_addr = rc.addr();
// TODO: confirm remote end is using the expected pubkey (RouterID).
// 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))
{
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;
connect_to(*maybe_other);
@ -609,23 +609,25 @@ namespace llarp
target_rid.FromString(target_key);
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};
if (is_exploratory)
{
std::string neighbors{};
const auto closest_rcs =
_router.node_db()->find_many_closest_to(target_addr, RC_LOOKUP_STORAGE_REDUNDANCY);
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
|| local_rid == rid)
continue;
neighbors += rid.bt_encode();
neighbors +=
rid.bt_encode(); // TODO: refactor to use reference to bt_dict_producer subdict
}
m.respond(
@ -635,12 +637,12 @@ namespace llarp
else
{
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};
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(
target_rid,
@ -837,7 +839,7 @@ namespace llarp
const auto now = _router.now();
const auto addr = dht::Key_t{reinterpret_cast<uint8_t*>(derived_signing_key.data())};
const auto local_key = _router.rc().pubkey;
const auto local_key = _router.rc().router_id();
if (not service::EncryptedIntroSet::verify(introset, derived_signing_key, sig))
{
@ -878,7 +880,7 @@ namespace llarp
log::info(link_cat, "Relaying PublishIntroMessage for {}", addr);
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)
{
@ -908,7 +910,7 @@ namespace llarp
for (const auto& rc : closest_rcs)
{
if (rc.pubkey == local_key)
if (rc.router_id() == local_key)
{
rc_index = index;
break;
@ -1024,7 +1026,7 @@ namespace llarp
log::info(link_cat, "Relaying FindIntroMessage for {}", addr);
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(
peer_key,

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

@ -22,7 +22,7 @@ namespace llarp
hop.nonce.Randomize();
// 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!");
log::warning(path_cat, err);
@ -60,7 +60,7 @@ namespace llarp
outer_nonce.Randomize();
// 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!");
throw std::runtime_error{"DH failed during hop info encryption"};

@ -54,7 +54,7 @@ namespace llarp
bool
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)
{
return path->HandleUpstream(llarp_buffer_t(enc), nonce, r);
@ -110,7 +110,7 @@ namespace llarp
bool
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)
{
return path->HandleDownstream(llarp_buffer_t(enc), nonce, r);

@ -14,8 +14,6 @@ static const std::string RC_FILE_EXT = ".signed";
namespace llarp
{
static auto logcat = log::Cat("nodedb");
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...
disk([this, data = std::move(copy)]() {
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{};
if (not rc.Read(f))
if (not rc.read(f))
{
// try loading it, purge it if it is junk
purge.emplace(f);
return true;
}
if (not rc.FromOurNetwork())
{
// skip entries that are not from our network
return true;
}
if (rc.IsExpired(time_now_ms()))
if (rc.is_expired(time_now_ms()))
{
// rc expired dont load it and purge it later
purge.emplace(f);
@ -145,8 +137,8 @@ namespace llarp
// validate signature and purge entries with invalid signatures
// load ones with valid signatures
if (rc.VerifySignature())
entries.emplace(rc.pubkey, rc);
if (rc.verify_signature()) // TODO: fix this after RouterContact -> RemoteRC
entries.emplace(rc.router_id(), rc);
else
purge.emplace(f);
@ -172,7 +164,7 @@ namespace llarp
router.loop()->call([this]() {
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();
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);
}
else
@ -230,8 +222,8 @@ namespace llarp
NodeDB::put_rc(RouterContact rc)
{
router.loop()->call([this, rc]() {
entries.erase(rc.pubkey);
entries.emplace(rc.pubkey, rc);
entries.erase(rc.router_id());
entries.emplace(rc.router_id(), rc);
});
}
@ -245,14 +237,14 @@ namespace llarp
NodeDB::put_rc_if_newer(RouterContact rc)
{
router.loop()->call([this, rc]() {
auto itr = entries.find(rc.pubkey);
if (itr == entries.end() or itr->second.rc.OtherIsNewer(rc))
auto itr = entries.find(rc.router_id());
if (itr == entries.end() or itr->second.rc.other_is_newer(rc))
{
// delete if existing
if (itr != entries.end())
entries.erase(itr);
// add new entry
entries.emplace(rc.pubkey, rc);
entries.emplace(rc.router_id(), rc);
}
});
}
@ -282,14 +274,14 @@ namespace llarp
llarp::RouterContact rc;
const llarp::dht::XorMetric compare(location);
VisitAll([&rc, compare](const auto& otherRC) {
if (rc.pubkey.IsZero())
if (rc.router_id().IsZero())
{
rc = otherRC;
return;
}
if (compare(
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;
});
return rc;

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

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

@ -83,7 +83,7 @@ namespace llarp
hop.nonce.Randomize();
// 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());
log::error(path_cat, err);
@ -121,7 +121,7 @@ namespace llarp
outer_nonce.Randomize();
// 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!");
throw std::runtime_error{"DH failed during hop info encryption"};
@ -218,16 +218,16 @@ namespace llarp
router->for_each_connection([&](link::Connection& conn) {
const auto& rc = conn.remote_rc;
#ifndef TESTNET
if (router->IsBootstrapNode(rc.pubkey))
if (router->IsBootstrapNode(rc._pubkey))
return;
#endif
if (exclude.count(rc.pubkey))
if (exclude.count(rc._pubkey))
return;
if (BuildCooldownHit(rc.pubkey))
if (BuildCooldownHit(rc._pubkey))
return;
if (router->router_profiling().IsBadForPath(rc.pubkey))
if (router->router_profiling().IsBadForPath(rc._pubkey))
return;
found = rc;
@ -243,7 +243,7 @@ namespace llarp
};
if (const auto maybe = router->node_db()->GetRandom(filter))
{
return GetHopsAlignedToForBuild(maybe->pubkey);
return GetHopsAlignedToForBuild(maybe->_pubkey);
}
return std::nullopt;
}
@ -353,7 +353,7 @@ namespace llarp
return false;
for (const auto& hop : hopsSet)
{
if (hop.pubkey == rc.pubkey)
if (hop._pubkey == rc.pubkey)
return false;
}
@ -362,7 +362,7 @@ namespace llarp
if (not pathConfig.Acceptable(hopsSet))
return false;
#endif
return rc.pubkey != endpointRC.pubkey;
return rc.pubkey != endpointRC._pubkey;
};
if (const auto maybe = router->node_db()->GetRandom(filter))
@ -402,7 +402,7 @@ namespace llarp
}
lastBuild = llarp::time_now_ms();
const RouterID edge{hops[0].pubkey};
const RouterID edge{hops[0]._pubkey};
if (not router->pathbuild_limiter().Attempt(edge))
{
@ -429,7 +429,7 @@ namespace llarp
{
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);
auto frame_str = PathBuildMessage::serialize(path_hops[i]);
@ -533,7 +533,7 @@ namespace llarp
DoPathBuildBackoff();
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
log::info(path_cat, "Looking up RouterID {} due to path build timeout", target);

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

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

@ -101,6 +101,7 @@ namespace llarp
throw;
}
// TODO: replace this with construction of RemoteRC
RouterContact result{std::move(payload)};
if (callback)
@ -204,22 +205,22 @@ namespace llarp
bool
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;
}
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;
}
// 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);
contacts->put_rc_node_async(rc);
}
@ -252,17 +253,17 @@ namespace llarp
RCLookupHandler::check_renegotiate_valid(RouterContact newrc, RouterContact oldrc)
{
// mismatch of identity ?
if (newrc.pubkey != oldrc.pubkey)
if (newrc.router_id() != oldrc.router_id())
return false;
if (!is_session_allowed(newrc.pubkey))
if (!is_session_allowed(newrc.router_id()))
return false;
auto func = [this, newrc] { check_rc(newrc); };
work_func(func);
// 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);
}
@ -278,15 +279,15 @@ namespace llarp
std::unordered_set<RouterID> routersToLookUp;
node_db->VisitInsertedBefore(
[&](const RouterContact& rc) { routersToLookUp.insert(rc.pubkey); },
now - RouterContact::UpdateInterval);
[&](const RouterContact& rc) { routersToLookUp.insert(rc.router_id()); },
now - RouterContact::REPUBLISH);
for (const auto& router : routersToLookUp)
{
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
@ -301,7 +302,7 @@ namespace llarp
{
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
// dht->ExploreNetworkVia(dht::Key_t{rc.pubkey});
@ -383,7 +384,7 @@ namespace llarp
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)
{
if (rc.pubkey == remote)
if (rc.router_id() == remote)
{
return true;
}

@ -6,8 +6,6 @@
namespace llarp
{
static auto logcat = log::Cat("route-poker");
void
RoutePoker::add_route(oxen::quic::Address ip)
{
@ -221,7 +219,7 @@ namespace llarp
// explicit route pokes for first hops
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 default route
@ -240,7 +238,7 @@ namespace llarp
{
// unpoke routes for first hops
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)
{
vpn::AbstractRouteManager& route = router.vpn_platform()->RouteManager();

@ -36,8 +36,6 @@ static constexpr std::chrono::milliseconds ROUTER_TICK_INTERVAL = 250ms;
namespace llarp
{
static auto logcat = log::Cat("router");
Router::Router(EventLoop_ptr loop, std::shared_ptr<vpn::Platform> vpnPlatform)
: _route_poker{std::make_shared<RoutePoker>(*this)}
, _lmq{std::make_shared<oxenmq::OxenMQ>()}
@ -178,7 +176,7 @@ namespace llarp
util::StatusObject stats{
{"running", true},
{"version", llarp::VERSION_FULL},
{"version", llarp::LOKINET_VERSION_FULL},
{"uptime", to_json(Uptime())},
{"numPathsBuilt", pathsCount},
{"numPeersConnected", peers},
@ -216,8 +214,9 @@ namespace llarp
std::unordered_set<RouterID> peer_pubkeys;
for_each_connection(
[&peer_pubkeys](link::Connection& conn) { peer_pubkeys.emplace(conn.remote_rc.pubkey); });
for_each_connection([&peer_pubkeys](link::Connection& conn) {
peer_pubkeys.emplace(conn.remote_rc.router_id());
});
loop()->call([this, &peer_pubkeys]() {
for (auto& pk : peer_pubkeys)
@ -253,7 +252,7 @@ namespace llarp
if (auto maybe = node_db()->GetRandom([](const auto&) -> bool { return true; }))
{
router = maybe->pubkey;
router = maybe->router_id();
return true;
}
return false;
@ -444,16 +443,16 @@ namespace llarp
Router::HandleSaveRC() const
{
std::string fname = our_rc_file.string();
router_contact.Write(fname.c_str());
router_contact.write(fname.c_str());
}
bool
Router::SaveRC()
{
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");
return false;
}
@ -569,9 +568,7 @@ namespace llarp
{
SecretKey nextOnionKey;
RouterContact nextRC = router_contact;
if (!nextRC.Sign(identity()))
return false;
if (!nextRC.Verify(time_now_ms(), false))
if (!nextRC.sign(identity())) // TODO: RouterContact -> LocalRC
return false;
router_contact = std::move(nextRC);
if (IsServiceNode())
@ -585,20 +582,17 @@ namespace llarp
// Set netid before anything else
log::debug(logcat, "Network ID set to {}", conf.router.m_netId);
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;
llarp::LogWarn(
"!!!! you have manually set netid to be '",
netid,
"' which does not equal '",
llarp::DEFAULT_NETID,
llarp::LOKINET_DEFAULT_NETID,
"' you will run as a different network, good luck "
"and don't forget: something something MUH traffic "
"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
@ -628,7 +622,7 @@ namespace llarp
else
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;
@ -679,10 +673,10 @@ namespace llarp
auto clearBadRCs = [this]() {
for (auto it = bootstrap_rc_list.begin(); it != bootstrap_rc_list.end();)
{
if (it->IsObsoleteBootstrap())
log::warning(logcat, "ignoring obsolete boostrap RC: {}", RouterID{it->pubkey});
else if (not it->Verify(now()))
log::warning(logcat, "ignoring invalid bootstrap RC: {}", RouterID{it->pubkey});
if (it->is_obsolete_bootstrap())
log::warning(logcat, "ignoring obsolete boostrap RC: {}", RouterID{it->router_id()});
else if (not it->verify(now())) // TODO: RouterContact -> RemoteRC
log::warning(logcat, "ignoring invalid bootstrap RC: {}", RouterID{it->router_id()});
else
{
++it;
@ -785,7 +779,7 @@ namespace llarp
return std::count_if(
bootstrap_rc_list.begin(),
bootstrap_rc_list.end(),
[r](const RouterContact& rc) -> bool { return rc.pubkey == r; })
[r](const RouterContact& rc) -> bool { return rc.router_id() == r; })
> 0;
}
@ -806,8 +800,8 @@ namespace llarp
if (IsServiceNode())
{
LogInfo(NumberOfConnectedClients(), " client connections");
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.age(now)), " since we last updated our RC");
LogInfo(ToString(router_contact.time_to_expiry(now)), " until our RC expires");
}
if (_last_stats_report > 0s)
LogInfo(ToString(now - _last_stats_report), " last reported stats");
@ -819,7 +813,7 @@ namespace llarp
{
std::string 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())
{
fmt::format_to(
@ -901,7 +895,8 @@ namespace llarp
bool should_gossip = appears_funded();
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");
if (update_rc())
@ -923,22 +918,22 @@ namespace llarp
// remove RCs for nodes that are no longer allowed by network policy
node_db()->RemoveIf([&](const RouterContact& rc) -> bool {
// 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;
}
// if for some reason we stored an RC that isn't a valid router
// 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;
}
/// 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;
}
// clients have no notion of a whilelist
@ -946,23 +941,23 @@ namespace llarp
// routers that are not whitelisted for first hops
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;
}
// if we don't have the whitelist yet don't remove the entry
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;
}
// if we have no whitelist enabled or we have
// the whitelist enabled and we got the whitelist
// check against the whitelist and remove if it's not
// 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 false;
@ -971,10 +966,10 @@ namespace llarp
if (not is_snode or not has_whitelist)
{
// 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) {
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))
close_peers.insert(pk);
@ -1051,7 +1046,7 @@ namespace llarp
std::set<dht::Key_t> peer_keys;
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(
[&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)
return false;
// set public signing key
router_contact.pubkey = seckey_topublic(identity());
// TODO: replace all this logic with construction of LocalRC
/* // set public signing key
router_contact._router_id = seckey_topublic(identity());
// set router version if service node
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");
return false;
@ -1130,7 +1128,7 @@ namespace llarp
router_contact.enckey = seckey_topublic(encryption());
LogInfo("Signing rc...");
if (!router_contact.Sign(identity()))
if (!router_contact.sign(identity()))
{
LogError("failed to sign rc");
return false;
@ -1166,14 +1164,14 @@ namespace llarp
// regenerate keys and resign rc before everything else
crypto::identity_keygen(_identity);
crypto::encryption_keygen(_encryption);
router_contact.pubkey = seckey_topublic(identity());
router_contact._router_id = seckey_topublic(identity());
router_contact.enckey = seckey_topublic(encryption());
if (!router_contact.Sign(identity()))
if (!router_contact.sign(identity()))
{
LogError("failed to regenerate keys and sign RC");
return false;
}
}
} */
LogInfo("starting hidden service context...");
if (!hidden_service_context().StartAll())
@ -1193,7 +1191,7 @@ namespace llarp
{
node_db()->put_rc(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");

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

@ -12,407 +12,276 @@
namespace llarp
{
static auto logcat = log::Cat("RC");
NetID&
NetID::DefaultValue()
{
static NetID defaultID(reinterpret_cast<const byte_t*>(llarp::DEFAULT_NETID));
return defaultID;
}
bool RouterContact::BlockBogons = true;
// RouterContact::RouterContact(std::string buf)
// {
// try
// {
// oxenc::bt_list_consumer btlc{buf};
/// 1 day rc lifespan
constexpr auto rc_lifetime = 24h;
/// 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;
// // signature.from_string(btlc.consume_string());
// signed_bt_dict = btlc.consume_string();
llarp_time_t RouterContact::Lifetime = rc_lifetime;
llarp_time_t RouterContact::StaleInsertionAge = rc_stale_age;
llarp_time_t RouterContact::UpdateInterval = rc_update_interval;
// // TODO: parse bt dict
// }
// 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
constexpr auto expiration_lifetime_generations = 10;
/// 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)
std::string
RouterContact::bt_encode() const
{
const size_t len = strnlen(reinterpret_cast<const char*>(val), size());
std::copy(val, val + len, begin());
oxenc::bt_dict_producer btdp;
bt_encode(btdp);
return std::move(btdp).str();
}
NetID::NetID() : NetID(DefaultValue().data())
{}
bool
NetID::operator==(const NetID& other) const
void
RouterContact::bt_load(oxenc::bt_dict_consumer& data)
{
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
NetID::ToString() const
{
return {begin(), std::find(begin(), end(), '\0')};
}
auto ipv4_port = data.require<std::string_view>("4");
bool
NetID::BDecode(llarp_buffer_t* buf)
{
Zero();
llarp_buffer_t strbuf;
if (!bencode_read_string(buf, &strbuf))
return false;
if (ipv4_port.size() != 6)
throw std::runtime_error{
"Invalid RC address: expected 6-byte IPv4 IP/port, got {}"_format(ipv4_port.size())};
if (strbuf.sz > size())
return false;
sockaddr_in s4;
s4.sin_family = AF_INET;
std::copy(strbuf.base, strbuf.base + strbuf.sz, begin());
return true;
}
std::memcpy(&s4.sin_addr.s_addr, ipv4_port.data(), 4);
std::memcpy(&s4.sin_port, ipv4_port.data() + 4, 2);
bool
NetID::BEncode(llarp_buffer_t* buf) const
{
auto term = std::find(begin(), end(), '\0');
return bencode_write_bytestring(buf, data(), std::distance(begin(), term));
}
_addr = oxen::quic::Address{&s4};
RouterContact::RouterContact(std::string buf)
{
try
if (!_addr.is_public())
throw std::runtime_error{"Invalid RC: IPv4 address is not a publicly routable IP"};
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());
signed_bt_dict = btlc.consume_string();
std::memcpy(&s6.sin6_addr.s6_addr, ipv6_port->data(), 16);
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
RouterContact::bt_encode() const
{
oxenc::bt_list_producer btlp;
auto netid = data.maybe<std::string_view>("i").value_or(llarp::LOKINET_DEFAULT_NETID);
if (netid != ACTIVE_NETID)
throw std::runtime_error{
"Invalid RC netid: expected {}, got {}; this is an RC for a different network!"_format(
ACTIVE_NETID, netid)};
try
{
btlp.append(signature.ToView());
btlp.append(signed_bt_dict);
}
catch (...)
{
log::warning(llarp_cat, "Error: RouterContact failed to bt encode contents!");
}
auto pk = data.require<std::string_view>("p");
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
RouterContact::bt_encode_subdict(oxenc::bt_list_producer& btlp) const
{
btlp.append(signature.ToView());
btlp.append(signed_bt_dict);
}
std::memcpy(_router_id.data(), pk.data(), RouterID::SIZE);
std::string
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;
}
_timestamp = rc_time{std::chrono::seconds{data.require<int64_t>("t")}};
bool
RouterContact::FromOurNetwork() const
{
return netID == NetID::DefaultValue();
}
auto ver = data.require<ustring_view>("v");
std::string
RouterContact::bencode_signed_section() const
{
oxenc::bt_dict_producer btdp;
if (ver.size() != 3)
throw std::runtime_error{
"Invalid RC router version: received {} bytes (!= 3)"_format(ver.size())};
btdp.append("a", addr.to_string());
btdp.append("i", netID.ToView());
btdp.append("k", pubkey.bt_encode());
btdp.append("p", enckey.ToView());
btdp.append("r", routerVersion->ToString());
for (int i = 0; i < 3; i++)
_router_version[i] = ver[i];
}
if (not srvRecords.empty())
{
auto sublist = btdp.append_list("s");
// std::string
// RouterContact::bencode_signed_section() const
// {
// oxenc::bt_dict_producer btdp;
for (auto& s : srvRecords)
sublist.append(s.bt_encode());
}
// btdp.append("4", _addr.to_string());
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
RouterContact::Clear()
{
signature.Zero();
enckey.Zero();
pubkey.Zero();
routerVersion = std::optional<RouterVersion>{};
last_updated = 0s;
srvRecords.clear();
version = llarp::constants::proto_version;
}
// btdp.append("p", _router_id.bt_encode());
// btdp.append("t", _timestamp.time_since_epoch().count());
// btdp.append("v", _router_version.data());
// return std::move(btdp).str();
// }
util::StatusObject
RouterContact::ExtractStatus() const
RouterContact::extract_status() const
{
util::StatusObject obj{
{"lastUpdated", last_updated.count()},
{"publicRouter", IsPublicRouter()},
{"identity", pubkey.ToString()},
{"address", addr.to_string()}};
{"lastUpdated", _timestamp.time_since_epoch().count()},
{"publicRouter", is_public_router()},
{"identity", _router_id.ToString()},
{"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;
}
bool
RouterContact::BDecode(llarp_buffer_t* buf)
{
Clear();
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);
}
// TODO: unfuck all of this
bool
RouterContact::DecodeVersion_1(oxenc::bt_list_consumer& btlist)
{
auto signature_string = btlist.consume_string_view();
signed_bt_dict = btlist.consume_dict_data();
(void)buf;
if (not btlist.is_finished())
{
log::debug(logcat, "RouterContact serialized list too long for specified version.");
return false;
}
// clear();
llarp_buffer_t sigbuf(signature_string.data(), signature_string.size());
if (not signature.FromBytestring(&sigbuf))
{
log::debug(logcat, "RouterContact serialized signature had invalid length.");
return false;
}
// 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());
// }
llarp_buffer_t data_dict_buf(signed_bt_dict.data(), signed_bt_dict.size());
return bencode_decode_dict(*this, &data_dict_buf);
return false;
}
bool
RouterContact::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
{
bool read = false;
(void)key;
// TOFIX: fuck everything about llarp_buffer_t
// if (!BEncodeMaybeReadDictEntry("a", addr, read, key, buf))
// return false;
if (!BEncodeMaybeReadDictEntry("i", netID, read, key, buf))
return false;
// if (!BEncodeMaybeReadDictEntry("i", netID, read, key, buf))
// return false;
if (!BEncodeMaybeReadDictEntry("k", pubkey, read, key, buf))
return false;
// if (!BEncodeMaybeReadDictEntry("k", _router_id, read, key, buf))
// return false;
if (key.startswith("r"))
{
RouterVersion r;
if (not r.BDecode(buf))
return false;
routerVersion = r;
return true;
}
// if (key.startswith("r"))
// {
// RouterVersion r;
// if (not r.BDecode(buf))
// return false;
// routerVersion = r;
// return true;
// }
if (not BEncodeMaybeReadDictList("s", srvRecords, read, key, buf))
return false;
// if (not BEncodeMaybeReadDictList("s", srvRecords, read, key, buf))
// return false;
if (!BEncodeMaybeReadDictEntry("p", enckey, read, key, buf))
return false;
// if (!BEncodeMaybeReadDictEntry("p", enckey, read, key, buf))
// return false;
if (!BEncodeMaybeReadDictInt("u", last_updated, read, key, buf))
return false;
// if (!BEncodeMaybeReadDictInt("u", _timestamp, read, key, buf))
// return false;
if (!BEncodeMaybeReadDictInt("v", version, read, key, buf))
return false;
// if (!BEncodeMaybeReadDictInt("v", version, read, key, buf))
// return false;
if (key.startswith("x") and serializeExit)
{
return bencode_discard(buf);
}
// if (key.startswith("x") and serializeExit)
// {
// return bencode_discard(buf);
// }
if (!BEncodeMaybeReadDictEntry("z", signature, read, key, buf))
return false;
// if (!BEncodeMaybeReadDictEntry("z", signature, read, key, buf))
// return false;
return read or bencode_discard(buf);
}
bool
RouterContact::IsPublicRouter() const
RouterContact::is_public_router() const
{
if (not routerVersion)
if (_router_version.empty())
return false;
return addr.is_addressable();
return _addr.is_addressable();
}
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
RouterContact::TimeUntilExpires(llarp_time_t now) const
RouterContact::time_to_expiry(llarp_time_t now) const
{
const auto expiresAt = last_updated + Lifetime;
return now < expiresAt ? expiresAt - now : 0s;
const auto expiry = _timestamp.time_since_epoch() + LIFETIME;
return now < expiry ? expiry - now : 0s;
}
llarp_time_t
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)
RouterContact::age(llarp_time_t now) const
{
pubkey = llarp::seckey_topublic(secretkey);
signature.Zero();
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;
auto delta = now - _timestamp.time_since_epoch();
return delta > 0s ? delta : 0s;
}
bool
RouterContact::VerifySignature() const
RouterContact::expires_within_delta(llarp_time_t now, llarp_time_t dlt) const
{
RouterContact copy;
copy = *this;
copy.signature.Zero();
auto bte = copy.bt_encode();
return crypto::verify(pubkey, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), signature);
return time_to_expiry(now) <= dlt;
}
static constexpr std::array obsolete_bootstraps = {
@ -421,18 +290,18 @@ namespace llarp
};
bool
RouterContact::IsObsoleteBootstrap() const
RouterContact::is_obsolete_bootstrap() const
{
for (const auto& k : obsolete_bootstraps)
{
if (pubkey.ToHex() == k)
if (_router_id.ToHex() == k)
return true;
}
return false;
}
bool
RouterContact::Write(const fs::path& fname) const
RouterContact::write(const fs::path& fname) const
{
std::array<byte_t, MAX_RC_SIZE> tmp;
llarp_buffer_t buf(tmp);
@ -453,13 +322,13 @@ namespace llarp
}
bool
RouterContact::Read(const fs::path& fname)
RouterContact::read(const fs::path& fname)
{
std::array<byte_t, MAX_RC_SIZE> tmp;
llarp_buffer_t buf(tmp);
try
{
util::slurp_file(fname, tmp.data(), tmp.size());
util::file_to_buffer(fname, tmp.data(), tmp.size());
}
catch (const std::exception& e)
{
@ -468,19 +337,4 @@ namespace llarp
}
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

@ -1,5 +1,6 @@
#pragma once
#include "router_id.hpp"
#include "router_version.hpp"
#include <llarp/constants/version.hpp>
@ -17,128 +18,169 @@
#include <functional>
#include <vector>
#define MAX_RC_SIZE (1024)
namespace oxenc
{
class bt_list_consumer;
} // 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
{
/// NetID
struct NetID final : public AlignedBuffer<8>
static auto logcat = log::Cat("RC");
using rc_time = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
/// RouterContact
struct RouterContact
{
static NetID&
DefaultValue();
static constexpr uint8_t RC_VERSION = 0;
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;
NetID&
operator=(const NetID& other) = default;
/// RC version that we support; we fail to load RCs that don't have the same version as that
/// means they are incompatible with us.
// static constexpr uint8_t VERSION = 0;
bool
operator==(const NetID& other) const;
/// Unit tests disable this to allow private IP ranges in RCs, which normally get rejected.
static inline bool BLOCK_BOGONS = true;
bool
operator!=(const NetID& other) const
{
return !(*this == other);
}
static inline std::string ACTIVE_NETID{LOKINET_DEFAULT_NETID};
std::string
ToString() const;
static inline constexpr size_t MAX_RC_SIZE = 1024;
bool
BDecode(llarp_buffer_t* buf);
/// Timespans for RCs:
bool
BEncode(llarp_buffer_t* buf) const;
};
/// How long (relative to its timestamp) before an RC becomes stale. Stale records are used
/// (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
struct RouterContact
{
/// for unit tests
static bool BlockBogons;
/// How long before an RC becomes invalid (and thus deleted).
static constexpr auto LIFETIME = 30 * 24h;
/// How long before a relay updates and re-publish its RC to the network. (Relays can
/// 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;
static llarp_time_t UpdateInterval;
static llarp_time_t StaleInsertionAge;
/// Getters for private attributes
const oxen::quic::Address&
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
oxen::quic::Address addr;
// network identifier
NetID netID;
// public encryption public key
llarp::PubKey enckey;
oxen::quic::Address _addr; // refactor all 15 uses to use addr() method
std::optional<oxen::quic::Address> _addr6; // optional ipv6
// public signing public key
llarp::PubKey pubkey;
// signature
llarp::Signature signature;
RouterID _router_id; // refactor all 103 uses to use router_id() method
rc_time _timestamp{};
llarp_time_t last_updated = 0s;
uint64_t version = llarp::constants::proto_version;
std::optional<RouterVersion> routerVersion;
// Lokinet version at the time the RC was produced
std::array<uint8_t, 3> _router_version;
public:
/// should we serialize the exit info?
const static bool serializeExit = true;
std::string signed_bt_dict;
std::vector<dns::SRVData> srvRecords;
ustring _signed_payload;
util::StatusObject
ExtractStatus() const;
extract_status() const;
RouterID
router_id() const
nlohmann::json
to_json() const
{
return pubkey;
return extract_status();
}
nlohmann::json
ToJson() const
virtual std::string
to_string() const
{
return ExtractStatus();
return fmt::format(
"[RC k={} updated={} v={} addr={}]",
_router_id,
_timestamp,
RC_VERSION,
_addr.to_string());
}
std::string
ToString() const;
/// On the wire we encode the data as a dict containing:
/// "" -- 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
bt_encode() const;
void
bt_encode_subdict(oxenc::bt_list_producer& btlp) const;
std::string
bencode_signed_section() const;
std::string
ToTXTRecord() const;
virtual void
bt_encode(oxenc::bt_dict_producer& btdp) const
{
(void)btdp;
}
bool
operator==(const RouterContact& other) const
{
return addr == other.addr && enckey == other.enckey && pubkey == other.pubkey
&& signature == other.signature && last_updated == other.last_updated
&& netID == other.netID;
return _router_id == other._router_id and _addr == other._addr and _addr6 == other._addr6
and _timestamp == other._timestamp and _router_version == other._router_version;
}
bool
operator<(const RouterContact& other) const
{
return pubkey < other.pubkey;
return _router_id < other._router_id;
}
bool
@ -147,14 +189,9 @@ namespace llarp
return !(*this == other);
}
void
Clear();
bool
IsExit() const
{
return false;
}
virtual void
clear()
{}
bool
BDecode(llarp_buffer_t* buf);
@ -163,64 +200,170 @@ namespace llarp
decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf);
bool
IsPublicRouter() const;
bool
Verify(llarp_time_t now, bool allowExpired = true) const;
bool
Sign(const llarp::SecretKey& secret);
is_public_router() const;
/// does this RC expire soon? default delta is 1 minute
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
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
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
llarp_time_t
Age(llarp_time_t now) const;
age(llarp_time_t now) const;
bool
OtherIsNewer(const RouterContact& other) const
other_is_newer(const RouterContact& other) const
{
return last_updated < other.last_updated;
return _timestamp < other._timestamp;
}
bool
Read(const fs::path& fname);
read(const fs::path& fname);
bool
Write(const fs::path& fname) const;
write(const fs::path& fname) const;
bool
VerifySignature() const;
is_obsolete_bootstrap() const;
/// return true if the netid in this rc is for the network id we are using
bool
FromOurNetwork() const;
void
bt_load(oxenc::bt_dict_consumer& data);
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:
bool
DecodeVersion_0(llarp_buffer_t* buf);
ustring _signature;
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
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 <>
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>&)>;
} // namespace llarp
@ -233,7 +376,7 @@ namespace std
size_t
operator()(const llarp::RouterContact& r) const
{
return std::hash<llarp::PubKey>{}(r.pubkey);
return std::hash<llarp::PubKey>{}(r.router_id());
}
};
} // 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/status.hpp"
#include <llarp/crypto/types.hpp>
namespace llarp
{
struct RouterID : public AlignedBuffer<32>
struct RouterID : public PubKey
{
static constexpr size_t SIZE = 32;
@ -13,10 +15,10 @@ namespace llarp
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

@ -181,7 +181,7 @@ namespace llarp::rpc
nlohmann::json payload = {
{"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())
payload["error"] = *err;

@ -154,7 +154,7 @@ namespace llarp::rpc
RPCServer::invoke(Version& version)
{
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);
}

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

@ -44,7 +44,7 @@ namespace llarp::service
{
crypto::identity_keygen(signkey);
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);
if (not crypto::derive_subkey_private(derivedSignKey, signkey, 1))
{
@ -114,7 +114,7 @@ namespace llarp::service
// read file
try
{
util::slurp_file(fname, tmp.data(), tmp.size());
util::file_to_buffer(fname, tmp.data(), tmp.size());
}
catch (const std::length_error&)
{
@ -139,7 +139,7 @@ namespace llarp::service
if (!vanity.IsZero())
van = vanity;
// 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))
{
throw std::runtime_error("failed to derive subkey");
@ -163,7 +163,7 @@ namespace llarp::service
// set service info
i.address_keys = pub;
// 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();

@ -256,7 +256,7 @@ namespace llarp::service
// copy
ProtocolFrameMessage frame(self->frame);
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);
self->msg.reset();

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

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

@ -14,21 +14,21 @@ namespace llarp::util
{
/// Reads a binary file from disk into a string. Throws on error.
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
/// file is bigger than the buffer. Returns the bytes copied on success.
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).
template <
typename Char,
std::enable_if_t<sizeof(Char) == 1 and not std::is_same_v<Char, char>, int> = 1>
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

Loading…
Cancel
Save