diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c2647676..e96dc26bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,11 +15,11 @@ endif() set(PROJECT_NAME lokinet) project(${PROJECT_NAME} - VERSION 0.7.1 + VERSION 0.8.0 DESCRIPTION "lokinet - IP packet onion router" LANGUAGES C CXX) -set(RELEASE_MOTTO "I'll remember that..." CACHE STRING "Release motto") +set(RELEASE_MOTTO "Proof of soon" CACHE STRING "Release motto") add_definitions(-DLLARP_VERSION_MAJOR=${lokinet_VERSION_MAJOR}) add_definitions(-DLLARP_VERSION_MINOR=${lokinet_VERSION_MINOR}) diff --git a/contrib/py/keygen/.gitignore b/contrib/py/keygen/.gitignore new file mode 100644 index 000000000..a4ed579b5 --- /dev/null +++ b/contrib/py/keygen/.gitignore @@ -0,0 +1 @@ +*.private \ No newline at end of file diff --git a/contrib/py/keygen/keygen.py b/contrib/py/keygen/keygen.py new file mode 100755 index 000000000..fa6639301 --- /dev/null +++ b/contrib/py/keygen/keygen.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +""" +keygen tool for lokinet +""" + +from argparse import ArgumentParser as AP +from base64 import b32encode + +from nacl.signing import SigningKey + +def base32z(data): + """ base32 z encode """ + return b32encode(data).translate( + bytes.maketrans( + b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', + b'ybndrfg8ejkmcpqxot1uwisza345h769')).decode().rstrip('=') + + +def main(): + """ + main function for keygen + """ + argparser = AP() + argparser.add_argument('--keyfile', type=str, required=True, help='place to put generated keys') + args = argparser.parse_args() + secret = SigningKey.generate() + with open(args.keyfile, 'wb') as wfile: + wfile.write(b'd1:s64:') + wfile.write(secret.encode()) + wfile.write(secret.verify_key.encode()) + wfile.write(b'e') + print("{}.loki".format(base32z(secret.verify_key.encode()))) + +if __name__ == '__main__': + main() diff --git a/contrib/py/keygen/readme.md b/contrib/py/keygen/readme.md new file mode 100644 index 000000000..3ee03e09a --- /dev/null +++ b/contrib/py/keygen/readme.md @@ -0,0 +1,14 @@ +# lokinet key generator + +requires: + +* python3.7 or higher +* pynacl + +usage: + +```bash +./keygen.py --keyfile somekeyfile.private +``` + +this will overwrite the keyfile with new keys diff --git a/external/loki-mq b/external/loki-mq index 0ac1d48bc..53481cdfa 160000 --- a/external/loki-mq +++ b/external/loki-mq @@ -1 +1 @@ -Subproject commit 0ac1d48bc8058bab4f10aeb3010343084f7a37d0 +Subproject commit 53481cdfa9b0dc8d6dbbf04803401298754d7f44 diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 54a1e0f45..b9b1c644c 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -175,6 +175,7 @@ add_library(liblokinet router/rc_lookup_handler.cpp router/rc_gossiper.cpp router/router.cpp + router/route_poker.cpp router_contact.cpp router_id.cpp router_version.cpp diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 287fb73f9..777aa9ba5 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -559,7 +559,7 @@ namespace llarp } void - Config::Save() const + Config::Save() { m_Parser.Save(); } diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index bef5410f1..a960bea8b 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -229,7 +229,7 @@ namespace llarp generateBaseRouterConfig(fs::path defaultDataDir); void - Save() const; + Save(); void Override(std::string section, std::string key, std::string value); diff --git a/llarp/config/ini.cpp b/llarp/config/ini.cpp index 6d3eb0970..c62329abd 100644 --- a/llarp/config/ini.cpp +++ b/llarp/config/ini.cpp @@ -171,7 +171,7 @@ namespace llarp } void - ConfigParser::Save() const + ConfigParser::Save() { // if we have no overrides keep the config the same on disk if (m_Overrides.empty()) @@ -189,6 +189,7 @@ namespace llarp ofs << key << "=" << value << std::endl; } } + m_Overrides.clear(); } } // namespace llarp diff --git a/llarp/config/ini.hpp b/llarp/config/ini.hpp index c9a8637a0..f39229746 100644 --- a/llarp/config/ini.hpp +++ b/llarp/config/ini.hpp @@ -46,7 +46,7 @@ namespace llarp /// save config and any overrides to the file it was loaded from void - Save() const; + Save(); private: bool diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 44a593c6b..1e4b9a497 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -623,9 +623,13 @@ namespace llarp std::string TunEndpoint::GetIfName() const { +#ifdef _WIN32 + return net::TruncateV6(GetIfAddr()).ToString(); +#else if (tunif) return tunif->ifname; return m_IfName; +#endif } bool diff --git a/llarp/net/ip_address.cpp b/llarp/net/ip_address.cpp index 6716b88e0..8305807b1 100644 --- a/llarp/net/ip_address.cpp +++ b/llarp/net/ip_address.cpp @@ -154,6 +154,14 @@ namespace llarp return m_ipAddress; } + huint32_t + IpAddress::toIP() const + { + huint32_t ip; + ip.FromString(toHost()); + return ip; + } + bool IpAddress::operator<(const IpAddress& other) const { diff --git a/llarp/net/ip_address.hpp b/llarp/net/ip_address.hpp index 66f066b00..b072d89a3 100644 --- a/llarp/net/ip_address.hpp +++ b/llarp/net/ip_address.hpp @@ -6,6 +6,8 @@ #include #include +#include + namespace llarp { /// A struct that can represent either an IPv4 or IPv6 address. It is meant for representation @@ -121,6 +123,9 @@ namespace llarp std::string toHost() const; + huint32_t + toIP() const; + // TODO: other utility functions left over from Addr which may be useful // IsBogon() const; // isPrivate() const; diff --git a/llarp/net/ip_range_map.hpp b/llarp/net/ip_range_map.hpp index ec717e5dc..bead3001e 100644 --- a/llarp/net/ip_range_map.hpp +++ b/llarp/net/ip_range_map.hpp @@ -32,6 +32,12 @@ namespace llarp return all; } + bool + Empty() const + { + return m_Entries.empty(); + } + bool ContainsValue(const Value_t& val) const { diff --git a/llarp/net/route.cpp b/llarp/net/route.cpp index 35dc4d334..9ac20f76f 100644 --- a/llarp/net/route.cpp +++ b/llarp/net/route.cpp @@ -370,25 +370,8 @@ namespace llarp::net #endif #elif _WIN32 ifname.back()++; - int ifindex = 0; - // find interface index for address - ForEachWIN32Interface([&ifindex, ifname = ifname](auto w32interface) { - in_addr interface_addr; - interface_addr.S_un.S_addr = (u_long)w32interface->dwForwardNextHop; - std::array interface_str{}; - StringCchCopy(interface_str.data(), interface_str.size(), inet_ntoa(interface_addr)); - std::string interface_name{interface_str.data()}; - if (interface_name == ifname) - { - ifindex = w32interface->dwForwardIfIndex; - } - }); - Execute( - RouteCommand() + " ADD 0.0.0.0 MASK 128.0.0.0 " + ifname + " IF " - + std::to_string(ifindex)); - Execute( - RouteCommand() + " ADD 128.0.0.0 MASK 128.0.0.0 " + ifname + " IF " - + std::to_string(ifindex)); + Execute(RouteCommand() + " ADD 0.0.0.0 MASK 128.0.0.0 " + ifname); + Execute(RouteCommand() + " ADD 128.0.0.0 MASK 128.0.0.0 " + ifname); #elif __APPLE__ Execute("/sbin/route -n add -cloning -net 0.0.0.0 -netmask 128.0.0.0 -interface " + ifname); Execute("/sbin/route -n add -cloning -net 128.0.0.0 -netmask 128.0.0.0 -interface " + ifname); diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index 3ca8d4328..4153e0b18 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -80,7 +80,7 @@ namespace llarp } } - LogInfo("Updating ", staleStats.size(), " stats"); + LogDebug("Updating ", staleStats.size(), " stats"); { auto guard = m_storage->transaction_guard(); @@ -96,7 +96,7 @@ namespace llarp auto end = time_now_ms(); auto elapsed = end - start; - LogInfo("PeerDb flush took about ", elapsed, " seconds"); + LogDebug("PeerDb flush took about ", elapsed, " seconds"); m_lastFlush.store(end); } diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index d32684e4d..ca53fc99e 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -43,6 +43,7 @@ namespace llarp struct IOutboundSessionMaker; struct ILinkManager; struct I_RCLookupHandler; + struct RoutePoker; namespace exit { @@ -156,6 +157,9 @@ namespace llarp virtual ILinkManager& linkManager() = 0; + virtual RoutePoker& + routePoker() = 0; + virtual I_RCLookupHandler& rcLookupHandler() = 0; diff --git a/llarp/router/route_poker.cpp b/llarp/router/route_poker.cpp new file mode 100644 index 000000000..2e2f5e505 --- /dev/null +++ b/llarp/router/route_poker.cpp @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include + +namespace llarp +{ + void + RoutePoker::AddRoute(huint32_t ip) + { + if (m_CurrentGateway.h == 0) + return; + m_PokedRoutes.emplace(ip, m_CurrentGateway); + net::AddRoute(ip.ToString(), m_CurrentGateway.ToString()); + } + + void + RoutePoker::DelRoute(huint32_t ip) + { + const auto itr = m_PokedRoutes.find(ip); + if (itr == m_PokedRoutes.end()) + return; + net::DelRoute(itr->first.ToString(), itr->second.ToString()); + m_PokedRoutes.erase(itr); + } + + RoutePoker::~RoutePoker() + { + for (const auto& [ip, gateway] : m_PokedRoutes) + net::DelRoute(ip.ToString(), gateway.ToString()); + } + + std::optional + RoutePoker::GetDefaultGateway(const AbstractRouter& router) const + { + const auto ep = router.hiddenServiceContext().GetDefault(); + const auto gateways = net::GetGatewaysNotOnInterface(ep->GetIfName()); + huint32_t addr{}; + if (not gateways.empty()) + addr.FromString(gateways[0]); + return addr; + } + + void + RoutePoker::Update(const AbstractRouter& router) + { + const auto maybe = GetDefaultGateway(router); + if (not maybe.has_value()) + { + LogError("Network is down"); + return; + } + const huint32_t gateway = *maybe; + if (m_CurrentGateway != gateway) + { + LogInfo("found default gateway: ", gateway); + // unpoke current routes + std::unordered_set holes; + + for (const auto& [ip, gw] : m_PokedRoutes) + { + // save hole + holes.emplace(ip); + // unpoke route + net::DelRoute(ip.ToString(), gw.ToString()); + } + m_PokedRoutes.clear(); + + m_CurrentGateway = gateway; + for (const auto& ip : holes) + { + AddRoute(ip); + } + + const auto ep = router.hiddenServiceContext().GetDefault(); + net::AddDefaultRouteViaInterface(ep->GetIfName()); + } + } +} // namespace llarp diff --git a/llarp/router/route_poker.hpp b/llarp/router/route_poker.hpp new file mode 100644 index 000000000..7494fc271 --- /dev/null +++ b/llarp/router/route_poker.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +namespace llarp +{ + struct AbstractRouter; + + struct RoutePoker + { + void + AddRoute(huint32_t ip); + + void + DelRoute(huint32_t ip); + + ~RoutePoker(); + + void + Update(const AbstractRouter& router); + + private: + std::optional + GetDefaultGateway(const AbstractRouter& router) const; + + std::unordered_map m_PokedRoutes; + huint32_t m_CurrentGateway; + }; +} // namespace llarp diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 23b11a0ac..7cba6c818 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -333,10 +333,6 @@ namespace llarp void Router::Close() { - for (const auto& [ip, gateway] : m_PokedRoutes) - { - net::DelRoute(ip, gateway); - } if (_onDown) _onDown(); LogInfo("closing router"); @@ -770,45 +766,9 @@ namespace llarp _linkManager.CheckPersistingSessions(now); - if (not IsServiceNode()) + if (HasClientExit()) { - const auto gateway = GetDefaultGateway(); - if (m_CurrentGateway != gateway) - { - // changed gateways - if (m_CurrentGateway.empty()) - { - LogInfo("found default gateway: ", gateway); - } - else if (not gateway.empty()) - { - LogInfo("default gateway changed from ", m_CurrentGateway, " to ", gateway); - } - else - { - LogError("Network is down"); - } - // unpoke current routes - std::unordered_set holes; - - for (const auto& [ip, gw] : m_PokedRoutes) - { - // save hole - holes.emplace(ip); - // unpoke route - net::DelRoute(ip, gw); - } - m_PokedRoutes.clear(); - - if (not gateway.empty()) - { - m_CurrentGateway = gateway; - for (const auto& ip : holes) - { - AddRoute(ip); - } - } - } + m_RoutePoker.Update(*this); } size_t connected = NumberOfConnectedRouters(); @@ -860,7 +820,7 @@ namespace llarp if (m_peerDb->shouldFlush(now)) { - LogWarn("Queing database flush..."); + LogDebug("Queing database flush..."); QueueDiskIO([this]() { m_peerDb->flushDatabase(); }); } } @@ -905,7 +865,7 @@ namespace llarp RouterContact rc; if (not nodedb()->Get(remote, rc)) return; - DelRoute(rc.addrs[0].toIpAddress().toHost()); + m_RoutePoker.DelRoute(rc.addrs[0].toIpAddress().toIP()); } void @@ -1269,31 +1229,13 @@ namespace llarp return true; } - std::string - Router::GetDefaultGateway() const + bool + Router::HasClientExit() const { + if (IsServiceNode()) + return false; const auto ep = hiddenServiceContext().GetDefault(); - const auto gateways = net::GetGatewaysNotOnInterface(ep->GetIfName()); - if (gateways.empty()) - return ""; - return gateways[0]; - } - - void - Router::AddRoute(std::string ip) - { - m_PokedRoutes.emplace(ip, m_CurrentGateway); - net::AddRoute(ip, m_CurrentGateway); - } - - void - Router::DelRoute(std::string ip) - { - const auto itr = m_PokedRoutes.find(ip); - if (itr == m_PokedRoutes.end()) - return; - net::DelRoute(itr->first, itr->second); - m_PokedRoutes.erase(itr); + return ep and ep->HasExit(); } bool @@ -1307,7 +1249,7 @@ namespace llarp [&](llarp::RouterContact rc) { if (IsServiceNode()) return; - AddRoute(rc.addrs[0].toIpAddress().toHost()); + m_RoutePoker.AddRoute(rc.addrs[0].toIpAddress().toIP()); }, util::memFn(&Router::ConnectionEstablished, this), util::memFn(&AbstractRouter::CheckRenegotiateValid, this), diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 0d167176b..6e295568c 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -260,17 +261,13 @@ namespace llarp */ } - std::string - GetDefaultGateway() const; - - void - AddRoute(std::string ip); - - void - DelRoute(std::string ip); + RoutePoker& + routePoker() override + { + return m_RoutePoker; + } - std::unordered_map m_PokedRoutes; - std::string m_CurrentGateway; + RoutePoker m_RoutePoker; void PumpLL() override; @@ -403,6 +400,10 @@ namespace llarp bool SaveRC(); + /// return true if we are a client with an exit configured + bool + HasClientExit() const; + const byte_t* pubkey() const override { diff --git a/llarp/router_contact.cpp b/llarp/router_contact.cpp index b405fd818..2fd810f92 100644 --- a/llarp/router_contact.cpp +++ b/llarp/router_contact.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include #include @@ -83,6 +85,29 @@ namespace llarp bool RouterContact::BEncode(llarp_buffer_t* buf) const + { + if (version == 0) + return BEncodeSignedSection(buf); + else if (version == 1) + { + // TODO: heapless serialization for this in lokimq's bt serialization. + if (not buf->writef("li1e%zu:", signature.size())) + return false; + if (not buf->write(signature.begin(), signature.end())) + return false; + if (not buf->write(signed_bt_dict.begin(), signed_bt_dict.end())) + return false; + if (not buf->writef("e")) + return false; + + return true; + } + + return false; + } + + bool + RouterContact::BEncodeSignedSection(llarp_buffer_t* buf) const { /* write dict begin */ if (!bencode_start_dict(buf)) @@ -150,11 +175,16 @@ namespace llarp if (!BEncodeWriteList(exits.begin(), exits.end(), buf)) return false; } - /* write signature */ - if (!bencode_write_bytestring(buf, "z", 1)) - return false; - if (!signature.BEncode(buf)) - return false; + + if (version == 0) + { + /* write signature */ + if (!bencode_write_bytestring(buf, "z", 1)) + return false; + if (!signature.BEncode(buf)) + return false; + } + return bencode_end(buf); } @@ -189,6 +219,80 @@ namespace llarp 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(buf->cur), buf->sz); + lokimq::bt_list_consumer btlist(buf_view); + + uint64_t outer_version = btlist.consume_integer(); + + if (outer_version == 1) + { + bool decode_result = DecodeVersion_1(btlist); + + // advance the llarp_buffer_t since lokimq serialization is unaware of it. + buf->cur += btlist.current_buffer().data() - buf_view.data() + 1; + + return decode_result; + } + else + { + llarp::LogWarn("Received RouterContact with unkown version (", outer_version, ")"); + return false; + } + } + catch (const std::exception& e) + { + llarp::LogDebug("RouterContact::BDecode failed, reason: ", e.what()); + } + + return false; + } + + bool + RouterContact::DecodeVersion_0(llarp_buffer_t* buf) + { + signed_bt_dict = std::string(reinterpret_cast(buf->cur), buf->sz); + return bencode_decode_dict(*this, buf); + } + + bool + RouterContact::DecodeVersion_1(lokimq::bt_list_consumer& btlist) + { + auto signature_string = btlist.consume_string_view(); + signed_bt_dict = btlist.consume_dict_data(); + + if (not btlist.is_finished()) + { + llarp::LogDebug("RouterContact serialized list too long for specified version."); + return false; + } + + llarp_buffer_t sigbuf(signature_string.data(), signature_string.size()); + if (not signature.FromBytestring(&sigbuf)) + { + llarp::LogDebug("RouterContact serialized signature had invalid length."); + return false; + } + + llarp_buffer_t data_dict_buf(signed_bt_dict.data(), signed_bt_dict.size()); + return bencode_decode_dict(*this, &data_dict_buf); + } + bool RouterContact::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) { @@ -311,13 +415,22 @@ namespace llarp llarp_buffer_t buf(tmp); signature.Zero(); last_updated = time_now_ms(); - if (!BEncode(&buf)) + + if (!BEncodeSignedSection(&buf)) { return false; } buf.sz = buf.cur - buf.base; buf.cur = buf.base; - return CryptoManager::instance()->sign(signature, secretkey, buf); + + signed_bt_dict = std::string(reinterpret_cast(buf.base), buf.sz); + + if (version == 0 or version == 1) + { + return CryptoManager::instance()->sign(signature, secretkey, buf); + } + + return false; } bool @@ -357,19 +470,30 @@ namespace llarp bool RouterContact::VerifySignature() const { - RouterContact copy; - copy = *this; - copy.signature.Zero(); - std::array tmp; - llarp_buffer_t buf(tmp); - if (!copy.BEncode(&buf)) + if (version == 0) { - llarp::LogError("bencode failed"); - return false; + RouterContact copy; + copy = *this; + copy.signature.Zero(); + std::array tmp; + llarp_buffer_t buf(tmp); + if (!copy.BEncode(&buf)) + { + llarp::LogError("bencode failed"); + return false; + } + buf.sz = buf.cur - buf.base; + buf.cur = buf.base; + return CryptoManager::instance()->verify(pubkey, buf, signature); } - buf.sz = buf.cur - buf.base; - buf.cur = buf.base; - return CryptoManager::instance()->verify(pubkey, buf, signature); + /* else */ + if (version == 1) + { + llarp_buffer_t buf(signed_bt_dict); + return CryptoManager::instance()->verify(pubkey, buf, signature); + } + + return false; } bool diff --git a/llarp/router_contact.hpp b/llarp/router_contact.hpp index 89bb2dace..c373c023a 100644 --- a/llarp/router_contact.hpp +++ b/llarp/router_contact.hpp @@ -17,6 +17,11 @@ #define MAX_RC_SIZE (1024) #define NICKLEN (32) +namespace lokimq +{ + class bt_list_consumer; +} // namespace lokimq + namespace llarp { /// NetID @@ -107,6 +112,8 @@ namespace llarp /// should we serialize the exit info? const static bool serializeExit = true; + std::string signed_bt_dict; + util::StatusObject ExtractStatus() const; @@ -125,6 +132,9 @@ namespace llarp bool BEncode(llarp_buffer_t* buf) const; + bool + BEncodeSignedSection(llarp_buffer_t* buf) const; + bool operator==(const RouterContact& other) const { @@ -155,11 +165,7 @@ namespace llarp } bool - BDecode(llarp_buffer_t* buf) - { - Clear(); - return bencode_decode_dict(*this, buf); - } + BDecode(llarp_buffer_t* buf); bool DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf); @@ -215,6 +221,13 @@ namespace llarp bool VerifySignature() const; + + private: + bool + DecodeVersion_0(llarp_buffer_t* buf); + + bool + DecodeVersion_1(lokimq::bt_list_consumer& btlist); }; inline std::ostream& diff --git a/llarp/rpc/rpc_server.cpp b/llarp/rpc/rpc_server.cpp index 3b6db6575..d973bb802 100644 --- a/llarp/rpc/rpc_server.cpp +++ b/llarp/rpc/rpc_server.cpp @@ -1,5 +1,5 @@ #include "rpc_server.hpp" -#include +#include #include #include #include @@ -167,7 +167,7 @@ namespace llarp::rpc { endpoint = endpoint_itr->get(); } - LogicCall(r->logic(), [map, exit, range, token, endpoint, r, reply]() { + LogicCall(r->logic(), [map, exit, range, token, endpoint, r, reply]() mutable { auto ep = r->hiddenServiceContext().GetEndpointByName(endpoint); if (ep == nullptr) { @@ -183,12 +183,18 @@ namespace llarp::rpc } ep->EnsurePathToService( *exit, - [reply, ep](auto, service::OutboundContext* ctx) { + [reply, ep, r](auto, service::OutboundContext* ctx) { if (ctx == nullptr) { reply(CreateJSONError("could not find exit")); return; } + r->ForEachPeer( + [r](auto session, auto) mutable { + const auto ip = session->GetRemoteEndpoint().toIP(); + r->routePoker().AddRoute(ip); + }, + false); net::AddDefaultRouteViaInterface(ep->GetIfName()); reply(CreateJSONResponse("OK")); }, @@ -203,6 +209,13 @@ namespace llarp::rpc else if (not map) { net::DelDefaultRouteViaInterface(ep->GetIfName()); + + r->ForEachPeer( + [r](auto session, auto) mutable { + const auto ip = session->GetRemoteEndpoint().toIP(); + r->routePoker().DelRoute(ip); + }, + false); ep->UnmapExitRange(range); } reply(CreateJSONResponse("OK")); diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index 255688373..a04692c99 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -190,6 +190,14 @@ namespace llarp auto obj = path::Builder::ExtractStatus(); obj["exitMap"] = m_ExitMap.ExtractStatus(); obj["identity"] = m_Identity.pub.Addr().ToString(); + + util::StatusObject authCodes; + for (const auto& [service, info] : m_RemoteAuthInfos) + { + authCodes[service.ToString()] = info.token; + } + obj["authCodes"] = authCodes; + return m_state->ExtractStatus(obj); } @@ -764,6 +772,12 @@ namespace llarp } }; + bool + Endpoint::HasExit() const + { + return not m_ExitMap.Empty(); + } + bool Endpoint::LookupNameAsync(std::string name, std::function)> handler) { diff --git a/llarp/service/endpoint.hpp b/llarp/service/endpoint.hpp index 4459447cc..11f8adfa8 100644 --- a/llarp/service/endpoint.hpp +++ b/llarp/service/endpoint.hpp @@ -360,6 +360,9 @@ namespace llarp uint64_t GetSeqNoForConvo(const ConvoTag& tag); + bool + HasExit() const; + bool SelectHop( llarp_nodedb* db, diff --git a/llarp/service/identity.cpp b/llarp/service/identity.cpp index f789d771f..a4a97776c 100644 --- a/llarp/service/identity.cpp +++ b/llarp/service/identity.cpp @@ -13,16 +13,10 @@ namespace llarp { if (!bencode_start_dict(buf)) return false; - if (!BEncodeWriteDictEntry("e", enckey, buf)) - return false; - if (!BEncodeWriteDictEntry("q", pq, buf)) - return false; if (!BEncodeWriteDictEntry("s", signkey, buf)) return false; if (!BEncodeWriteDictInt("v", version, buf)) return false; - if (!BEncodeWriteDictEntry("x", vanity, buf)) - return false; return bencode_end(buf); } @@ -30,28 +24,23 @@ namespace llarp Identity::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) { bool read = false; - if (!BEncodeMaybeReadDictEntry("e", enckey, read, key, buf)) - return false; - if (key == "q") - { - llarp_buffer_t str; - if (!bencode_read_string(buf, &str)) - return false; - if (str.sz == 3200 || str.sz == 2818) - { - pq = str.base; - return true; - } - - return false; - } if (!BEncodeMaybeReadDictEntry("s", signkey, read, key, buf)) return false; if (!BEncodeMaybeReadDictInt("v", version, read, key, buf)) return false; - if (!BEncodeMaybeReadDictEntry("x", vanity, read, key, buf)) - return false; - return read; + if (not read) + return bencode_discard(buf); + return true; + } + + void + Identity::Clear() + { + signkey.Zero(); + enckey.Zero(); + pq.Zero(); + derivedSignKey.Zero(); + vanity.Zero(); } void @@ -64,7 +53,7 @@ namespace llarp crypto->pqe_keygen(pq); if (not crypto->derive_subkey_private(derivedSignKey, signkey, 1)) { - LogError("failed to generate derived key"); + throw std::runtime_error("failed to derive subkey"); } } @@ -87,6 +76,9 @@ namespace llarp void Identity::EnsureKeys(fs::path fname, bool needBackup) { + // make sure we are empty + Clear(); + std::array tmp; llarp_buffer_t buf(tmp); @@ -137,12 +129,21 @@ namespace llarp if (!bencode_decode_dict(*this, &buf)) throw std::length_error("could not decode service identity"); + auto crypto = CryptoManager::instance(); + + // ensure that the encryption key is set + if (enckey.IsZero()) + crypto->encryption_keygen(enckey); + + // also ensure the ntru key is set + if (pq.IsZero()) + crypto->pqe_keygen(pq); + std::optional van; if (!vanity.IsZero()) van = vanity; // update pubkeys pub.Update(seckey_topublic(signkey), seckey_topublic(enckey), van); - auto crypto = CryptoManager::instance(); if (not crypto->derive_subkey_private(derivedSignKey, signkey, 1)) { throw std::runtime_error("failed to derive subkey"); diff --git a/llarp/service/identity.hpp b/llarp/service/identity.hpp index 349c2c2f5..ba8471883 100644 --- a/llarp/service/identity.hpp +++ b/llarp/service/identity.hpp @@ -55,6 +55,10 @@ namespace llarp bool Sign(Signature& sig, const llarp_buffer_t& buf) const; + + /// zero out all secret key members + void + Clear(); }; inline bool diff --git a/llarp/util/aligned.hpp b/llarp/util/aligned.hpp index fa0726466..b3c79f9f6 100644 --- a/llarp/util/aligned.hpp +++ b/llarp/util/aligned.hpp @@ -229,6 +229,18 @@ namespace llarp return m_data.cend(); } + bool + FromBytestring(llarp_buffer_t* buf) + { + if (buf->sz != sz) + { + llarp::LogError("bdecode buffer size mismatch ", buf->sz, "!=", sz); + return false; + } + memcpy(data(), buf->base, sz); + return true; + } + bool BEncode(llarp_buffer_t* buf) const { @@ -243,13 +255,7 @@ namespace llarp { return false; } - if (strbuf.sz != sz) - { - llarp::LogError("bdecode buffer size mismatch ", strbuf.sz, "!=", sz); - return false; - } - memcpy(data(), strbuf.base, sz); - return true; + return FromBytestring(&strbuf); } std::string diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c749d9cf8..42ec51e78 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -34,7 +34,6 @@ add_executable(testAll routing/test_llarp_routing_obtainexitmessage.cpp service/test_llarp_service_address.cpp test_llarp_encrypted_frame.cpp - test_llarp_router_contact.cpp util/meta/test_llarp_util_memfn.cpp util/meta/test_llarp_util_traits.cpp util/test_llarp_util_aligned.cpp @@ -78,6 +77,7 @@ add_executable(catchAll iwp/test_iwp_session.cpp service/test_llarp_service_identity.cpp test_util.cpp + test_llarp_router_contact.cpp check_main.cpp) target_link_libraries(catchAll PUBLIC liblokinet Catch2::Catch2) diff --git a/test/test_llarp_router_contact.cpp b/test/test_llarp_router_contact.cpp index e26304acf..4f3f6afaa 100644 --- a/test/test_llarp_router_contact.cpp +++ b/test/test_llarp_router_contact.cpp @@ -1,49 +1,125 @@ -#include +#include #include #include -#include #include #include -using namespace ::llarp; -using namespace ::testing; +namespace +{ + llarp::sodium::CryptoLibSodium crypto; + llarp::CryptoManager cmanager(&crypto); +} -static const byte_t DEF_VALUE[] = "unittest"; +namespace llarp +{ -struct RCTest : public test::LlarpTest<> +TEST_CASE("RouterContact Sign and Verify", "[RC][RouterContact][signature][sign][verify]") { - using RC_t = RouterContact; - using SecKey_t = SecretKey; + RouterContact rc; + + SecretKey sign; + cmanager.instance()->identity_keygen(sign); - RCTest() : oldval(NetID::DefaultValue()) - { - NetID::DefaultValue() = NetID(DEF_VALUE); - } + SecretKey encr; + cmanager.instance()->encryption_keygen(encr); - ~RCTest() - { - NetID::DefaultValue() = oldval; - } + rc.enckey = encr.toPublic(); + rc.pubkey = sign.toPublic(); - const NetID oldval; -}; + REQUIRE(rc.Sign(sign)); + REQUIRE(rc.Verify(time_now_ms())); +} -TEST_F(RCTest, TestSignVerify) +TEST_CASE("RouterContact Decode Version 1", "[RC][RouterContact][V1]") { - NetID netid(DEF_VALUE); - RC_t rc; - SecKey_t encr; - SecKey_t sign; + RouterContact rc; + + SecretKey sign; + cmanager.instance()->identity_keygen(sign); + + SecretKey encr; + cmanager.instance()->encryption_keygen(encr); + + rc.version = 1; rc.enckey = encr.toPublic(); rc.pubkey = sign.toPublic(); - ASSERT_TRUE(rc.netID == netid); - ASSERT_TRUE(rc.netID == NetID::DefaultValue()); - EXPECT_CALL(m_crypto, sign(_, sign, _)).WillOnce(Return(true)); - EXPECT_CALL(m_crypto, verify(_, _, _)).WillOnce(Return(true)); + REQUIRE(rc.Sign(sign)); + + std::array encoded_buffer; + llarp_buffer_t encoded_llarp(encoded_buffer); + + rc.BEncode(&encoded_llarp); + + encoded_llarp.sz = encoded_llarp.cur - encoded_llarp.base; + encoded_llarp.cur = encoded_llarp.base; + + RouterContact decoded_rc; + + REQUIRE(decoded_rc.BDecode(&encoded_llarp)); + + REQUIRE(decoded_rc.Verify(time_now_ms())); - ASSERT_TRUE(rc.Sign(sign)); - ASSERT_TRUE(rc.Verify(time_now_ms())); + REQUIRE(decoded_rc == rc); } + +TEST_CASE("RouterContact Decode Mixed Versions", "[RC][RouterContact]") +{ + RouterContact rc1, rc2, rc3, rc4; + + rc1.version = 0; + rc2.version = 1; + rc3.version = 0; + rc4.version = 1; + + SecretKey sign1, sign2, sign3, sign4; + cmanager.instance()->identity_keygen(sign1); + cmanager.instance()->identity_keygen(sign2); + cmanager.instance()->identity_keygen(sign3); + cmanager.instance()->identity_keygen(sign4); + + SecretKey encr1, encr2, encr3, encr4; + cmanager.instance()->encryption_keygen(encr1); + cmanager.instance()->encryption_keygen(encr2); + cmanager.instance()->encryption_keygen(encr3); + cmanager.instance()->encryption_keygen(encr4); + + rc1.enckey = encr1.toPublic(); + rc2.enckey = encr2.toPublic(); + rc3.enckey = encr3.toPublic(); + rc4.enckey = encr4.toPublic(); + rc1.pubkey = sign1.toPublic(); + rc2.pubkey = sign2.toPublic(); + rc3.pubkey = sign3.toPublic(); + rc4.pubkey = sign4.toPublic(); + + REQUIRE(rc1.Sign(sign1)); + REQUIRE(rc2.Sign(sign2)); + REQUIRE(rc3.Sign(sign3)); + REQUIRE(rc4.Sign(sign4)); + + std::vector rc_vec; + rc_vec.push_back(rc1); + rc_vec.push_back(rc2); + rc_vec.push_back(rc3); + rc_vec.push_back(rc4); + + std::array encoded_buffer; + llarp_buffer_t encoded_llarp(encoded_buffer); + + BEncodeWriteList(rc_vec.begin(), rc_vec.end(), &encoded_llarp); + encoded_llarp.sz = encoded_llarp.cur - encoded_llarp.base; + encoded_llarp.cur = encoded_llarp.base; + + std::vector rc_vec_out; + + BEncodeReadList(rc_vec_out, &encoded_llarp); + + REQUIRE(rc_vec.size() == rc_vec_out.size()); + for (size_t i=0; i<4; i++) + REQUIRE(rc_vec[i] == rc_vec_out[i]); +} + +} // namespace llarp diff --git a/test/util/test_llarp_util_str.cpp b/test/util/test_llarp_util_str.cpp index 9df647c12..b7cd15fba 100644 --- a/test/util/test_llarp_util_str.cpp +++ b/test/util/test_llarp_util_str.cpp @@ -39,25 +39,22 @@ TEST_CASE("TrimWhitespace -- negative tests", "[str][trim]") TEST_CASE("caseless comparison tests - less than", "[str][lt]") { using namespace llarp; CaselessLessThan lt; - // Workaround for gcc 5's stdlib; we can drop this crap (and drop all the `T`'s below) once we - // stop supporting it. - using T = std::tuple; auto expect_less_than = GENERATE(table({ - T{"", "1"}, - T{"1", "11"}, - T{"abc", "abcd"}, - T{"ABC", "abcd"}, - T{"abc", "ABCD"}, - T{"abc", "Abcd"}, - T{"abc", "abcD"}, - T{"abc", "abCd"}, - T{"abc", "zz"}, - T{"abc", "zzzz"}, - T{"abc", "abd"}, - T{"abc", "aBd"}, - T{"abc", "abD"}, - T{"ABC", "abd"}, - T{"abC", "abd"}, + {"", "1"}, + {"1", "11"}, + {"abc", "abcd"}, + {"ABC", "abcd"}, + {"abc", "ABCD"}, + {"abc", "Abcd"}, + {"abc", "abcD"}, + {"abc", "abCd"}, + {"abc", "zz"}, + {"abc", "zzzz"}, + {"abc", "abd"}, + {"abc", "aBd"}, + {"abc", "abD"}, + {"ABC", "abd"}, + {"abC", "abd"}, })); REQUIRE( lt(std::get<0>(expect_less_than), std::get<1>(expect_less_than)) ); REQUIRE( !lt(std::get<1>(expect_less_than), std::get<0>(expect_less_than)) ); @@ -66,13 +63,12 @@ TEST_CASE("caseless comparison tests - less than", "[str][lt]") { TEST_CASE("caseless comparison tests - equality", "[str][eq]") { using namespace llarp; CaselessLessThan lt; - using T = std::tuple; // gcc 5 workaround auto expect_equal = GENERATE(table({ - T{"1", "1"}, - T{"a", "A"}, - T{"abc", "ABC"}, - T{"abc", "aBc"}, - T{"ABC", "abc"}, + {"1", "1"}, + {"a", "A"}, + {"abc", "ABC"}, + {"abc", "aBc"}, + {"ABC", "abc"}, })); REQUIRE( !lt(std::get<0>(expect_equal), std::get<1>(expect_equal)) ); REQUIRE( !lt(std::get<1>(expect_equal), std::get<0>(expect_equal)) );