mirror of
https://github.com/oxen-io/lokinet
synced 2023-12-14 06:53:00 +01:00
protocol docs, more efficient padding, make code reflect protocol changes.
This commit is contained in:
parent
59b5af551a
commit
adbf53bc40
9 changed files with 296 additions and 407 deletions
225
doc/iwp_v0.txt
225
doc/iwp_v0.txt
|
@ -1,225 +0,0 @@
|
|||
|
||||
invisible wire protocol version 0:
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in RFC 2119 [RFC2119].
|
||||
|
||||
cryptography:
|
||||
|
||||
see crypto_v0.txt
|
||||
|
||||
|
||||
wire decryption:
|
||||
|
||||
the first 32 bytes are message authentication bytes, h
|
||||
the next 32 bytes are nouce for cipher, n
|
||||
the remaining bytes are interpreted as ciphertext, x
|
||||
|
||||
a shared secret S is generated in the session start message
|
||||
next the integrity of the ciphertext is done by checking MDS(n + x, S) == h
|
||||
if the ciphertext is valid then the frame is decrypted via SD(S, x, n)
|
||||
|
||||
wire encryption:
|
||||
|
||||
given variadic sized payload p, 32 byte nounce n and public encryption keys A
|
||||
and B
|
||||
|
||||
x = SE(S, p, n[0:24])
|
||||
h = MDS(n + x, S)
|
||||
|
||||
the resulting data is:
|
||||
|
||||
h + n + x
|
||||
|
||||
|
||||
handshake:
|
||||
|
||||
0) intro
|
||||
|
||||
32 bytes hmac, h
|
||||
32 bytes nounce, n
|
||||
32 bytes encrypted alice's transport public encryption key e
|
||||
variadic bytes padding, w0
|
||||
|
||||
Alice transmits ( h + n + e + w0 ) to Bob from the transport address matching
|
||||
his public transport encryption key (b.k).
|
||||
|
||||
w0 = "[insert variable length random padding here]"
|
||||
n = RAND(32)
|
||||
e = SE(a.k, HS(b.k + n), n[0:24])
|
||||
S = TKE(a.k, b.k, n)
|
||||
h = MDS(n + e + w0, S)
|
||||
|
||||
Bob recieves ( s + n + e + w0 )
|
||||
|
||||
1) intro ack
|
||||
|
||||
sent in reply to an intro, bob sends an intro ack encrypted to Alice using
|
||||
|
||||
32 bytes hmac, h
|
||||
32 bytes nounce, n
|
||||
32 bytes ciphertext, x
|
||||
variadic bytes padding, w1
|
||||
|
||||
w1 = "[insert variable length random padding here]"
|
||||
token = RAND(32)
|
||||
S = TKE(a.k, b.k, n)
|
||||
x = SE(k, token, n[0:24])
|
||||
h = MDS(n + x + w1, S)
|
||||
|
||||
Bob transmits ( s + n + x + w1 ), r is ignored and discarded
|
||||
Alice recieves ( s + n + x + w1 ) and verifies the signature silently
|
||||
dropping if it does not match.
|
||||
|
||||
2) session start
|
||||
|
||||
Alice uses the token from the previous message to start the wire session
|
||||
|
||||
32 bytes hmac, h
|
||||
32 bytes nounce, n
|
||||
32 bytes ciphertext, x
|
||||
variadic byttes padding, w2
|
||||
|
||||
w2 = "[insert variable length random padding here]"
|
||||
e_K = TKE(a.k, b.k, n)
|
||||
x = SE(e_K, token, n[0:24])
|
||||
h = MDS(n + x + w2, e_K)
|
||||
T = HS(token + n)
|
||||
K = TKE(a.k, b.k, T)
|
||||
|
||||
Alice transmits ( h + n + x + w2 )
|
||||
Bob recieves ( h + n + x + w2) and verifies that h == MDS(n + x, k) silently
|
||||
drops if not matching
|
||||
|
||||
the session is now established with session key K,
|
||||
Bob replies by transmitting a LIM
|
||||
|
||||
IWP payload format:
|
||||
|
||||
ciphertext:
|
||||
32 bytes hmac, h
|
||||
32 bytes nounce, n
|
||||
N bytes of ciphertext, x
|
||||
|
||||
plaintext header, H
|
||||
8 bits protocol version, v (currently 0)
|
||||
8 bits message type, t
|
||||
16 bits payload size, s
|
||||
8 bits reserved, r (currently 0)
|
||||
8 bits flags, f
|
||||
|
||||
plaintext payload: P
|
||||
s bytes of data
|
||||
N bytes remaining data is discarded
|
||||
|
||||
Encryption:
|
||||
|
||||
D = H + P
|
||||
x = SE(D, S, n)
|
||||
h = MDS(n + x, S)
|
||||
|
||||
Alice transmits h + n + x
|
||||
|
||||
Bob recieves recieve h + n + x
|
||||
|
||||
Bob checks hmac by verifying h == MDS(n + x, S)
|
||||
|
||||
if the hmac fails the data is silently dropped
|
||||
|
||||
Decryption:
|
||||
|
||||
verify h == MDS(n + x, S)
|
||||
D = SD(x, S, n)
|
||||
H = D[0:6]
|
||||
P = D[6:6+H.s]
|
||||
|
||||
message types:
|
||||
|
||||
ALIV = 0x00
|
||||
|
||||
keepalive message
|
||||
|
||||
XMIT = 0x01
|
||||
|
||||
begin link layer message transmission
|
||||
|
||||
ACKS = 0x02
|
||||
|
||||
acknolege link layer message fragment
|
||||
|
||||
FRAG = 0x03
|
||||
|
||||
transmit link layer message fragment
|
||||
|
||||
flags:
|
||||
|
||||
SESSION_INVALIDATED = 1 << 0
|
||||
|
||||
this session is now invalidated and a new session is required
|
||||
|
||||
HIGH_PACKET_DROP = 1 << 1
|
||||
|
||||
high packet drop detected
|
||||
|
||||
HIGH_MTU_DETECTED = 1 << 2
|
||||
|
||||
the network uses an mtu greater than 1488 bytes
|
||||
|
||||
PROTOCOL_UPGRADE = 1 << 3
|
||||
|
||||
indicates we want to do protocol upgrade (future use)
|
||||
|
||||
XMIT payload:
|
||||
|
||||
start transmiting a link layer message
|
||||
|
||||
msg_bytes = BE(msg)
|
||||
|
||||
32 bytes hash of message computed by HS(msg_bytes)
|
||||
64 bits unsigned int message id
|
||||
16 bits unsigned int fragment size bytes, S
|
||||
16 bits size of last fragment in bytes, L
|
||||
16 bits reserved for future, currently zero
|
||||
8 bits unsigned int nonzero number of fragments, n
|
||||
8 bits reserved flags, f
|
||||
if f LSB is set then last fragment is included and is l bytes long
|
||||
|
||||
f's LSB MUST be set as of protocol version 0.
|
||||
|
||||
msg_bytes is S * (n - 1) + L bytes long
|
||||
|
||||
FRAG payload:
|
||||
|
||||
transmit a link layer message fragment
|
||||
|
||||
64 bits message id
|
||||
8 bits unsigned int fragment number
|
||||
S bytes of payload fragment data
|
||||
remaining bytes discarded
|
||||
|
||||
ACKS payload:
|
||||
|
||||
indicates we which chunks we have recieved
|
||||
|
||||
64 bits message id
|
||||
32 bits bitmask of chunks we have received
|
||||
remaining bytes discarded
|
||||
|
||||
|
||||
control flow:
|
||||
|
||||
To transmit link message over an established session the transmitter sends an
|
||||
XMIT frame.
|
||||
In reply to an XMIT frame the recipiant MUST send an ACKS frame with an emtpy
|
||||
bitmask.
|
||||
After the transmitter recieves the first ACKS frame it is allowed to start
|
||||
sending FRAG messages.
|
||||
When all fragmenets are obtained by the recipiant, the recipiant sends an ACKS
|
||||
frame with a full bitfield (0xFFFF), to indicate the link message was recieved.
|
||||
In the event of packet drop the sender decides when to retransmit FRAG frames
|
||||
with expontential backoff.
|
||||
|
||||
In the event of packet loss greater than 50% over 10 second the session is
|
||||
invalidated and must be renegotiated with a new handshake.
|
||||
|
|
@ -266,14 +266,18 @@ identifies the sender as having the RC contained in r. The recipiant MUST
|
|||
validate the RC's signature and ensure that the public key in use is listed in
|
||||
the RC.a matching the ipv6 address it originated from.
|
||||
|
||||
if r is not present in sessions made by clients.
|
||||
|
||||
{
|
||||
a: "i",
|
||||
n: "<32 bytes nonce for key exhcange>",
|
||||
p: uint64_milliseconds_session_period,
|
||||
r: RC,
|
||||
v: 0
|
||||
v: 0,
|
||||
z: "<64 bytes signature of entire message by r.k>"
|
||||
}
|
||||
|
||||
the link session will be kept open for p milliseconds after which
|
||||
the session MUST be renegotiated.
|
||||
|
||||
link relay commit message (LRCM)
|
||||
|
||||
request a commit to relay traffic to another node.
|
||||
|
@ -734,4 +738,4 @@ wrapper message for sending many dht messages down a path.
|
|||
M: [many, dht, messages, here],
|
||||
S: uint64_sequence_number,
|
||||
V: 0
|
||||
}
|
||||
}
|
||||
|
|
64
doc/wire-protocol.txt
Normal file
64
doc/wire-protocol.txt
Normal file
|
@ -0,0 +1,64 @@
|
|||
Wire Protocol (version 0)
|
||||
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in RFC 2119 [RFC2119].
|
||||
|
||||
LLARP supports by default an authenticated and framed transport over UTP [1]
|
||||
|
||||
1088 byte fragments are sent over UTP in an ordered fashion.
|
||||
|
||||
The each fragment has the following structure:
|
||||
|
||||
[ 32 bytes blake2 keyed hash of the following 1056 bytes ]
|
||||
[ 24 bytes random nonce ]
|
||||
[ 1032 bytes encrypted payload ]
|
||||
|
||||
the decrypted payload has the following structure:
|
||||
|
||||
[ big endian unsigned 32 bit flags (F) ]
|
||||
[ big endian unsigned 32 bit fragment length (N) ]
|
||||
[ N bytes of plaintext payload ]
|
||||
|
||||
if F is non zero then more fragments for the current message being transmitted
|
||||
are expected. If F is zero then this fragment is the last in the sequence.
|
||||
|
||||
On each fragment append the N bytes of payload to an internal buffer.
|
||||
This internal buffer MUST NOT exceed 8192 bytes, the maximum size of an inter
|
||||
node message.
|
||||
|
||||
When the last fragment in the sequence is reached the internal buffer is
|
||||
processed as a link layer message (see proto_v0.txt)
|
||||
|
||||
Handshake phase:
|
||||
|
||||
Before data flows a protocol handshake must happen.
|
||||
|
||||
The first message sent is a LIM (L) (see proto_v0.txt) by the connection initiator, Alice.
|
||||
|
||||
The receiving end MUST verify the signatures of the LIM and RC.
|
||||
If any verification fails at any phase the underlying UTP session MUST be reset.
|
||||
|
||||
Each side re-computes the session key.
|
||||
|
||||
the session key kdf for K is:
|
||||
|
||||
t_h = HS(K + L.n)
|
||||
K = TKE(A.p, B_a.e, sk, t_h)
|
||||
|
||||
the initial value of K is HS(B.k)
|
||||
|
||||
Periodically the connection initiator MUST renegotiate the session key by
|
||||
sending a LIM after L.p milliseconds have elapsed.
|
||||
|
||||
If either party's RC changes while a connection is established they MUST
|
||||
renegotioate the session keys to ensure the new RC is sent.
|
||||
|
||||
|
||||
references:
|
||||
|
||||
[1] http://www.bittorrent.org/beps/bep_0029.html
|
||||
|
||||
|
||||
|
|
@ -43,6 +43,9 @@ namespace llarp
|
|||
std::function< const PubKey &(void) > GetPubKey;
|
||||
/// get remote address
|
||||
std::function< const Addr &(void) > GetRemoteEndpoint;
|
||||
|
||||
/// handle a valid LIM
|
||||
std::function< bool(const LinkIntroMessage *msg) > GotLIM;
|
||||
};
|
||||
} // namespace llarp
|
||||
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
#define LLARP_LINK_LAYER_HPP
|
||||
#include <llarp/link/server.hpp>
|
||||
#include <llarp/link/session.hpp>
|
||||
constexpr size_t MAX_LINK_MSG_SIZE = 8192;
|
||||
constexpr size_t MAX_LINK_MSG_SIZE = 8192;
|
||||
constexpr llarp_time_t DefaultLinkSessionLifetime = 10 * 1000;
|
||||
#endif
|
||||
|
|
|
@ -8,6 +8,8 @@ namespace llarp
|
|||
|
||||
struct LinkIntroMessage : public ILinkMessage
|
||||
{
|
||||
static constexpr size_t MaxSize = MAX_RC_SIZE + 256;
|
||||
|
||||
LinkIntroMessage() : ILinkMessage()
|
||||
{
|
||||
}
|
||||
|
@ -19,8 +21,12 @@ namespace llarp
|
|||
~LinkIntroMessage();
|
||||
|
||||
RouterContact rc;
|
||||
|
||||
KeyExchangeNonce N;
|
||||
Signature Z;
|
||||
uint64_t P;
|
||||
|
||||
LinkIntroMessage&
|
||||
operator=(const LinkIntroMessage& msg);
|
||||
|
||||
bool
|
||||
DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf);
|
||||
|
@ -30,6 +36,12 @@ namespace llarp
|
|||
|
||||
bool
|
||||
HandleMessage(llarp_router* router) const;
|
||||
|
||||
bool
|
||||
Sign(llarp_crypto* c, const SecretKey& signKeySecret);
|
||||
|
||||
bool
|
||||
Verify(llarp_crypto* c) const;
|
||||
};
|
||||
} // namespace llarp
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#define MAXHOPS (8)
|
||||
#define DEFAULT_PATH_LIFETIME (10 * 60 * 1000)
|
||||
#define PATH_BUILD_TIMEOUT (10 * 1000)
|
||||
#define MESSAGE_PAD_SIZE (1024)
|
||||
#define MESSAGE_PAD_SIZE (512)
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
|
|
@ -60,14 +60,14 @@ namespace llarp
|
|||
Alive();
|
||||
|
||||
/// base
|
||||
BaseSession(llarp_router* r);
|
||||
BaseSession(LinkLayer* p);
|
||||
|
||||
/// outbound
|
||||
BaseSession(llarp_router* r, utp_socket* s, const RouterContact& rc,
|
||||
BaseSession(LinkLayer* p, utp_socket* s, const RouterContact& rc,
|
||||
const AddressInfo& addr);
|
||||
|
||||
/// inbound
|
||||
BaseSession(llarp_router* r, utp_socket* s, const Addr& remote);
|
||||
BaseSession(LinkLayer* p, utp_socket* s, const Addr& remote);
|
||||
|
||||
enum State
|
||||
{
|
||||
|
@ -143,14 +143,7 @@ namespace llarp
|
|||
QueueWriteBuffers(llarp_buffer_t buf)
|
||||
{
|
||||
llarp::LogDebug("write ", buf.sz, " bytes to ", remoteAddr);
|
||||
if(state != eSessionReady)
|
||||
{
|
||||
llarp::LogWarn("failed to send ", buf.sz,
|
||||
" bytes on non ready session state=", state);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
lastActive = llarp_time_now_ms();
|
||||
lastActive = llarp_time_now_ms();
|
||||
size_t sz = buf.sz;
|
||||
byte_t* ptr = buf.base;
|
||||
while(sz)
|
||||
|
@ -176,57 +169,36 @@ namespace llarp
|
|||
OnLinkEstablished(p);
|
||||
KeyExchangeNonce nonce;
|
||||
nonce.Randomize();
|
||||
gotLIM = true;
|
||||
if(DoKeyExchange(Router()->crypto.transport_dh_client, nonce,
|
||||
remoteTransportPubKey, Router()->encryption))
|
||||
{
|
||||
SendHandshake(nonce);
|
||||
EnterState(eSessionReady);
|
||||
SendKeepAlive();
|
||||
}
|
||||
OutboundHandshake(nonce);
|
||||
}
|
||||
|
||||
// send our RC to the remote
|
||||
// send first message
|
||||
void
|
||||
SendHandshake(const KeyExchangeNonce& n)
|
||||
{
|
||||
FragmentBuffer tmp;
|
||||
auto buf = InitBuffer(tmp.data(), tmp.size());
|
||||
// fastforward buffer for handshake to fit before
|
||||
buf.cur += sizeof(uint32_t) * 2;
|
||||
byte_t* begin = buf.cur;
|
||||
LinkIntroMessage msg;
|
||||
msg.rc = Router()->rc;
|
||||
msg.N = n;
|
||||
if(!msg.BEncode(&buf))
|
||||
{
|
||||
llarp::LogError("failed to encode our RC for handshake");
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t sz = buf.cur - begin;
|
||||
llarp::LogDebug("handshake is of size ", sz, " bytes");
|
||||
// write handshake header
|
||||
buf.cur = buf.base;
|
||||
llarp_buffer_put_uint32(&buf, LLARP_PROTO_VERSION);
|
||||
llarp_buffer_put_uint32(&buf, sz);
|
||||
// send it
|
||||
write_ll(tmp.data(), sz + (sizeof(uint32_t) * 2));
|
||||
}
|
||||
OutboundHandshake(const KeyExchangeNonce& n);
|
||||
|
||||
// mix keys
|
||||
bool
|
||||
DoKeyExchange(llarp_transport_dh_func dh, const KeyExchangeNonce& n,
|
||||
const PubKey& other, const SecretKey& secret)
|
||||
{
|
||||
PubKey us = llarp::seckey_topublic(secret);
|
||||
llarp::LogDebug("DH us=", us, " them=", other, " n=", n);
|
||||
if(!dh(sessionKey, other, secret, n))
|
||||
ShortHash t_h;
|
||||
AlignedBuffer< 64 > tmp;
|
||||
memcpy(tmp.data(), sessionKey, sessionKey.size());
|
||||
memcpy(tmp.data() + sessionKey.size(), n, n.size());
|
||||
// t_h = HS(K + L.n)
|
||||
if(!Router()->crypto.shorthash(t_h, ConstBuffer(tmp)))
|
||||
{
|
||||
llarp::LogError("key exchange with ", other, " failed");
|
||||
Close();
|
||||
llarp::LogError("failed to mix key to ", remoteAddr);
|
||||
return false;
|
||||
}
|
||||
|
||||
// K = TKE(a.p, B_a.e, sk, t_h)
|
||||
if(!dh(sessionKey, other, secret, t_h))
|
||||
{
|
||||
llarp::LogError("key exchange with ", other, " failed");
|
||||
return false;
|
||||
}
|
||||
llarp::LogDebug("keys mixed with session to ", remoteAddr);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -238,19 +210,11 @@ namespace llarp
|
|||
void
|
||||
Close();
|
||||
|
||||
bool
|
||||
RecvHandshake(const void* buf, size_t bufsz, LinkLayer* p);
|
||||
|
||||
bool
|
||||
Recv(const void* buf, size_t sz)
|
||||
{
|
||||
if(state != eSessionReady)
|
||||
{
|
||||
llarp::LogWarn("session not ready via ", remoteAddr);
|
||||
return false;
|
||||
}
|
||||
Alive();
|
||||
byte_t* ptr = (const byte_t*)buf;
|
||||
byte_t* ptr = (byte_t*)buf;
|
||||
llarp::LogDebug("utp read ", sz, " from ", remoteAddr);
|
||||
size_t s = sz;
|
||||
// process leftovers
|
||||
|
@ -290,6 +254,12 @@ namespace llarp
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InboundLIM(const LinkIntroMessage* msg);
|
||||
|
||||
bool
|
||||
OutboundLIM(const LinkIntroMessage* msg);
|
||||
|
||||
bool
|
||||
IsTimedOut(llarp_time_t now) const
|
||||
{
|
||||
|
@ -527,13 +497,15 @@ namespace llarp
|
|||
return std::unique_ptr< LinkLayer >(new LinkLayer(r));
|
||||
}
|
||||
|
||||
BaseSession::BaseSession(llarp_router* r)
|
||||
BaseSession::BaseSession(LinkLayer* p)
|
||||
{
|
||||
parent = p;
|
||||
remoteTransportPubKey.Zero();
|
||||
parent = nullptr;
|
||||
recvMsgOffset = 0;
|
||||
|
||||
SendKeepAlive = [&]() -> bool {
|
||||
if(false && sendq.size() == 0)
|
||||
/*
|
||||
if(sendq.size() == 0 && state == eSessionReady)
|
||||
{
|
||||
DiscardMessage msg;
|
||||
byte_t tmp[128] = {0};
|
||||
|
@ -545,8 +517,10 @@ namespace llarp
|
|||
if(!this->QueueWriteBuffers(buf))
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
return true;
|
||||
};
|
||||
gotLIM = false;
|
||||
sendBufOffset = 0;
|
||||
recvBufOffset = 0;
|
||||
TimedOut = [&](llarp_time_t now) -> bool {
|
||||
|
@ -567,9 +541,9 @@ namespace llarp
|
|||
GetRemoteEndpoint = std::bind(&BaseSession::RemoteEndpoint, this);
|
||||
}
|
||||
|
||||
BaseSession::BaseSession(llarp_router* r, utp_socket* s,
|
||||
BaseSession::BaseSession(LinkLayer* p, utp_socket* s,
|
||||
const RouterContact& rc, const AddressInfo& addr)
|
||||
: BaseSession(r)
|
||||
: BaseSession(p)
|
||||
{
|
||||
remoteRC.Clear();
|
||||
remoteTransportPubKey = addr.pubkey;
|
||||
|
@ -579,17 +553,99 @@ namespace llarp
|
|||
assert(s == sock);
|
||||
remoteAddr = addr;
|
||||
Start = std::bind(&BaseSession::Connect, this);
|
||||
GotLIM =
|
||||
std::bind(&BaseSession::OutboundLIM, this, std::placeholders::_1);
|
||||
}
|
||||
|
||||
BaseSession::BaseSession(llarp_router* r, utp_socket* s, const Addr& addr)
|
||||
: BaseSession(r)
|
||||
BaseSession::BaseSession(LinkLayer* p, utp_socket* s, const Addr& addr)
|
||||
: BaseSession(p)
|
||||
{
|
||||
p->router->crypto.shorthash(sessionKey,
|
||||
ConstBuffer(p->router->rc.pubkey));
|
||||
remoteRC.Clear();
|
||||
sock = s;
|
||||
assert(s == sock);
|
||||
assert(utp_set_userdata(sock, this) == this);
|
||||
remoteAddr = addr;
|
||||
Start = []() {};
|
||||
GotLIM = std::bind(&BaseSession::InboundLIM, this, std::placeholders::_1);
|
||||
}
|
||||
|
||||
bool
|
||||
BaseSession::InboundLIM(const LinkIntroMessage* msg)
|
||||
{
|
||||
if(gotLIM && remoteRC.pubkey != msg->rc.pubkey)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
remoteRC = msg->rc;
|
||||
gotLIM = true;
|
||||
if(!DoKeyExchange(Router()->crypto.transport_dh_server, msg->N,
|
||||
remoteRC.enckey, parent->TransportSecretKey()))
|
||||
return false;
|
||||
EnterState(eSessionReady);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseSession::OutboundLIM(const LinkIntroMessage* msg)
|
||||
{
|
||||
if(gotLIM && remoteRC.pubkey != msg->rc.pubkey)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
remoteRC = msg->rc;
|
||||
gotLIM = true;
|
||||
// TODO: update address info pubkey
|
||||
return DoKeyExchange(Router()->crypto.transport_dh_client, msg->N,
|
||||
remoteTransportPubKey, Router()->encryption);
|
||||
}
|
||||
|
||||
void
|
||||
BaseSession::OutboundHandshake(const KeyExchangeNonce& n)
|
||||
{
|
||||
Router()->crypto.shorthash(sessionKey, ConstBuffer(remoteRC.pubkey));
|
||||
|
||||
byte_t tmp[LinkIntroMessage::MaxSize];
|
||||
auto buf = StackBuffer< decltype(tmp) >(tmp);
|
||||
LinkIntroMessage msg;
|
||||
msg.rc = Router()->rc;
|
||||
msg.N = n;
|
||||
msg.P = DefaultLinkSessionLifetime;
|
||||
if(!msg.Sign(&Router()->crypto, Router()->identity))
|
||||
{
|
||||
llarp::LogError("failed to sign LIM for outbound handshake to ",
|
||||
remoteAddr);
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
// encode
|
||||
if(!msg.BEncode(&buf))
|
||||
{
|
||||
llarp::LogError("failed to encode LIM for handshake to ", remoteAddr);
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
// rewind
|
||||
buf.sz = buf.cur - buf.base;
|
||||
buf.cur = buf.base;
|
||||
// send
|
||||
if(!SendMessageBuffer(buf))
|
||||
{
|
||||
llarp::LogError("failed to send handshake to ", remoteAddr);
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
// mix keys
|
||||
if(!DoKeyExchange(Router()->crypto.transport_dh_client, msg.N,
|
||||
remoteTransportPubKey, Router()->encryption))
|
||||
{
|
||||
llarp::LogError("failed to mix keys for outbound session to ",
|
||||
remoteAddr);
|
||||
Close();
|
||||
}
|
||||
else
|
||||
EnterState(eSessionReady);
|
||||
}
|
||||
|
||||
llarp_router*
|
||||
|
@ -611,14 +667,12 @@ namespace llarp
|
|||
LinkLayer::NewOutboundSession(const RouterContact& rc,
|
||||
const AddressInfo& addr)
|
||||
{
|
||||
return new BaseSession(router, utp_create_socket(_utp_ctx), rc, addr);
|
||||
return new BaseSession(this, utp_create_socket(_utp_ctx), rc, addr);
|
||||
}
|
||||
|
||||
uint64
|
||||
LinkLayer::OnRead(utp_callback_arguments* arg)
|
||||
{
|
||||
LinkLayer* parent =
|
||||
static_cast< LinkLayer* >(utp_context_get_userdata(arg->context));
|
||||
BaseSession* self =
|
||||
static_cast< BaseSession* >(utp_get_userdata(arg->socket));
|
||||
|
||||
|
@ -628,26 +682,13 @@ namespace llarp
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
else if(self->state == BaseSession::eSessionReady)
|
||||
if(!self->Recv(arg->buf, arg->len))
|
||||
{
|
||||
if(!self->Recv(arg->buf, arg->len))
|
||||
{
|
||||
llarp::LogDebug("recv fail for ", self->remoteAddr);
|
||||
self->Close();
|
||||
return 0;
|
||||
}
|
||||
utp_read_drained(arg->socket);
|
||||
}
|
||||
else if(self->state == BaseSession::eLinkEstablished)
|
||||
{
|
||||
if(!self->RecvHandshake(arg->buf, arg->len, parent))
|
||||
{
|
||||
llarp::LogDebug("recv handshake failed for ", self->remoteAddr);
|
||||
self->Close();
|
||||
return 0;
|
||||
}
|
||||
utp_read_drained(arg->socket);
|
||||
llarp::LogDebug("recv fail for ", self->remoteAddr);
|
||||
self->Close();
|
||||
return 0;
|
||||
}
|
||||
utp_read_drained(arg->socket);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -698,7 +739,7 @@ namespace llarp
|
|||
static_cast< LinkLayer* >(utp_context_get_userdata(arg->context));
|
||||
Addr remote(*arg->address);
|
||||
llarp::LogDebug("utp accepted from ", remote);
|
||||
BaseSession* session = new BaseSession(self->router, arg->socket, remote);
|
||||
BaseSession* session = new BaseSession(self, arg->socket, remote);
|
||||
self->PutSession(remote, session);
|
||||
session->OnLinkEstablished(self);
|
||||
return 0;
|
||||
|
@ -709,12 +750,8 @@ namespace llarp
|
|||
bool isLastFragment)
|
||||
|
||||
{
|
||||
if(state != eSessionReady)
|
||||
{
|
||||
llarp::LogWarn("tried to send to non ready session on ", remoteAddr);
|
||||
return;
|
||||
}
|
||||
FragmentBuffer buf;
|
||||
sendq.emplace_back();
|
||||
auto& buf = sendq.back();
|
||||
llarp::LogDebug("encrypt then hash ", sz, " bytes last=", isLastFragment);
|
||||
buf.Randomize();
|
||||
byte_t* nonce = buf.data() + FragmentHashSize;
|
||||
|
@ -740,8 +777,6 @@ namespace llarp
|
|||
payload.sz = FragmentBufferSize - FragmentHashSize;
|
||||
// key'd hash
|
||||
Router()->crypto.hmac(buf.data(), payload, sessionKey);
|
||||
// push back
|
||||
sendq.push_back(std::move(buf));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -811,83 +846,6 @@ namespace llarp
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseSession::RecvHandshake(const void* buf, size_t bufsz, LinkLayer* p)
|
||||
{
|
||||
size_t sz = bufsz;
|
||||
parent = p;
|
||||
|
||||
llarp::LogDebug("recv handshake ", sz, " from ", remoteAddr);
|
||||
if(sz <= 8)
|
||||
{
|
||||
llarp::LogDebug("handshake too small from ", remoteAddr);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// process handshake header
|
||||
byte_t* ptr = (byte_t*)buf;
|
||||
uint32_t version = bufbe32toh(ptr);
|
||||
if(version != LLARP_PROTO_VERSION)
|
||||
{
|
||||
llarp::LogWarn("protocol version missmatch ", version,
|
||||
" != ", LLARP_PROTO_VERSION);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
ptr += sizeof(uint32_t);
|
||||
sz -= sizeof(uint32_t);
|
||||
uint32_t limsz = bufbe32toh(ptr);
|
||||
ptr += sizeof(uint32_t);
|
||||
sz -= sizeof(uint32_t);
|
||||
if(limsz > sz)
|
||||
{
|
||||
// not enough data
|
||||
// TODO: don't bail here, continue reading
|
||||
llarp::LogDebug("not enough data for handshake, want ", limsz,
|
||||
" bytes but got ", sz);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
llarp::LogDebug("from ", bufsz, " bytes reading ", limsz, " of ", sz,
|
||||
" bytes");
|
||||
|
||||
// process LIM
|
||||
auto mbuf = InitBuffer(ptr, limsz);
|
||||
LinkIntroMessage msg(this);
|
||||
if(!msg.BDecode(&mbuf))
|
||||
{
|
||||
llarp::LogError("Failed to parse LIM from ", remoteAddr);
|
||||
llarp::DumpBuffer(mbuf);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if(!msg.HandleMessage(Router()))
|
||||
{
|
||||
llarp::LogError("failed to verify signature of rc");
|
||||
return false;
|
||||
}
|
||||
gotLIM = true;
|
||||
sz -= limsz;
|
||||
remoteRC = msg.rc;
|
||||
if(!DoKeyExchange(Router()->crypto.transport_dh_server, msg.N,
|
||||
remoteRC.enckey, parent->TransportSecretKey()))
|
||||
return false;
|
||||
EnterState(eSessionReady);
|
||||
if(sz)
|
||||
{
|
||||
llarp::LogDebug("got ", sz, " leftover from handshake from ",
|
||||
remoteAddr);
|
||||
return Recv(ptr + limsz, sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
llarp::LogDebug("no leftovers in handshake from ", remoteAddr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BaseSession::Close()
|
||||
{
|
||||
|
|
|
@ -29,6 +29,10 @@ namespace llarp
|
|||
llarp::LogWarn("failed to decode nonce in LIM");
|
||||
return false;
|
||||
}
|
||||
if(llarp_buffer_eq(key, "p"))
|
||||
{
|
||||
return bencode_read_integer(buf, &P);
|
||||
}
|
||||
if(llarp_buffer_eq(key, "r"))
|
||||
{
|
||||
if(rc.BDecode(buf))
|
||||
|
@ -50,6 +54,10 @@ namespace llarp
|
|||
llarp::LogDebug("LIM version ", version);
|
||||
return true;
|
||||
}
|
||||
else if(llarp_buffer_eq(key, "z"))
|
||||
{
|
||||
return Z.BDecode(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
llarp::LogWarn("invalid LIM key: ", *key.cur);
|
||||
|
@ -73,6 +81,11 @@ namespace llarp
|
|||
if(!N.BEncode(buf))
|
||||
return false;
|
||||
|
||||
if(!bencode_write_bytestring(buf, "p", 1))
|
||||
return false;
|
||||
if(!bencode_write_uint64(buf, P))
|
||||
return false;
|
||||
|
||||
if(!bencode_write_bytestring(buf, "r", 1))
|
||||
return false;
|
||||
if(!rc.BEncode(buf))
|
||||
|
@ -81,12 +94,71 @@ namespace llarp
|
|||
if(!bencode_write_version_entry(buf))
|
||||
return false;
|
||||
|
||||
if(!bencode_write_bytestring(buf, "z", 1))
|
||||
return false;
|
||||
if(!Z.BEncode(buf))
|
||||
return false;
|
||||
|
||||
return bencode_end(buf);
|
||||
}
|
||||
|
||||
LinkIntroMessage&
|
||||
LinkIntroMessage::operator=(const LinkIntroMessage& msg)
|
||||
{
|
||||
version = msg.version;
|
||||
Z = msg.Z;
|
||||
rc = msg.rc;
|
||||
N = msg.N;
|
||||
P = msg.P;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
LinkIntroMessage::HandleMessage(llarp_router* router) const
|
||||
{
|
||||
return rc.VerifySignature(&router->crypto);
|
||||
if(!Verify(&router->crypto))
|
||||
return false;
|
||||
return session->GotLIM(this);
|
||||
}
|
||||
|
||||
bool
|
||||
LinkIntroMessage::Sign(llarp_crypto* c, const SecretKey& k)
|
||||
{
|
||||
Z.Zero();
|
||||
byte_t tmp[MaxSize] = {0};
|
||||
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
|
||||
if(!BEncode(&buf))
|
||||
return false;
|
||||
buf.sz = buf.cur - buf.base;
|
||||
buf.cur = buf.base;
|
||||
return c->sign(Z, k, buf);
|
||||
}
|
||||
|
||||
bool
|
||||
LinkIntroMessage::Verify(llarp_crypto* c) const
|
||||
{
|
||||
LinkIntroMessage copy;
|
||||
copy = *this;
|
||||
copy.Z.Zero();
|
||||
byte_t tmp[MaxSize] = {0};
|
||||
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
|
||||
if(!copy.BEncode(&buf))
|
||||
return false;
|
||||
buf.sz = buf.cur - buf.base;
|
||||
buf.cur = buf.base;
|
||||
// outer signature
|
||||
if(!c->verify(rc.pubkey, buf, Z))
|
||||
{
|
||||
llarp::LogError("outer signature failure");
|
||||
return false;
|
||||
}
|
||||
// rc signature
|
||||
if(!rc.VerifySignature(c))
|
||||
{
|
||||
llarp::LogError("inner signature failure");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace llarp
|
||||
|
|
Loading…
Reference in a new issue