session renegotiation, RC expiration, more utp unit tests, network isolation.

pull/174/head
Jeff Becker 6 years ago
parent 22c9a0c814
commit cca19290de
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -135,7 +135,7 @@ router's full identity
{
a: [ one, or, many, AI, here ... ],
i: "<max 16 bytes network identifier>",
i: "<max 8 bytes network identifier>",
k: "<32 bytes public long term identity signing key>",
n: "<optional max 32 bytes router nickname>",
p: "<32 bytes public path encryption key>",

@ -63,7 +63,7 @@ namespace llarp
{
if(!item.BDecode(buf))
{
llarp::LogWarnTag("llarp/BEncode.hpp", "failed to decode key ", k,
llarp::LogWarnTag("llarp/bencode.hpp", "failed to decode key ", k,
" for entry in dict");
return false;
@ -261,7 +261,9 @@ namespace llarp
return true;
if(DecodeKey(*k, val))
return true;
llarp::LogError("unhandled key '", *k->cur, "'");
llarp::LogWarnTag("llarp/bencode.hpp", "undefined key '", *k->cur,
"' for entry in dict");
return false;
}

@ -82,7 +82,7 @@ namespace llarp
{
crypto = std::unique_ptr< llarp::Crypto >(
new llarp::Crypto{llarp::Crypto::sodium{}});
nodedb = new llarp_nodedb(crypto.get());
nodedb = new llarp_nodedb(crypto.get(), router->disk);
if(!llarp_nodedb::ensure_dir(nodedb_dir.c_str()))
{
@ -129,8 +129,6 @@ namespace llarp
{
llarp::LogInfo(LLARP_VERSION, " ", LLARP_RELEASE_MOTTO);
llarp::LogInfo("starting up");
if(!this->LoadDatabase())
return -1;
llarp_ev_loop_alloc(&mainloop);
// ensure worker thread pool
@ -150,7 +148,9 @@ namespace llarp
logic = new Logic;
router = new Router(worker, mainloop, logic);
// must be done after router is made so we can use its disk io worker
if(!this->LoadDatabase())
return 1;
if(!router->Configure(config))
{
llarp::LogError("Failed to configure router");

@ -700,7 +700,7 @@ namespace llarp
bool
Validate(const RouterContact &rc) const override
{
if(!rc.Verify(parent->Crypto()))
if(!rc.Verify(parent->Crypto(), parent->Now()))
{
llarp::LogWarn("rc from lookup result is invalid");
return false;

@ -6,6 +6,7 @@ namespace llarp
ILinkLayer::ILinkLayer(const byte_t* routerEncSecret, GetRCFunc getrc,
LinkMessageHandler handler, SignBufferFunc signbuf,
SessionEstablishedHandler establishedSession,
SessionRenegotiateHandler reneg,
TimeoutHandler timeout, SessionClosedHandler closed)
: HandleMessage(handler)
, HandleTimeout(timeout)
@ -13,6 +14,7 @@ namespace llarp
, GetOurRC(getrc)
, SessionEstablished(establishedSession)
, SessionClosed(closed)
, SessionRenegotiate(reneg)
, m_RouterEncSecret(routerEncSecret)
{
}
@ -40,6 +42,29 @@ namespace llarp
}
}
bool
ILinkLayer::VisitSessionByPubkey(const byte_t* pk,
std::function< bool(ILinkSession*) > visit)
{
auto itr = m_AuthedLinks.find(pk);
if(itr != m_AuthedLinks.end())
{
return visit(itr->second.get());
}
return false;
}
void
ILinkLayer::ForEachSession(std::function< void(ILinkSession*) > visit)
{
auto itr = m_AuthedLinks.begin();
while(itr != m_AuthedLinks.end())
{
visit(itr->second.get());
++itr;
}
}
bool
ILinkLayer::Configure(llarp_ev_loop* loop, const std::string& ifname, int af,
uint16_t port)

@ -30,6 +30,13 @@ namespace llarp
/// handler of session established
using SessionEstablishedHandler = std::function< void(llarp::RouterContact) >;
/// f(new, old)
/// handler of session renegotiation
/// returns true if the new rc is valid
/// returns false otherwise and the session is terminated
using SessionRenegotiateHandler =
std::function< bool(llarp::RouterContact, llarp::RouterContact) >;
/// handles close of all sessions with pubkey
using SessionClosedHandler = std::function< void(llarp::RouterID) >;
@ -38,7 +45,8 @@ namespace llarp
ILinkLayer(const byte_t* routerEncSecret, GetRCFunc getrc,
LinkMessageHandler handler, SignBufferFunc signFunc,
SessionEstablishedHandler sessionEstablish,
TimeoutHandler timeout, SessionClosedHandler closed);
SessionRenegotiateHandler renegotiate, TimeoutHandler timeout,
SessionClosedHandler closed);
virtual ~ILinkLayer();
/// get current time via event loop
@ -57,6 +65,9 @@ namespace llarp
void
ForEachSession(std::function< void(const ILinkSession*) > visit) const;
void
ForEachSession(std::function< void(ILinkSession*) > visit);
static void
udp_tick(llarp_udp_io* udp)
{
@ -116,6 +127,10 @@ namespace llarp
bool
GetOurAddressInfo(AddressInfo& addr) const;
bool
VisitSessionByPubkey(const byte_t* pk,
std::function< bool(ILinkSession*) > visit);
virtual uint16_t
Rank() const = 0;
@ -153,6 +168,7 @@ namespace llarp
GetRCFunc GetOurRC;
SessionEstablishedHandler SessionEstablished;
SessionClosedHandler SessionClosed;
SessionRenegotiateHandler SessionRenegotiate;
private:
static void

@ -45,7 +45,7 @@ namespace llarp
std::function< const byte_t *(void) > GetPubKey;
/// get remote address
std::function< const Addr &(void) > GetRemoteEndpoint;
std::function< Addr(void) > GetRemoteEndpoint;
// get remote rc
std::function< llarp::RouterContact(void) > GetRemoteRC;
@ -58,6 +58,9 @@ namespace llarp
/// get parent link layer
std::function< ILinkLayer *(void) > GetLinkLayer;
/// renegotiate session when we have a new RC locally
std::function< bool(void) > RenegotiateSession;
};
} // namespace llarp

@ -233,8 +233,8 @@ namespace llarp
return remoteRC.pubkey;
}
const Addr&
Session::RemoteEndpoint() const
Addr
Session::RemoteEndpoint()
{
return remoteAddr;
}
@ -346,10 +346,11 @@ namespace llarp
llarp::GetRCFunc getrc, llarp::LinkMessageHandler h,
llarp::SignBufferFunc sign,
llarp::SessionEstablishedHandler established,
llarp::SessionRenegotiateHandler reneg,
llarp::TimeoutHandler timeout,
llarp::SessionClosedHandler closed)
: ILinkLayer(routerEncSecret, getrc, h, sign, established, timeout,
closed)
: ILinkLayer(routerEncSecret, getrc, h, sign, established, reneg,
timeout, closed)
{
_crypto = crypto;
_utp_ctx = utp_init(2);
@ -494,6 +495,7 @@ namespace llarp
void
LinkLayer::Stop()
{
ForEachSession([](ILinkSession* s) { s->SendClose(); });
}
bool
@ -525,11 +527,14 @@ namespace llarp
std::unique_ptr< ILinkLayer >
NewServer(llarp::Crypto* crypto, const byte_t* routerEncSecret,
llarp::GetRCFunc getrc, llarp::LinkMessageHandler h,
llarp::SignBufferFunc sign, llarp::SessionEstablishedHandler est,
llarp::TimeoutHandler timeout, llarp::SessionClosedHandler closed)
llarp::SessionEstablishedHandler est,
llarp::SessionRenegotiateHandler reneg,
llarp::SignBufferFunc sign, llarp::TimeoutHandler timeout,
llarp::SessionClosedHandler closed)
{
return std::unique_ptr< ILinkLayer >(new LinkLayer(
crypto, routerEncSecret, getrc, h, sign, est, timeout, closed));
return std::unique_ptr< ILinkLayer >(
new LinkLayer(crypto, routerEncSecret, getrc, h, sign, est, reneg,
timeout, closed));
}
std::unique_ptr< ILinkLayer >
@ -539,10 +544,12 @@ namespace llarp
&r->crypto, r->encryption, std::bind(&llarp::Router::rc, r),
std::bind(&llarp::Router::HandleRecvLinkMessageBuffer, r,
std::placeholders::_1, std::placeholders::_2),
std::bind(&llarp::Router::Sign, r, std::placeholders::_1,
std::placeholders::_2),
std::bind(&llarp::Router::OnSessionEstablished, r,
std::placeholders::_1),
std::bind(&llarp::Router::CheckRenegotiateValid, r,
std::placeholders::_1, std::placeholders::_2),
std::bind(&llarp::Router::Sign, r, std::placeholders::_1,
std::placeholders::_2),
std::bind(&llarp::Router::OnConnectTimeout, r, std::placeholders::_1),
std::bind(&llarp::Router::SessionClosed, r, std::placeholders::_1));
}
@ -594,8 +601,9 @@ namespace llarp
return this->state == eSessionReady || this->state == eLinkEstablished;
};
SendClose = std::bind(&Session::Close, this);
GetRemoteEndpoint = std::bind(&Session::RemoteEndpoint, this);
SendClose = std::bind(&Session::Close, this);
GetRemoteEndpoint = std::bind(&Session::RemoteEndpoint, this);
RenegotiateSession = std::bind(&Session::Rehandshake, this);
}
/// outbound session
@ -660,7 +668,7 @@ namespace llarp
auto buf = StackBuffer< decltype(tmp) >(tmp);
LinkIntroMessage replymsg;
replymsg.rc = parent->GetOurRC();
if(!replymsg.rc.Verify(Crypto()))
if(!replymsg.rc.Verify(Crypto(), parent->Now()))
{
llarp::LogError("our RC is invalid? closing session to", remoteAddr);
Close();
@ -754,7 +762,7 @@ namespace llarp
// build our RC
LinkIntroMessage msg;
msg.rc = parent->GetOurRC();
if(!msg.rc.Verify(Crypto()))
if(!msg.rc.Verify(Crypto(), parent->Now()))
{
llarp::LogError("our RC is invalid? closing session to", remoteAddr);
Close();
@ -926,9 +934,7 @@ namespace llarp
// key'd hash
if(!Crypto()->hmac(buf.data(), payload, txKey))
return false;
if(msgid)
return MutateKey(txKey, A);
return true;
return MutateKey(txKey, A);
}
void
@ -940,9 +946,53 @@ namespace llarp
{
parent->MapAddr(remoteRC.pubkey.data(), this);
parent->SessionEstablished(remoteRC);
/// future LIM are used for session renegotiation
GotLIM = std::bind(&Session::GotSessionRenegotiate, this,
std::placeholders::_1);
}
}
bool
Session::GotSessionRenegotiate(const LinkIntroMessage* msg)
{
// check with parent and possibly process and store new rc
if(!parent->SessionRenegotiate(msg->rc, remoteRC))
{
// failed to renegotiate
Close();
return false;
}
// set remote rc
remoteRC = msg->rc;
// recalcuate rx key
return DoKeyExchange(Crypto()->transport_dh_server, rxKey, msg->N,
remoteRC.enckey, parent->RouterEncryptionSecret());
}
bool
Session::Rehandshake()
{
byte_t tmp[LinkIntroMessage::MaxSize];
LinkIntroMessage lim;
lim.rc = parent->GetOurRC();
lim.N.Randomize();
lim.P = 60 * 1000 * 10;
if(!lim.Sign(parent->Sign))
return false;
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
if(!lim.BEncode(&buf))
return false;
// rewind and resize buffer
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
// send message
if(!SendMessageBuffer(buf))
return false;
// regen our tx Key
return DoKeyExchange(Crypto()->transport_dh_client, txKey, lim.N,
remoteRC.enckey, parent->RouterEncryptionSecret());
}
bool
Session::VerifyThenDecrypt(const byte_t* ptr)
{
@ -1019,6 +1069,13 @@ namespace llarp
}
// determine if this message is done
bool result = true;
// mutate key
if(!MutateKey(rxKey, A))
{
llarp::LogError("failed to mutate rx key");
return false;
}
if(remaining == 0)
{
llarp_buffer_t buf = itr->second.buffer;
@ -1032,15 +1089,6 @@ namespace llarp
// get rid of message buffer
itr = m_RecvMsgs.erase(itr);
}
// mutate key
if(msgid)
{
if(!MutateKey(rxKey, A))
{
llarp::LogError("failed to mutate rx key");
return false;
}
}
return result;
}

@ -13,8 +13,9 @@ namespace llarp
std::unique_ptr< ILinkLayer >
NewServer(llarp::Crypto* crypto, const byte_t* routerEncSecret,
llarp::GetRCFunc getrc, llarp::LinkMessageHandler h,
llarp::SessionEstablishedHandler est, llarp::SignBufferFunc sign,
llarp::TimeoutHandler timeout,
llarp::SessionEstablishedHandler est,
llarp::SessionRenegotiateHandler reneg,
llarp::SignBufferFunc sign, llarp::TimeoutHandler timeout,
llarp::SessionClosedHandler closed);
std::unique_ptr< ILinkLayer >

@ -162,6 +162,14 @@ namespace llarp
Session();
~Session();
/// handle LIM after handshake
bool
GotSessionRenegotiate(const LinkIntroMessage* msg);
/// re negotiate session with our new local RC
bool
Rehandshake();
/// pump tx queue
void
PumpWrite();
@ -234,8 +242,8 @@ namespace llarp
RemotePubKey() const;
/// get remote address
const Addr&
RemoteEndpoint() const;
Addr
RemoteEndpoint();
/// get parent link
ILinkLayer*
@ -279,6 +287,7 @@ namespace llarp
llarp::GetRCFunc getrc, llarp::LinkMessageHandler h,
llarp::SignBufferFunc sign,
llarp::SessionEstablishedHandler established,
llarp::SessionRenegotiateHandler reneg,
llarp::TimeoutHandler timeout,
llarp::SessionClosedHandler closed);

@ -154,7 +154,7 @@ namespace llarp
return false;
}
// verify RC
if(!rc.Verify(c))
if(!rc.Verify(c, llarp::time_now_ms()))
{
llarp::LogError("invalid RC in link intro");
return false;

@ -66,6 +66,31 @@ llarp_nodedb::getRCFilePath(const byte_t *pubkey) const
return filepath.string();
}
struct async_insert_rc
{
llarp_nodedb *nodedb;
llarp::RouterContact rc;
async_insert_rc(llarp_nodedb *n, const llarp::RouterContact &r)
: nodedb(n), rc(r)
{
}
};
static void
handle_async_insert_rc(void *u)
{
async_insert_rc *job = static_cast< async_insert_rc * >(u);
job->nodedb->Insert(job->rc);
delete job;
}
void
llarp_nodedb::InsertAsync(llarp::RouterContact rc)
{
async_insert_rc *ctx = new async_insert_rc(this, rc);
llarp_threadpool_queue_job(disk, {ctx, &handle_async_insert_rc});
}
/// insert and write to disk
bool
llarp_nodedb::Insert(const llarp::RouterContact &rc)
@ -144,7 +169,7 @@ llarp_nodedb::loadfile(const fs::path &fpath)
llarp::LogError("failed to read file ", fpath);
return false;
}
if(!rc.Verify(crypto))
if(!rc.Verify(crypto, llarp::time_now_ms()))
{
llarp::LogError(fpath, " contains invalid RC");
return false;
@ -231,7 +256,8 @@ crypto_threadworker_verifyrc(void *user)
llarp_async_verify_rc *verify_request =
static_cast< llarp_async_verify_rc * >(user);
llarp::RouterContact rc = verify_request->rc;
verify_request->valid = rc.Verify(verify_request->nodedb->crypto);
verify_request->valid =
rc.Verify(verify_request->nodedb->crypto, llarp::time_now_ms());
// if it's valid we need to set it
if(verify_request->valid && rc.IsPublicRouter())
{

@ -28,7 +28,8 @@ struct llarp_nodedb_iter
struct llarp_nodedb
{
llarp_nodedb(llarp::Crypto *c) : crypto(c)
llarp_nodedb(llarp::Crypto *c, llarp_threadpool *diskworker)
: crypto(c), disk(diskworker)
{
}
@ -38,6 +39,7 @@ struct llarp_nodedb
}
llarp::Crypto *crypto;
llarp_threadpool *disk;
llarp::util::Mutex access;
std::unordered_map< llarp::RouterID, llarp::RouterContact,
llarp::RouterID::Hash >
@ -63,6 +65,10 @@ struct llarp_nodedb
bool
Insert(const llarp::RouterContact &rc);
/// insert and write to disk in background
void
InsertAsync(llarp::RouterContact rc);
ssize_t
Load(const fs::path &path);

@ -182,6 +182,7 @@ namespace llarp
Router::OnSessionEstablished(llarp::RouterContact rc)
{
async_verify_RC(rc, nullptr);
llarp::LogInfo("session with ", rc.pubkey, "established");
}
Router::Router(struct llarp_threadpool *_tp, struct llarp_ev_loop *_netloop,
@ -321,16 +322,14 @@ namespace llarp
{
return;
}
if(results[0].Verify(&crypto))
if(results[0].Verify(&crypto, Now()))
{
nodedb->Insert(results[0]);
llarp_router_try_connect(this, results[0], 10);
return;
}
}
else
{
DiscardOutboundFor(remote);
}
DiscardOutboundFor(remote);
}
void
@ -346,6 +345,17 @@ namespace llarp
}
}
void
Router::ForEachPeer(std::function< void(llarp::ILinkSession *) > visit)
{
outboundLink->ForEachSession(
[visit](llarp::ILinkSession *peer) { visit(peer); });
for(const auto &link : inboundLinks)
{
link->ForEachSession([visit](llarp::ILinkSession *peer) { visit(peer); });
}
}
void
Router::try_connect(fs::path rcfile)
{
@ -355,7 +365,7 @@ namespace llarp
llarp::LogError("failure to decode or verify of remote RC");
return;
}
if(remote.Verify(&crypto))
if(remote.Verify(&crypto, Now()))
{
llarp::LogDebug("verified signature");
// store into filesystem
@ -421,7 +431,7 @@ namespace llarp
Router::SaveRC()
{
llarp::LogDebug("verify RC signature");
if(!rc().Verify(&crypto))
if(!rc().Verify(&crypto, Now()))
{
rc().Dump< MAX_RC_SIZE >();
llarp::LogError("RC is invalid, not saving");
@ -545,7 +555,7 @@ namespace llarp
return;
for(const auto &rc : results)
{
if(rc.Verify(&crypto))
if(rc.Verify(&crypto, Now()))
nodedb->Insert(rc);
else
return;
@ -625,11 +635,68 @@ namespace llarp
return validRouters.size();
}
bool
Router::UpdateOurRC(bool rotateKeys)
{
llarp::SecretKey nextOnionKey;
llarp::RouterContact nextRC = _rc;
if(rotateKeys)
{
crypto.encryption_keygen(nextOnionKey);
nextRC.enckey = llarp::seckey_topublic(nextOnionKey);
}
nextRC.last_updated = Now();
if(!nextRC.Sign(&crypto, identity))
return false;
_rc = nextRC;
if(rotateKeys)
{
encryption = nextOnionKey;
// propagate RC by renegotiating sessions
ForEachPeer([&](llarp::ILinkSession *s) {
if(!s->RenegotiateSession())
{
llarp::LogWarn("failed to renegotiate session with ",
s->GetRemoteEndpoint());
}
});
}
// TODO: do this async
return SaveRC();
}
bool
Router::CheckRenegotiateValid(RouterContact newrc, RouterContact oldrc)
{
// missmatch of identity ?
if(newrc.pubkey != oldrc.pubkey)
return false;
// store it in nodedb async
nodedb->InsertAsync(newrc);
// update valid routers
{
auto itr = validRouters.find(newrc.pubkey);
if(itr == validRouters.end())
validRouters[newrc.pubkey] = newrc;
else
itr->second = newrc;
}
// TODO: check for other places that need updating the RC
return true;
}
void
Router::Tick()
{
// llarp::LogDebug("tick router");
auto now = llarp_ev_loop_time_now_ms(netloop);
if(_rc.ExpiresSoon(now))
{
if(!UpdateOurRC())
llarp::LogError("Failed to update our RC");
}
paths.TickPaths(now);
paths.ExpirePaths(now);
{
@ -950,7 +1017,8 @@ namespace llarp
// TODO: enable this once the network can serialize xi
//_rc.exits.emplace_back(_rc.pubkey, a);
llarp::LogInfo(
"Neato tehl33toh, You are a freaking exit relay. w00t!!!!! your exit "
"Neato tehl33toh, You are a freaking exit relay. w00t!!!!! your "
"exit "
"is advertised as exiting at ",
a);
}
@ -1316,10 +1384,16 @@ namespace llarp
{
self->bootstrapRCList.emplace_back();
auto &rc = self->bootstrapRCList.back();
if(rc.Read(val) && rc.Verify(&self->crypto))
if(rc.Read(val) && rc.Verify(&self->crypto, self->Now()))
{
llarp::LogInfo("Added bootstrap node ", RouterID(rc.pubkey.data()));
}
else if(self->Now() - rc.last_updated > RouterContact::Lifetime)
{
llarp::LogWarn("Bootstrap node ", RouterID(rc.pubkey.data()),
" is too old and needs to be refreshed");
self->bootstrapRCList.pop_back();
}
else
{
llarp::LogError("malformed rc file: ", val);

@ -304,6 +304,16 @@ namespace llarp
ForEachPeer(
std::function< void(const llarp::ILinkSession *, bool) > visit) const;
void
ForEachPeer(std::function< void(llarp::ILinkSession *) > visit);
/// check if newRc matches oldRC and update local rc for this remote contact
/// if valid
/// returns true on valid and updated
/// returns false otherwise
bool
CheckRenegotiateValid(RouterContact newRc, RouterContact oldRC);
/// flush outbound message queue
void
FlushOutbound();
@ -368,6 +378,9 @@ namespace llarp
HandleAsyncLoadRCForSendTo(llarp_async_load_rc *async);
private:
bool
UpdateOurRC(bool rotateKeys = true);
template < typename Config >
void
mergeHiddenServiceConfig(const Config &in, Config &out)

@ -14,6 +14,46 @@ namespace llarp
{
bool RouterContact::IgnoreBogons = false;
/// 1 hour
llarp_time_t RouterContact::Lifetime = 60 * 60 * 1000;
NetID::NetID() : AlignedBuffer< 8 >((const byte_t *)LLARP_NET_ID)
{
}
bool
NetID::operator==(const NetID &other) const
{
return memcmp(data(), other.data(), size()) == 0;
}
std::string
NetID::ToString() const
{
size_t l = strnlen((const char *)data(), size());
return std::string((const char *)data(), l);
}
bool
NetID::BDecode(llarp_buffer_t *buf)
{
Zero();
llarp_buffer_t strbuf;
if(!bencode_read_string(buf, &strbuf))
return false;
if(strbuf.sz > 8)
return false;
memcpy(data(), strbuf.base, strbuf.sz);
return true;
}
bool
NetID::BEncode(llarp_buffer_t *buf) const
{
size_t l = strnlen((const char *)data(), size());
return bencode_write_bytestring(buf, data(), l);
}
bool
RouterContact::BEncode(llarp_buffer_t *buf) const
{
@ -27,6 +67,12 @@ namespace llarp
if(!BEncodeWriteList(addrs.begin(), addrs.end(), buf))
return false;
/* write netid */
if(!bencode_write_bytestring(buf, "i", 1))
return false;
if(!bencode_write_bytestring(buf, netID.data(), netID.size()))
return false;
/* write signing pubkey */
if(!bencode_write_bytestring(buf, "k", 1))
return false;
@ -50,7 +96,7 @@ namespace llarp
return false;
/* write last updated */
if(!bencode_write_bytestring(buf, "u", 1))
if(!bencode_write_bytestring(buf, "t", 1))
return false;
if(!bencode_write_uint64(buf, last_updated))
return false;
@ -92,6 +138,9 @@ namespace llarp
if(!BEncodeMaybeReadDictList("a", addrs, read, key, buf))
return false;
if(!BEncodeMaybeReadDictEntry("i", netID, read, key, buf))
return false;
if(!BEncodeMaybeReadDictEntry("k", pubkey, read, key, buf))
return false;
@ -113,7 +162,7 @@ namespace llarp
if(!BEncodeMaybeReadDictInt("v", version, read, key, buf))
return false;
if(!BEncodeMaybeReadDictInt("u", last_updated, read, key, buf))
if(!BEncodeMaybeReadDictInt("t", last_updated, read, key, buf))
return false;
if(!BEncodeMaybeReadDictList("x", exits, read, key, buf))
@ -144,6 +193,18 @@ namespace llarp
memcpy(nickname, nick.c_str(), std::min(nick.size(), nickname.size()));
}
bool
RouterContact::IsExpired(llarp_time_t now) const
{
return now > last_updated && now - last_updated >= Lifetime;
}
bool
RouterContact::ExpiresSoon(llarp_time_t now, llarp_time_t dlt) const
{
return now - (last_updated + Lifetime) >= dlt;
}
std::string
RouterContact::Nick() const
{
@ -167,8 +228,12 @@ namespace llarp
}
bool
RouterContact::Verify(llarp::Crypto *crypto) const
RouterContact::Verify(llarp::Crypto *crypto, llarp_time_t now) const
{
if(netID.ToString() != LLARP_NET_ID)
return false;
if(IsExpired(now))
return false;
for(const auto &a : addrs)
{
if(IsBogon(a.ip) && !IgnoreBogons)

@ -5,6 +5,7 @@
#include <bencode.hpp>
#include <crypto.h>
#include <exit_info.hpp>
#include <version.hpp>
#include <vector>
@ -13,11 +14,44 @@
namespace llarp
{
/// NetID
struct NetID final : public AlignedBuffer< 8 >
{
NetID();
bool
operator==(const NetID &other) const;
bool
operator!=(const NetID &other) const
{
return !(*this == other);
}
friend std::ostream &
operator<<(std::ostream &out, const NetID &id)
{
return out << id.ToString();
}
std::string
ToString() const;
bool
BDecode(llarp_buffer_t *buf);
bool
BEncode(llarp_buffer_t *buf) const;
};
/// RouterContact
struct RouterContact final : public IBEncodeMessage
{
/// for unit tests
static bool IgnoreBogons;
static llarp_time_t Lifetime;
RouterContact() : IBEncodeMessage()
{
Clear();
@ -26,6 +60,7 @@ namespace llarp
RouterContact(const RouterContact &other)
: IBEncodeMessage()
, addrs(other.addrs)
, netID(other.netID)
, enckey(other.enckey)
, pubkey(other.pubkey)
, exits(other.exits)
@ -38,6 +73,8 @@ namespace llarp
// advertised addresses
std::vector< AddressInfo > addrs;
// network identifier
NetID netID;
// public encryption public key
llarp::PubKey enckey;
// public signing public key
@ -59,7 +96,8 @@ namespace llarp
{
return addrs == other.addrs && enckey == other.enckey
&& pubkey == other.pubkey && signature == other.signature
&& nickname == other.nickname && last_updated == other.last_updated;
&& nickname == other.nickname && last_updated == other.last_updated
&& netID == other.netID;
}
void
@ -97,11 +135,18 @@ namespace llarp
SetNick(const std::string &nick);
bool
Verify(llarp::Crypto *crypto) const;
Verify(llarp::Crypto *crypto, llarp_time_t now) const;
bool
Sign(llarp::Crypto *crypto, const llarp::SecretKey &secret);
/// does this RC expire soon? default delta is 1 minute
bool
ExpiresSoon(llarp_time_t now, llarp_time_t dlt = 60000) const;
bool
IsExpired(llarp_time_t now) const;
bool
OtherIsNewer(const RouterContact &other) const
{

@ -29,4 +29,8 @@
#define LLARP_RELEASE_MOTTO "(dev build)"
#endif
#ifndef LLARP_NET_ID
#define LLARP_NET_ID "testnet"
#endif
#endif

@ -43,6 +43,15 @@ struct UTPTest : public ::testing::Test
return rc.pubkey;
}
/// regenerate rc and rotate onion key
bool
Regen()
{
crypto->encryption_keygen(encryptionKey);
rc.enckey = llarp::seckey_topublic(encryptionKey);
return rc.Sign(crypto, signingKey);
}
std::unique_ptr< Link_t > link;
bool
@ -84,6 +93,8 @@ struct UTPTest : public ::testing::Test
llarp_ev_loop* netLoop;
std::unique_ptr< llarp::Logic > logic;
llarp_time_t oldRCLifetime;
UTPTest()
: crypto(llarp::Crypto::sodium{})
, Alice(crypto)
@ -95,7 +106,9 @@ struct UTPTest : public ::testing::Test
void
SetUp()
{
oldRCLifetime = llarp::RouterContact::Lifetime;
llarp::RouterContact::IgnoreBogons = true;
llarp::RouterContact::Lifetime = 500;
llarp_ev_loop_alloc(&netLoop);
logic.reset(new llarp::Logic());
}
@ -108,6 +121,7 @@ struct UTPTest : public ::testing::Test
logic.reset();
llarp_ev_loop_free(&netLoop);
llarp::RouterContact::IgnoreBogons = false;
llarp::RouterContact::Lifetime = oldRCLifetime;
}
static void
@ -134,17 +148,12 @@ struct UTPTest : public ::testing::Test
bool AliceGotMessage(llarp_buffer_t)
{
success = true;
return true;
}
bool BobGotMessage(llarp_buffer_t)
{
success = true;
Stop();
return true;
}
};
TEST_F(UTPTest, TestAliceAndBob)
TEST_F(UTPTest, TestAliceRenegWithBob)
{
Alice.link.reset(new Link_t(
&crypto, Alice.encryptionKey,
@ -152,7 +161,8 @@ TEST_F(UTPTest, TestAliceAndBob)
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
if(Alice.gotLIM)
{
return AliceGotMessage(buf);
Alice.Regen();
return s->RenegotiateSession();
}
else
{
@ -161,26 +171,86 @@ TEST_F(UTPTest, TestAliceAndBob)
return false;
if(!s->GotLIM(&msg))
return false;
Alice.gotLIM = true;
return true;
}
},
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Alice.signingKey, buf);
},
[&](llarp::RouterContact rc) { ASSERT_EQ(rc, Bob.GetRC()); },
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Bob.GetRC());
llarp::LogInfo("alice established with bob");
},
[&](llarp::RouterContact, llarp::RouterContact) -> bool { return true; },
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
Stop();
},
[&](llarp::RouterID router) { ASSERT_EQ(router, Bob.GetRouterID()); }));
auto sendDiscardMessage = [](llarp::ILinkSession* s) -> bool {
// send discard message in reply to complete unit test
byte_t tmp[32] = {0};
auto otherBuf = llarp::StackBuffer< decltype(tmp) >(tmp);
llarp::DiscardMessage discard;
if(!discard.BEncode(&otherBuf))
return false;
otherBuf.sz = otherBuf.cur - otherBuf.base;
otherBuf.cur = otherBuf.base;
return s->SendMessageBuffer(otherBuf);
};
Bob.link.reset(new Link_t(
&crypto, Bob.encryptionKey,
[&]() -> const llarp::RouterContact& { return Bob.GetRC(); },
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
if(Bob.gotLIM)
llarp::LinkIntroMessage msg;
if(!msg.BDecode(&buf))
return false;
if(!s->GotLIM(&msg))
return false;
Bob.gotLIM = true;
return sendDiscardMessage(s);
},
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Bob.signingKey, buf);
},
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Alice.GetRC());
llarp::LogInfo("bob established with alice");
Bob.link->VisitSessionByPubkey(Alice.GetRC().pubkey.data(),
sendDiscardMessage);
},
[&](llarp::RouterContact newrc, llarp::RouterContact oldrc) -> bool {
success = newrc.pubkey == oldrc.pubkey;
return true;
},
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
},
[&](llarp::RouterID router) { ASSERT_EQ(router, Alice.GetRouterID()); }));
ASSERT_TRUE(Alice.Start(logic.get(), netLoop, AlicePort));
ASSERT_TRUE(Bob.Start(logic.get(), netLoop, BobPort));
ASSERT_TRUE(Alice.link->TryEstablishTo(Bob.GetRC()));
RunMainloop();
ASSERT_TRUE(Alice.gotLIM);
ASSERT_TRUE(Bob.gotLIM);
ASSERT_TRUE(success);
}
TEST_F(UTPTest, TestAliceConnectToBob)
{
Alice.link.reset(new Link_t(
&crypto, Alice.encryptionKey,
[&]() -> const llarp::RouterContact& { return Alice.GetRC(); },
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
if(Alice.gotLIM)
{
return BobGotMessage(buf);
return AliceGotMessage(buf);
}
else
{
@ -189,14 +259,58 @@ TEST_F(UTPTest, TestAliceAndBob)
return false;
if(!s->GotLIM(&msg))
return false;
Bob.gotLIM = true;
Alice.gotLIM = true;
return true;
}
},
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Alice.signingKey, buf);
},
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Bob.GetRC());
llarp::LogInfo("alice established with bob");
},
[&](llarp::RouterContact, llarp::RouterContact) -> bool { return true; },
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
Stop();
},
[&](llarp::RouterID router) { ASSERT_EQ(router, Bob.GetRouterID()); }));
auto sendDiscardMessage = [](llarp::ILinkSession* s) -> bool {
// send discard message in reply to complete unit test
byte_t tmp[32] = {0};
auto otherBuf = llarp::StackBuffer< decltype(tmp) >(tmp);
llarp::DiscardMessage discard;
if(!discard.BEncode(&otherBuf))
return false;
otherBuf.sz = otherBuf.cur - otherBuf.base;
otherBuf.cur = otherBuf.base;
return s->SendMessageBuffer(otherBuf);
};
Bob.link.reset(new Link_t(
&crypto, Bob.encryptionKey,
[&]() -> const llarp::RouterContact& { return Bob.GetRC(); },
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
llarp::LinkIntroMessage msg;
if(!msg.BDecode(&buf))
return false;
if(!s->GotLIM(&msg))
return false;
Bob.gotLIM = true;
return true;
},
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Bob.signingKey, buf);
},
[&](llarp::RouterContact rc) { ASSERT_EQ(rc, Alice.GetRC()); },
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Alice.GetRC());
llarp::LogInfo("bob established with alice");
Bob.link->VisitSessionByPubkey(Alice.GetRC().pubkey.data(),
sendDiscardMessage);
},
[&](llarp::RouterContact, llarp::RouterContact) -> bool { return true; },
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
},

Loading…
Cancel
Save