more hidden service stuff

pull/6/head^2
Jeff Becker 6 years ago
parent 02ca1796b2
commit d38646ed54

@ -242,6 +242,7 @@ set(LIB_SRC
llarp/service/address.cpp
llarp/service/context.cpp
llarp/service/endpoint.cpp
llarp/service/frame.cpp
llarp/service/lookup.cpp
llarp/service/protocol.cpp
llarp/service/tag.cpp

@ -525,6 +525,8 @@ TODO: document this better
intro message (variant 1)
start a new session
{
A: "H",
D: "<N bytes encrypted HSD>",
@ -535,6 +537,10 @@ intro message (variant 1)
Z: "<64 bytes signature of entire message using sender's signing key>"
}
D is encrypted with session key K which is derived by
K = PKE(H, SI.enckey, N)
ordered data message (variant 2)
{
@ -552,6 +558,7 @@ data sent anonymously over the network to a recipiant from a sender.
sent inside a HSFM encrypted with a shared secret.
{
A: protocol_number_uint,
D: "<N bytes payload>",
I: Introduction for reply,
S: SI of sender,

@ -4,6 +4,7 @@
#include <llarp/crypto.hpp>
#include <llarp/encrypted.hpp>
#include <llarp/routing/message.hpp>
#include <llarp/service/protocol.hpp>
namespace llarp
{
@ -12,8 +13,8 @@ namespace llarp
struct PathTransferMessage : public IMessage
{
PathID_t P;
Encrypted T;
uint64_t V = 0;
service::ProtocolFrame* T = nullptr;
uint64_t V = 0;
TunnelNonce Y;
PathTransferMessage();

@ -81,6 +81,9 @@ namespace llarp
Path*
PickRandomEstablishedPath();
Path*
GetPathByRouter(const RouterID& router);
bool
GetCurrentIntroductions(
std::set< llarp::service::Introduction >& intros) const;

@ -43,6 +43,9 @@ namespace llarp
bool
DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf);
void
Clear();
bool
operator<(const Introduction& other) const
{

@ -36,6 +36,12 @@ namespace llarp
llarp_threadpool*
Worker();
llarp_router*
Router()
{
return m_Router;
}
bool
Start();
@ -63,8 +69,11 @@ namespace llarp
bool
ForgetPathToService(const Address& remote);
byte_t*
GetEncryptionSecretKey();
Identity*
GetIdentity()
{
return &m_Identity;
}
/// context needed to initiate an outbound hidden service session
struct OutboundContext : public llarp_pathbuilder_context
@ -74,6 +83,12 @@ namespace llarp
/// the remote hidden service's curren intro set
IntroSet currentIntroSet;
/// the current selected intro
Introduction selectedIntro;
/// update the current selected intro to be a new best introduction
void
ShiftIntroduction();
/// encrypt asynchronously and send to remote endpoint from us
void
@ -91,26 +106,17 @@ namespace llarp
private:
void
AsyncEncrypt(ProtocolMessage* msg,
std::function< void(ProtocolMessage*) > result);
void
AsyncGenIntro(ProtocolMessage* msg,
std::function< void(ProtocolMessage*) > result);
AsyncEncrypt(llarp_buffer_t payload);
/// handle key exchange done
void
HandleIntroGen(ProtocolMessage* msg);
/// send an encrypted message
AsyncGenIntro(llarp_buffer_t payload);
/// send a fully encrypted hidden service frame
void
SendMessage(ProtocolMessage* msg);
Send(ProtocolFrame& f);
uint64_t sequenceNo = 0;
llarp::SharedSecret sharedKey;
llarp::util::CoDelQueue<
ProtocolMessage*, ProtocolMessage::GetTime,
ProtocolMessage::PutTime, ProtocolMessage::Compare,
llarp::util::DummyMutex, llarp::util::DummyLock >
m_SendQueue;
Endpoint* m_Parent;
};
@ -157,6 +163,8 @@ namespace llarp
Identity m_Identity;
std::unordered_map< Address, OutboundContext*, Address::Hash >
m_RemoteSessions;
std::unordered_map< Address, PathEnsureHook, Address::Hash >
m_PendingServiceLookups;
uint64_t m_CurrentPublishTX = 0;
llarp_time_t m_LastPublish = 0;
llarp_time_t m_LastPublishAttempt = 0;

@ -0,0 +1,28 @@
#ifndef LLARP_SERVICE_FRAME_HPP
#define LLARP_SERVICE_FRAME_HPP
#include <llarp/bencode.hpp>
#include <llarp/crypto.hpp>
#include <llarp/encrypted.hpp>
namespace llarp
{
namespace service
{
struct DataFrame : public llarp::IBEncodeMessage
{
llarp::Encrypted D;
llarp::PubKey H;
llarp::KeyExchangeNonce N;
uint64_t S = 0;
llarp::Signature Z;
bool
BEncode(llarp_buffer_t* buf) const;
bool
DecodeKey(llarp_buffer_t key, llarp_buffer_t* val);
};
} // namespace service
} // namespace llarp
#endif

@ -3,27 +3,33 @@
#include <llarp/time.h>
#include <llarp/bencode.hpp>
#include <llarp/crypto.hpp>
#include <llarp/encrypted.hpp>
#include <llarp/service/Info.hpp>
#include <llarp/service/Intro.hpp>
#include <vector>
namespace llarp
{
namespace service
{
constexpr std::size_t MAX_PROTOCOL_MESSAGE_SIZE = 2048;
enum ProtocolType
{
eProtocolText = 0,
eProtocolTraffic = 1
};
/// inner message
struct ProtocolMessage : public llarp::IBEncodeMessage
{
ProtocolMessage(ProtocolType t, uint64_t seqno);
ProtocolMessage();
~ProtocolMessage();
ProtocolType proto;
llarp_time_t queued = 0;
std::vector< byte_t > payload;
llarp::KeyExchangeNonce N;
uint64_t sequenceNum;
Introduction introReply;
ServiceInfo sender;
bool
DecodeKey(llarp_buffer_t key, llarp_buffer_t* val);
@ -32,34 +38,35 @@ namespace llarp
void
PutBuffer(llarp_buffer_t payload);
};
/// outer message
struct ProtocolFrame : public llarp::IBEncodeMessage
{
llarp::Encrypted D;
uint64_t S = 0;
llarp::PubKey H;
llarp::KeyExchangeNonce N;
llarp::Signature Z;
~ProtocolFrame();
bool
EncryptAndSign(llarp_crypto* c, const ProtocolMessage* msg,
byte_t* sharedkey, byte_t* signingkey);
bool
DecryptPayloadInto(llarp_crypto* c, byte_t* sharedkey,
ProtocolMessage* into) const;
struct Compare
{
bool
operator()(const ProtocolMessage* left,
const ProtocolMessage* right) const
{
return left->sequenceNum < right->sequenceNum;
}
};
bool
DecodeKey(llarp_buffer_t key, llarp_buffer_t* val);
struct GetTime
{
llarp_time_t
operator()(const ProtocolMessage* msg) const
{
return msg->queued;
}
};
bool
BEncode(llarp_buffer_t* buf) const;
struct PutTime
{
void
operator()(ProtocolMessage* msg, llarp_time_t now) const
{
msg->queued = now;
}
};
bool
Verify(llarp_crypto* c, byte_t* signingkey);
};
} // namespace service
} // namespace llarp

@ -27,6 +27,18 @@ namespace llarp
buff.sz = t.size();
return buff;
}
template < typename T >
llarp_buffer_t
ConstBuffer(const T& t)
{
llarp_buffer_t buff;
buff.base = (byte_t*)&t[0];
buff.cur = buff.base;
buff.sz = t.size();
return buff;
}
} // namespace llarp
#endif

@ -45,6 +45,19 @@ namespace llarp
}
}
Path*
PathSet::GetPathByRouter(const RouterID& id)
{
auto itr = m_Paths.begin();
while(itr != m_Paths.end())
{
if(itr->first.first == id)
return itr->second;
++itr;
}
return nullptr;
}
size_t
PathSet::NumInStatus(PathStatus st) const
{

@ -1,4 +1,5 @@
#include <llarp/messages/path_transfer.hpp>
#include "../buffer.hpp"
#include "../router.hpp"
namespace llarp
@ -19,8 +20,13 @@ namespace llarp
bool read = false;
if(!BEncodeMaybeReadDictEntry("P", P, read, key, val))
return false;
if(!BEncodeMaybeReadDictEntry("T", T, read, key, val))
return false;
if(llarp_buffer_eq(key, "T"))
{
if(T)
delete T;
T = new service::ProtocolFrame();
return T->BDecode(val);
}
if(!BEncodeMaybeReadDictInt("V", V, read, key, val))
return false;
if(!BEncodeMaybeReadDictEntry("Y", Y, read, key, val))
@ -37,8 +43,12 @@ namespace llarp
return false;
if(!BEncodeWriteDictEntry("P", P, buf))
return false;
if(!BEncodeWriteDictEntry("T", T, buf))
if(!bencode_write_bytestring(buf, "T", 1))
return false;
if(!T->BEncode(buf))
return false;
if(!BEncodeWriteDictInt(buf, "V", LLARP_PROTO_VERSION))
return false;
if(!BEncodeWriteDictEntry("Y", Y, buf))
@ -51,15 +61,31 @@ namespace llarp
PathTransferMessage::HandleMessage(IMessageHandler* h,
llarp_router* r) const
{
auto path = r->paths.GetByUpstream(r->pubkey(), P);
if(path)
auto path = r->paths.GetByDownstream(r->pubkey(), P);
if(!path)
{
return path->HandleDownstream(T.Buffer(), Y, r);
llarp::LogWarn("No such path for path transfer pathid=", P);
return false;
}
llarp::LogWarn("No such local path for path transfer src=", from,
" dst=", P);
return false;
if(!T)
{
llarp::LogError("no data to transfer on data message");
return false;
}
byte_t tmp[service::MAX_PROTOCOL_MESSAGE_SIZE];
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
if(!T->BEncode(&buf))
{
llarp::LogWarn("failed to transfer data message, encode failed");
return false;
}
// rewind
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
// send
return path->HandleDownstream(buf, Y, r);
}
} // namespace routing
} // namespace llarp
} // namespace routing
} // namespace llarp

@ -140,6 +140,15 @@ namespace llarp
return bencode_end(buf);
}
void
Introduction::Clear()
{
router.Zero();
pathID.Zero();
latency = 0;
expiresAt = 0;
}
Identity::~Identity()
{
}

@ -300,32 +300,80 @@ namespace llarp
struct HiddenServiceAddressLookup : public IServiceLookup
{
Endpoint* endpoint;
HiddenServiceAddressLookup(Endpoint* parent) : endpoint(parent)
Address remote;
uint64_t txid;
HiddenServiceAddressLookup(Endpoint* parent, const Address& addr,
uint64_t tx)
: endpoint(parent), remote(addr), txid(tx)
{
}
bool
HandleResponse(const std::set< IntroSet >& results)
{
if(results.size() == 0)
{
auto itr = results.begin();
endpoint->PutNewOutboundContext(*itr);
}
else
if(results.size() == 1)
{
// TODO: retry request?
endpoint->PutNewOutboundContext(*results.begin());
}
delete this;
return true;
}
llarp::routing::IMessage*
BuildRequestMessage()
{
llarp::routing::DHTMessage* msg = new llarp::routing::DHTMessage();
msg->M.push_back(new llarp::dht::FindIntroMessage(remote, txid));
return msg;
}
};
void
Endpoint::PutNewOutboundContext(const llarp::service::IntroSet& introset)
{
Address addr;
introset.A.CalculateAddress(addr);
// only add new session if it's not there
if(m_RemoteSessions.find(addr) == m_RemoteSessions.end())
{
OutboundContext* ctx = new OutboundContext(introset, this);
m_RemoteSessions.insert(std::make_pair(addr, ctx));
llarp::LogInfo("Created New outbound context for ", addr.ToString());
}
// inform pending
auto itr = m_PendingServiceLookups.find(addr);
if(itr != m_PendingServiceLookups.end())
{
itr->second(m_RemoteSessions.at(addr));
m_PendingServiceLookups.erase(itr);
}
}
bool
Endpoint::EnsurePathToService(const Address& remote, PathEnsureHook hook,
llarp_time_t timeoutMS)
{
// TODO: implement me
return false;
{
auto itr = m_RemoteSessions.find(remote);
if(itr != m_RemoteSessions.end())
{
hook(itr->second);
return true;
}
}
auto itr = m_PendingServiceLookups.find(remote);
if(itr != m_PendingServiceLookups.end())
{
// duplicate
return false;
}
m_PendingServiceLookups.insert(std::make_pair(remote, hook));
HiddenServiceAddressLookup* job =
new HiddenServiceAddressLookup(this, remote, GenTXID());
m_PendingLookups.insert(std::make_pair(job->txid, job));
return true;
}
Endpoint::OutboundContext::OutboundContext(const IntroSet& intro,
@ -333,16 +381,28 @@ namespace llarp
: llarp_pathbuilder_context(parent->m_Router, parent->m_Router->dht, 2,
4)
, currentIntroSet(intro)
, m_SendQueue(parent->Name() + "::outbound_queue")
, m_Parent(parent)
{
selectedIntro.Clear();
}
Endpoint::OutboundContext::~OutboundContext()
{
}
void
Endpoint::OutboundContext::ShiftIntroduction()
{
for(const auto& intro : currentIntroSet.I)
{
if(intro.expiresAt > selectedIntro.expiresAt)
{
selectedIntro = intro;
}
}
}
bool
Endpoint::OutboundContext::HandleGotIntroMessage(
const llarp::dht::GotIntroMessage* msg)
@ -355,6 +415,7 @@ namespace llarp
if(itr->VerifySignature(crypto) && currentIntroSet.A == itr->A)
{
currentIntroSet = *itr;
ShiftIntroduction();
return true;
}
else
@ -371,68 +432,89 @@ namespace llarp
Endpoint::OutboundContext::AsyncEncryptAndSendTo(llarp_buffer_t data,
ProtocolType protocol)
{
auto sendto =
std::bind(&OutboundContext::SendMessage, this, std::placeholders::_1);
ProtocolMessage* msg = new ProtocolMessage(protocol, sequenceNo);
msg->PutBuffer(data);
if(sequenceNo)
{
AsyncEncrypt(msg, sendto);
AsyncEncrypt(data);
}
else
{
AsyncGenIntro(msg, sendto);
AsyncGenIntro(data);
}
}
struct AsyncKeyExchange
struct AsyncIntroGen
{
llarp_logic* logic;
llarp_crypto* crypto;
byte_t* sharedKey;
byte_t* remotePubkey;
byte_t* localSeckey;
byte_t* nonce;
ProtocolMessage* msg = nullptr;
std::function< void(ProtocolMessage*) > hook;
Identity* m_LocalIdentity;
ProtocolMessage msg;
ProtocolFrame frame;
std::function< void(ProtocolFrame&) > hook;
AsyncKeyExchange(llarp_logic* l, llarp_crypto* c, byte_t* key,
byte_t* remote, byte_t* localSecret, byte_t* n)
AsyncIntroGen(llarp_logic* l, llarp_crypto* c, byte_t* key,
byte_t* remote, Identity* localident)
: logic(l)
, crypto(c)
, sharedKey(key)
, remotePubkey(remote)
, localSeckey(localSecret)
, nonce(n)
, m_LocalIdentity(localident)
{
}
static void
Result(void* user)
{
AsyncIntroGen* self = static_cast< AsyncIntroGen* >(user);
self->hook(self->frame);
delete self;
}
static void
Work(void* user)
{
AsyncKeyExchange* self = static_cast< AsyncKeyExchange* >(user);
AsyncIntroGen* self = static_cast< AsyncIntroGen* >(user);
// randomize Nounce
self->frame.N.Randomize();
// derive session key
self->crypto->dh_server(self->sharedKey, self->remotePubkey,
self->localSeckey, self->nonce);
self->m_LocalIdentity->enckey, self->frame.N);
// encrypt and sign
self->frame.EncryptAndSign(self->crypto, &self->msg, self->sharedKey,
self->m_LocalIdentity->signkey);
// inform result
llarp_logic_queue_job(self->logic, {self, &Result});
}
};
void
Endpoint::OutboundContext::AsyncGenIntro(
ProtocolMessage* msg, std::function< void(ProtocolMessage*) > result)
Endpoint::OutboundContext::AsyncGenIntro(llarp_buffer_t payload)
{
msg->N.Randomize();
AsyncKeyExchange* ex = new AsyncKeyExchange(
m_Parent->Logic(), m_Parent->Crypto(), sharedKey,
currentIntroSet.A.enckey, m_Parent->GetEncryptionSecretKey(), msg->N);
AsyncIntroGen* ex =
new AsyncIntroGen(m_Parent->Logic(), m_Parent->Crypto(), sharedKey,
currentIntroSet.A.enckey, m_Parent->GetIdentity());
ex->hook = std::bind(&Endpoint::OutboundContext::Send, this,
std::placeholders::_1);
ex->msg.PutBuffer(payload);
llarp_threadpool_queue_job(m_Parent->Worker(),
{ex, &AsyncKeyExchange::Work});
{ex, &AsyncIntroGen::Work});
}
void
Endpoint::OutboundContext::SendMessage(ProtocolMessage* msg)
Endpoint::OutboundContext::Send(ProtocolFrame& msg)
{
// TODO: delete msg
// TODO: implement me
// in this context we assume the message contents are encrypted
auto path = GetPathByRouter(selectedIntro.router);
if(path)
{
routing::PathTransferMessage transfer;
transfer.T = &msg;
transfer.Y.Randomize();
transfer.P = selectedIntro.pathID;
path->SendRoutingMessage(&transfer, m_Parent->Router());
}
}
bool
@ -473,8 +555,7 @@ namespace llarp
}
void
Endpoint::OutboundContext::AsyncEncrypt(
ProtocolMessage* msg, std::function< void(ProtocolMessage*) > result)
Endpoint::OutboundContext::AsyncEncrypt(llarp_buffer_t payload)
{
// TODO: implement me
}
@ -497,11 +578,5 @@ namespace llarp
return m_Router->tp;
}
byte_t*
Endpoint::GetEncryptionSecretKey()
{
return m_Identity.enckey;
}
} // namespace service
} // namespace llarp

@ -0,0 +1,8 @@
#include <llarp/service/frame.hpp>
namespace llarp
{
namespace service
{
}
} // namespace llarp

@ -1,11 +1,11 @@
#include <llarp/service/protocol.hpp>
#include "buffer.hpp"
namespace llarp
{
namespace service
{
ProtocolMessage::ProtocolMessage(ProtocolType t, uint64_t seqno)
: proto(t), sequenceNum(seqno)
ProtocolMessage::ProtocolMessage()
{
}
@ -32,6 +32,105 @@ namespace llarp
{
payload.resize(buf.sz);
memcpy(payload.data(), buf.base, buf.sz);
payload.shrink_to_fit();
}
ProtocolFrame::~ProtocolFrame()
{
}
bool
ProtocolFrame::BEncode(llarp_buffer_t* buf) const
{
if(!bencode_start_dict(buf))
return false;
if(!BEncodeWriteDictMsgType(buf, "A", "H"))
return false;
if(!BEncodeWriteDictEntry("D", D, buf))
return false;
if(S == 0)
{
if(!BEncodeWriteDictEntry("H", H, buf))
return false;
}
if(!BEncodeWriteDictEntry("N", N, buf))
return false;
if(!BEncodeWriteDictInt(buf, "S", S))
return false;
if(!BEncodeWriteDictInt(buf, "V", version))
return false;
if(!BEncodeWriteDictEntry("Z", Z, buf))
return false;
return bencode_end(buf);
}
bool
ProtocolFrame::DecodeKey(llarp_buffer_t key, llarp_buffer_t* val)
{
bool read = false;
if(!BEncodeMaybeReadDictEntry("D", D, read, key, val))
return false;
if(!BEncodeMaybeReadDictEntry("H", H, read, key, val))
return false;
if(!BEncodeMaybeReadDictEntry("N", N, read, key, val))
return false;
if(!BEncodeMaybeReadDictInt("S", S, read, key, val))
return false;
if(!BEncodeMaybeReadVersion("V", version, LLARP_PROTO_VERSION, read, key,
val))
return false;
if(!BEncodeMaybeReadDictEntry("Z", Z, read, key, val))
return false;
return read;
}
bool
ProtocolFrame::EncryptAndSign(llarp_crypto* crypto,
const ProtocolMessage* msg,
byte_t* sessionKey, byte_t* signingkey)
{
// put payload and encrypt
D = llarp::ConstBuffer(msg->payload);
memcpy(D.data(), msg->payload.data(), D.size());
auto dbuf = D.Buffer();
crypto->xchacha20(*dbuf, sessionKey, N);
// zero out signature
Z.Zero();
// encode
byte_t tmp[MAX_PROTOCOL_MESSAGE_SIZE];
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
if(!BEncode(&buf))
return false;
// rewind
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
// sign
return crypto->sign(Z, signingkey, buf);
}
bool
ProtocolFrame::Verify(llarp_crypto* crypto, byte_t* signkey)
{
// save signature
llarp::Signature sig = Z;
// zero out signature for verify
Z.Zero();
bool result = false;
// serialize
byte_t tmp[MAX_PROTOCOL_MESSAGE_SIZE];
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
if(BEncode(&buf))
{
// rewind buffer
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
// verify
result = crypto->verify(sig, buf, signkey);
}
// restore signature
Z = sig;
return result;
}
} // namespace service
} // namespace llarp

@ -127,12 +127,6 @@ namespace llarp
TransitHop::HandlePathTransferMessage(
const llarp::routing::PathTransferMessage* msg, llarp_router* r)
{
auto path = r->paths.GetByDownstream(r->pubkey(), msg->P);
if(path)
{
return path->HandleDownstream(msg->T.Buffer(), msg->Y, r);
}
llarp::LogWarn("No such path for path transfer pathid=", msg->P);
return false;
}

Loading…
Cancel
Save