From 4758bc119a19fd37483e2dcea869fcb6527a5224 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 12 Jun 2018 12:45:12 -0400 Subject: [PATCH] more onion routing code --- doc/proto_v0.txt | 4 +- include/llarp/aligned.hpp | 2 +- include/llarp/encrypted.hpp | 8 ++ include/llarp/encrypted_frame.hpp | 1 - include/llarp/messages/relay_ack.hpp | 22 +++ include/llarp/messages/relay_commit.hpp | 3 + include/llarp/path.hpp | 68 ++++++++- llarp/path.cpp | 182 +++++++++++++++++++----- llarp/relay_ack.cpp | 77 +++++++++- 9 files changed, 317 insertions(+), 50 deletions(-) diff --git a/doc/proto_v0.txt b/doc/proto_v0.txt index d163d4f93..eb8695410 100644 --- a/doc/proto_v0.txt +++ b/doc/proto_v0.txt @@ -227,14 +227,14 @@ to encrypt a frame to a router with public key B.k A.k, s = ECKG() n = RAND(32) -S = PKE(p, A.k, B.k) +S = PKE(p, A.k, B.k, n) 1) encode and encrypt x = BE(msg) new_x = SE(S, n[0:24], x) -2) generate hmac +2) generate message authentication h = MDS(n + A.k + new_x, S) diff --git a/include/llarp/aligned.hpp b/include/llarp/aligned.hpp index 8d74b8d32..1bddb5c68 100644 --- a/include/llarp/aligned.hpp +++ b/include/llarp/aligned.hpp @@ -76,7 +76,7 @@ namespace llarp void Randomize() { - randombytes(l, sz); + randombytes(b, sz); } byte_t* diff --git a/include/llarp/encrypted.hpp b/include/llarp/encrypted.hpp index 0af903746..3504eb585 100644 --- a/include/llarp/encrypted.hpp +++ b/include/llarp/encrypted.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace llarp { @@ -20,6 +21,13 @@ namespace llarp return bencode_write_bytestring(buf, data, size); } + void + Randomize() + { + if(data) + randombytes(data, size); + } + bool BDecode(llarp_buffer_t* buf) { diff --git a/include/llarp/encrypted_frame.hpp b/include/llarp/encrypted_frame.hpp index f36ca6a05..38be82701 100644 --- a/include/llarp/encrypted_frame.hpp +++ b/include/llarp/encrypted_frame.hpp @@ -90,7 +90,6 @@ namespace llarp ctx->result(ctx->target->Buffer(), ctx->context); else ctx->result(nullptr, ctx->context); - delete ctx->target; } AsyncFrameDecrypter(llarp_crypto* c, byte_t* secretkey, DecryptHandler h) diff --git a/include/llarp/messages/relay_ack.hpp b/include/llarp/messages/relay_ack.hpp index 4c14c6026..8ee5a492d 100644 --- a/include/llarp/messages/relay_ack.hpp +++ b/include/llarp/messages/relay_ack.hpp @@ -1,11 +1,33 @@ #ifndef LLARP_MESSAGES_RELAY_ACK_HPP #define LLARP_MESSAGES_RELAY_ACK_HPP +#include +#include #include +#include namespace llarp { + struct LR_AckRecord + { + PubKey pubkey; + TunnelNonce nonce; + PathID_t rxPathID; + uint64_t version = 0; + + bool + BEncode(llarp_buffer_t* buf) const; + + bool + BDecode(llarp_buffer_t* buf); + }; + struct LR_AckMessage : public ILinkMessage { + std::vector< EncryptedFrame > acks; + EncryptedFrame lasthopFrame; + PathID_t txPathID; + uint64_t version = 0; + LR_AckMessage(const RouterID& from); ~LR_AckMessage(); diff --git a/include/llarp/messages/relay_commit.hpp b/include/llarp/messages/relay_commit.hpp index 2dc78dcdd..ba2453c79 100644 --- a/include/llarp/messages/relay_commit.hpp +++ b/include/llarp/messages/relay_commit.hpp @@ -50,6 +50,9 @@ namespace llarp std::vector< EncryptedAck > acks; uint64_t version; + LR_CommitMessage() : ILinkMessage() + { + } LR_CommitMessage(const RouterID &from) : ILinkMessage(from) { } diff --git a/include/llarp/path.hpp b/include/llarp/path.hpp index 4164e2a93..5189b3f18 100644 --- a/include/llarp/path.hpp +++ b/include/llarp/path.hpp @@ -63,6 +63,8 @@ namespace llarp struct TransitHop { + TransitHop() = default; + SharedSecret rxKey; SharedSecret txKey; llarp_time_t started; @@ -218,14 +220,76 @@ namespace llarp bool HandleRelayAck(const LR_AckMessage* msg); + void + PutPendingRelayCommit(const RouterID& router, const PathID_t& txid, + const TransitHopInfo& info, const TransitHop& hop); + + bool + HasPendingRelayCommit(const RouterID& upstream, const PathID_t& txid); + + bool + ForwardLRCM(const RouterID& nextHop, std::deque< EncryptedFrame >& frames, + std::deque< EncryptedAck >& acks, EncryptedFrame& lastFrame); + + bool + HopIsUs(const PubKey& k) const; + typedef std::unordered_map< TransitHopInfo, TransitHop, TransitHopInfo::Hash > TransitHopsMap_t; + typedef std::pair< std::mutex, TransitHopsMap_t > SyncTransitMap_t; + + struct PendingPathKey + { + RouterID upstream; + PathID_t txID; + + PendingPathKey(const RouterID& up, const PathID_t& id) + : upstream(up), txID(id) + { + } + + bool + operator==(const PendingPathKey& other) const + { + return upstream == other.upstream && txID == other.txID; + } + + struct Hash + { + std::size_t + operator()(PendingPathKey const& a) const + { + std::size_t idx0, idx1; + memcpy(&idx0, a.upstream, sizeof(std::size_t)); + memcpy(&idx1, a.txID, sizeof(std::size_t)); + return idx0 ^ idx1; + } + }; + }; + + typedef std::pair< TransitHopInfo, TransitHop > PendingCommit_t; + + typedef std::pair< std::mutex, + std::unordered_map< PendingPathKey, PendingCommit_t, + PendingPathKey::Hash > > + SyncPendingCommitMap_t; + + llarp_threadpool* + Worker(); + + llarp_crypto* + Crypto(); + + byte_t* + EncryptionSecretKey(); + private: llarp_router* m_Router; - std::mutex m_TransitPathsMutex; - TransitHopsMap_t m_TransitPaths; + SyncTransitMap_t m_TransitPaths; + SyncPendingCommitMap_t m_WaitingForAcks; + bool m_AllowTransit; }; } diff --git a/llarp/path.cpp b/llarp/path.cpp index 92158d65a..52fdbc51f 100644 --- a/llarp/path.cpp +++ b/llarp/path.cpp @@ -1,3 +1,4 @@ +#include #include #include #include "router.hpp" @@ -23,26 +24,24 @@ namespace llarp { typedef AsyncFrameDecrypter< LRCMFrameDecrypt > Decrypter; Decrypter* decrypter; - std::vector< EncryptedFrame > leftovers; - EncryptedFrame ourFrame; + std::deque< EncryptedFrame > frames; + std::deque< EncryptedAck > acks; + EncryptedFrame lastFrame; PathContext* context; RouterID from; LR_CommitRecord record; LRCMFrameDecrypt(PathContext* ctx, Decrypter* dec, const LR_CommitMessage* commit) - : decrypter(dec), context(ctx), from(commit->remote) + : decrypter(dec) + , lastFrame(commit->lasthopFrame) + , context(ctx) + , from(commit->remote) { - auto sz = commit->frames.size(); - size_t idx = 0; - while(idx < sz) - { - if(sz == 0) - ourFrame = commit->frames[idx]; - else - leftovers.push_back(commit->frames[idx]); - ++idx; - } + for(const auto& f : commit->frames) + frames.push_front(f); + for(const auto& a : commit->acks) + acks.push_front(a); } ~LRCMFrameDecrypt() @@ -53,30 +52,62 @@ namespace llarp static void HandleDecrypted(llarp_buffer_t* buf, LRCMFrameDecrypt* self) { - if(buf) + if(!buf) + { + llarp::Error("LRCM decrypt failed from ", self->from); + delete self; + return; + } + llarp::Debug("decrypted LRCM from ", self->from); + // successful decrypt + if(!self->record.BDecode(buf)) { - llarp::Debug("decrypted LRCM from ", self->from); - // successful decrypt - if(self->record.BDecode(buf)) - { - TransitHopInfo info(self->from, self->record); - if(self->context->HasTransitHop(info)) - { - // duplicate hop - llarp::Warn("duplicate transit hop ", info); - } - else - { - // accepted - return; - } - } - else - llarp::Error("malformed LR Commit Record from ", self->from); + llarp::Error("malformed frame inside LRCM from ", self->from); + delete self; + return; + } + TransitHopInfo info(self->from, self->record); + if(self->context->HasTransitHop(info)) + { + llarp::Error("duplicate transit hop ", info); + delete self; + return; + } + TransitHop hop; + // choose rx id + // TODO: check for duplicates + info.rxID.Randomize(); + + // generate tx key as we are in a worker thread + auto DH = self->context->Crypto()->dh_server; + if(!DH(hop.txKey, self->record.commkey, + self->context->EncryptionSecretKey(), self->record.tunnelNonce)) + { + llarp::Error("LRCM DH Failed ", info); + delete self; + return; + } + if(self->context->HopIsUs(self->record.nextHop)) + { + // we are the farthest hop + llarp::Info("We are the farthest hop for ", info); } else - llarp::Debug("malformed frame inside LRCM from ", self->from); - delete self; + { + llarp::Info("Accepted ", info); + self->context->PutPendingRelayCommit(info.upstream, info.txID, info, + hop); + size_t sz = self->frames.front().size; + // we pop the front element it was ours + self->frames.pop_front(); + // put random on the end + // TODO: should this be an encrypted frame? + self->frames.emplace_back(sz); + self->frames.back().Randomize(); + // forward upstream + self->context->ForwardLRCM(info.upstream, self->frames, self->acks, + self->lastFrame); + } } }; @@ -101,16 +132,93 @@ namespace llarp LRCMFrameDecrypt* frames = new LRCMFrameDecrypt(this, decrypter, commit); // decrypt frames async - decrypter->AsyncDecrypt(m_Router->tp, &frames->ourFrame, frames); + decrypter->AsyncDecrypt(m_Router->tp, &frames->frames.front(), frames); return true; } + llarp_threadpool* + PathContext::Worker() + { + return m_Router->tp; + } + + llarp_crypto* + PathContext::Crypto() + { + return &m_Router->crypto; + } + + byte_t* + PathContext::EncryptionSecretKey() + { + return m_Router->encryption; + } + + bool + PathContext::HopIsUs(const PubKey& k) const + { + return memcmp(k, m_Router->pubkey(), PUBKEYSIZE) == 0; + } + + bool + PathContext::ForwardLRCM(const RouterID& nextHop, + std::deque< EncryptedFrame >& frames, + std::deque< EncryptedAck >& acks, + EncryptedFrame& lastHop) + { + LR_CommitMessage* msg = new LR_CommitMessage; + while(frames.size()) + { + msg->frames.push_back(frames.front()); + frames.pop_front(); + } + while(acks.size()) + { + msg->acks.push_back(acks.front()); + acks.pop_front(); + } + msg->lasthopFrame = lastHop; + return m_Router->SendToOrQueue(nextHop, {msg}); + } + + template < typename Map_t, typename Value_t > + bool + MapHas(Map_t& map, const Value_t& val) + { + std::unique_lock< std::mutex > lock(map.first); + return map.second.find(val) != map.second.end(); + } + + template < typename Map_t, typename Key_t, typename Value_t > + void + MapPut(Map_t& map, const Key_t& k, const Value_t& v) + { + std::unique_lock< std::mutex > lock(map.first); + map.second[k] = v; + } + bool PathContext::HasTransitHop(const TransitHopInfo& info) { - std::unique_lock< std::mutex > lock(m_TransitPathsMutex); - return m_TransitPaths.find(info) != m_TransitPaths.end(); + return MapHas(m_TransitPaths, info); + } + + void + PathContext::PutPendingRelayCommit(const RouterID& upstream, + const PathID_t& txid, + const TransitHopInfo& info, + const TransitHop& hop) + { + MapPut(m_WaitingForAcks, PendingPathKey(upstream, txid), + std::make_pair(info, hop)); + } + + bool + PathContext::HasPendingRelayCommit(const RouterID& upstream, + const PathID_t& txid) + { + return MapHas(m_WaitingForAcks, PendingPathKey(upstream, txid)); } TransitHopInfo::TransitHopInfo(const RouterID& down, diff --git a/llarp/relay_ack.cpp b/llarp/relay_ack.cpp index f656c0bd9..54a0c290e 100644 --- a/llarp/relay_ack.cpp +++ b/llarp/relay_ack.cpp @@ -1,28 +1,91 @@ #include -namespace llarp +#include "router.hpp" + +namespace llarp { - LR_AckMessage::LR_AckMessage(const RouterID & from) : ILinkMessage(from) + bool + LR_AckRecord::BEncode(llarp_buffer_t* buf) const + { + return false; + } + + bool + LR_AckRecord::BDecode(llarp_buffer_t* buf) { + return false; + } + LR_AckMessage::LR_AckMessage(const RouterID& from) : ILinkMessage(from) + { } LR_AckMessage::~LR_AckMessage() { - } - bool LR_AckMessage::BEncode(llarp_buffer_t * buf) const + bool + LR_AckMessage::BEncode(llarp_buffer_t* buf) const { return false; } - bool LR_AckMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t * buf) + bool + LR_AckMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf) { return false; } - bool LR_AckMessage::HandleMessage(llarp_router * router) const + struct LRAM_Decrypt { - return false; + typedef AsyncFrameDecrypter< LRAM_Decrypt > Decrypter; + + llarp_router* router; + Decrypter* decrypt; + std::vector< EncryptedFrame > frames; + LR_AckRecord record; + + LRAM_Decrypt(llarp_router* r, byte_t* seckey, + const std::vector< EncryptedFrame >& f) + : router(r), frames(f) + { + decrypt = new Decrypter(&r->crypto, seckey, &Decrypted); + } + + ~LRAM_Decrypt() + { + delete decrypt; + } + + static void + Decrypted(llarp_buffer_t* buf, LRAM_Decrypt* self) + { + if(!buf) + { + llarp::Error("Failed to decrypt LRAM frame"); + delete self; + return; + } + if(!self->record.BDecode(buf)) + { + llarp::Error("LRAR invalid format"); + delete self; + return; + } + } + }; + + bool + LR_AckMessage::HandleMessage(llarp_router* router) const + { + if(!router->paths.HasPendingRelayCommit(remote, txPathID)) + { + llarp::Warn("got LRAM from ", remote, + " with no previous LRCM txid=", txPathID); + return false; + } + // TODO: use different private key for different path contexts as client + LRAM_Decrypt* lram = new LRAM_Decrypt(router, router->encryption, acks); + lram->decrypt->AsyncDecrypt(router->tp, &lram->frames[0], lram); + return true; } } \ No newline at end of file