more protocol changes to make it saner.

iwp sessions now can derive session key
pull/1/head
Jeff Becker 6 years ago
parent ec4e7674b7
commit dc92b98c9c
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -16,21 +16,20 @@ see crypto_v0.txt
wire decryption:
the first 32 bytes are message authentication bytes, h
the next 32 bytes are nounce for shared secret, n
the next 32 bytes are nouce for cipher, n
the remaining bytes are interpreted as ciphertext, x
a shared secret s is generated via TKE(initiater, recipiant, n)
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, n, 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
s = TKE(A, B, n)
x = SE(s, n, p)
h = MDS(n + x, s)
x = SE(S, p, n[0:24])
h = MDS(n + x, S)
the resulting data is:
@ -109,8 +108,9 @@ N bytes of ciphertext, x
plaintext header, H
8 bits protocol version, v (currently 0)
8 bits message type, t
12 bits payload size, s
4 bits flags, f
16 bits payload size, s
8 bits reserved, r (currently 0)
8 bits flags, f
plaintext payload: P
s bytes of data
@ -183,7 +183,7 @@ msg_bytes = BE(msg)
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
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

@ -124,8 +124,9 @@ typedef void (*iwp_async_frame_hook)(struct iwp_async_frame *);
struct iwp_async_frame
{
void *user;
bool success;
struct llarp_async_iwp *iwp;
void *user;
uint8_t *sessionkey;
size_t sz;
iwp_async_frame_hook hook;

@ -0,0 +1,15 @@
#ifndef LLARP_FRAME_HANDLER_HPP
#define LLARP_FRAME_HANDLER_HPP
#include <llarp/mem.h>
#include <vector>
namespace llarp
{
struct FrameHandler
{
bool
Process(const std::vector< byte_t >& buffer);
};
}
#endif

@ -31,7 +31,7 @@ namespace iwp
}
void
inform_gen_intro(void *user)
inform_intro(void *user)
{
iwp_async_intro *intro = static_cast< iwp_async_intro * >(user);
intro->hook(intro);
@ -69,14 +69,7 @@ namespace iwp
crypto->hmac(intro->buf, buf, sharedkey);
llarp::dumphex< llarp_hmac_t >(intro->buf);
// inform result
llarp_logic_queue_job(intro->iwp->logic, {intro, &inform_gen_intro});
}
void
inform_verify_intro(void *user)
{
iwp_async_intro *intro = static_cast< iwp_async_intro * >(user);
intro->hook(intro);
llarp_logic_queue_job(intro->iwp->logic, {intro, &inform_intro});
}
void
@ -118,11 +111,11 @@ namespace iwp
intro->buf = nullptr;
}
// inform result
llarp_logic_queue_job(intro->iwp->logic, {intro, &inform_verify_intro});
llarp_logic_queue_job(intro->iwp->logic, {intro, &inform_intro});
}
void
inform_verify_introack(void *user)
inform_introack(void *user)
{
iwp_async_introack *introack = static_cast< iwp_async_introack * >(user);
introack->hook(introack);
@ -170,14 +163,7 @@ namespace iwp
// copy token
memcpy(introack->token, token, 32);
}
llarp_logic_queue_job(logic, {introack, &inform_verify_introack});
}
void
handle_generated_introack(void *user)
{
iwp_async_introack *introack = static_cast< iwp_async_introack * >(user);
introack->hook(introack);
llarp_logic_queue_job(logic, {introack, &inform_introack});
}
void
@ -206,12 +192,11 @@ namespace iwp
buf.cur = buf.base;
crypto->hmac(introack->buf, buf, sharedkey);
llarp_logic_queue_job(introack->iwp->logic,
{introack, &handle_generated_introack});
llarp_logic_queue_job(introack->iwp->logic, {introack, &inform_introack});
}
void
inform_gen_session_start(void *user)
inform_session_start(void *user)
{
iwp_async_session_start *session =
static_cast< iwp_async_session_start * >(user);
@ -249,13 +234,13 @@ namespace iwp
memcpy(tmp + 32, N, 32);
shorthash(T, buf);
// e_K = TKE(a.k, b.k, N)
// e_K = TKE(a.k, b.k, n)
dh(e_K, b_K, a_sK, N);
// K = TKE(a.k, b.k, T)
dh(K, b_K, a_sK, T);
// x = SE(e_K, token, n[0:24])
buf.base = (session->buf + 32);
buf.base = (session->buf + 64);
buf.sz = 32;
memcpy(buf.base, token, 32);
encrypt(buf, e_K, N);
@ -265,10 +250,127 @@ namespace iwp
buf.sz = session->sz - 32;
hmac(session->buf, buf, e_K);
// K = TKE(a.k, b.k, T)
dh(K, b_K, a_sK, T);
llarp_logic_queue_job(logic, {user, &inform_session_start});
}
void
verify_session_start(void *user)
{
iwp_async_session_start *session =
static_cast< iwp_async_session_start * >(user);
auto crypto = session->iwp->crypto;
auto dh = crypto->transport_dh_server;
auto shorthash = crypto->shorthash;
auto hmac = crypto->hmac;
auto decrypt = crypto->xchacha20;
auto logic = session->iwp->logic;
auto b_sK = session->secretkey;
auto a_K = session->remote_pubkey;
auto N = session->nonce;
auto token = session->token;
auto K = session->sessionkey;
llarp_sharedkey_t e_K;
llarp_shorthash_t T;
byte_t tmp[64];
llarp_buffer_t buf;
// e_K = TKE(a.k, b.k, N)
dh(e_K, a_K, b_sK, N);
// h = MDS( n + x + w2, e_K)
buf.base = session->buf + 32;
buf.cur = buf.base;
buf.sz = session->sz - 32;
hmac(tmp, buf, e_K);
if(memcmp(tmp, session->buf, 32) == 0)
{
// hmac good
buf.base = session->buf + 64;
buf.cur = buf.base;
buf.sz = 32;
// token = SD(e_K, x, n[0:24])
decrypt(buf, e_K, N);
// ensure it's the same token
if(memcmp(buf.base, token, 32) == 0)
{
// T = HS(token + n)
memcpy(tmp, token, 32);
memcpy(tmp + 32, N, 32);
shorthash(T, buf);
// K = TKE(a.k, b.k, T)
dh(K, a_K, b_sK, T);
}
else // token missmatch
{
session->buf = nullptr;
printf("token miss match\n");
}
}
else // hmac fail
session->buf = nullptr;
llarp_logic_queue_job(logic, {user, &inform_session_start});
}
void
inform_frame_done(void *user)
{
iwp_async_frame *frame = static_cast< iwp_async_frame * >(user);
frame->hook(frame);
}
void
hmac_then_decrypt(void *user)
{
iwp_async_frame *frame = static_cast< iwp_async_frame * >(user);
auto crypto = frame->iwp->crypto;
auto hmac = frame->buf;
auto nonce = frame->buf + 32;
auto body = frame->buf + 64;
llarp_logic_queue_job(logic, {user, &inform_gen_session_start});
llarp_sharedkey_t digest;
llarp_buffer_t buf;
buf.base = body;
buf.cur = buf.base;
buf.sz = frame->sz - 64;
// h = MDS(n + x, S)
crypto->hmac(digest, buf, frame->sessionkey);
// check hmac
frame->success = memcmp(digest, hmac, 32) == 0;
// x = SE(S, p, n[0:24])
crypto->xchacha20(buf, frame->sessionkey, nonce);
// inform result
llarp_logic_queue_job(frame->iwp->logic, {user, &inform_frame_done});
}
void
encrypt_then_hmac(void *user)
{
iwp_async_frame *frame = static_cast< iwp_async_frame * >(user);
auto crypto = frame->iwp->crypto;
auto hmac = frame->buf;
auto nonce = frame->buf + 32;
auto body = frame->buf + 64;
llarp_buffer_t buf;
buf.base = body;
buf.cur = buf.base;
buf.sz = frame->sz - 64;
// randomize N
crypto->randbytes(nonce, 32);
// x = SE(S, p, n[0:24])
crypto->xchacha20(buf, frame->sessionkey, nonce);
// h = MDS(n + x, S)
crypto->hmac(hmac, buf, frame->sessionkey);
// inform result
llarp_logic_queue_job(frame->iwp->logic, {user, &inform_frame_done});
}
}
@ -326,6 +428,25 @@ void
iwp_call_async_frame_decrypt(struct llarp_async_iwp *iwp,
struct iwp_async_frame *frame)
{
frame->iwp = iwp;
llarp_threadpool_queue_job(iwp->worker, {frame, &iwp::hmac_then_decrypt});
}
void
iwp_call_async_frame_encrypt(struct llarp_async_iwp *iwp,
struct iwp_async_frame *frame)
{
frame->iwp = iwp;
llarp_threadpool_queue_job(iwp->worker, {frame, &iwp::encrypt_then_hmac});
}
void
iwp_call_async_verify_session_start(struct llarp_async_iwp *iwp,
struct iwp_async_session_start *session)
{
session->iwp = iwp;
llarp_threadpool_queue_job(iwp->worker,
{session, &iwp::verify_session_start});
}
struct llarp_async_iwp *

@ -2,6 +2,7 @@
#include <llarp/iwp.h>
#include <llarp/net.h>
#include <llarp/time.h>
#include <llarp/frame_handler.hpp>
#include <sodium/crypto_sign_ed25519.h>
@ -44,16 +45,16 @@ namespace iwp
/** plaintext frame header */
struct frame_header
{
uint8_t *ptr;
byte_t *ptr;
frame_header(uint8_t *buf) : ptr(buf)
frame_header(byte_t *buf) : ptr(buf)
{
}
uint8_t *
byte_t *
data()
{
return ptr + 4;
return ptr + 6;
}
uint8_t &
@ -68,53 +69,67 @@ namespace iwp
return ptr[1];
}
// 12 bits
uint16_t
size() const
{
uint16_t sz = (ptr[3] | 0x00fc) << 8;
sz |= ptr[2];
uint16_t sz = (ptr[3] | 0x00ff) << 8;
sz |= (ptr[2] & 0x00ff);
return sz;
}
void
setsize(uint16_t sz)
{
ptr[3] = (sz | 0xfc00) >> 8;
ptr[3] = (sz | 0xff00) >> 8;
ptr[2] = (sz | 0x00ff);
}
// 4 bits
uint8_t
flags() const
{
return ptr[3] & 0x07;
return ptr[5];
}
void
setflag(header_flag f)
{
ptr[3] |= f;
ptr[5] |= f;
}
};
/** xmit header */
struct xmit
{
uint32_t buffer[11];
byte_t buffer[48];
xmit()
{
}
xmit(uint8_t *ptr)
xmit(byte_t *ptr)
{
memcpy(buffer, ptr, 44);
memcpy(buffer, ptr, sizeof(buffer));
}
xmit(const xmit &other)
{
memcpy(buffer, other.buffer, 44);
memcpy(buffer, other.buffer, sizeof(buffer));
}
void
set_info(const byte_t *hash, uint64_t id, uint16_t fragsz, uint16_t lastsz,
uint8_t numfrags, uint8_t flags = 0x01)
{
// big endian assumed
// TODO: implement little endian
memcpy(buffer, hash, 32);
memcpy(buffer + 32, &id, 8);
memcpy(buffer + 40, &fragsz, 2);
memcpy(buffer + 42, &lastsz, 2);
buffer[44] = 0;
buffer[45] = 0;
buffer[46] = numfrags;
buffer[47] = flags;
}
uint64_t
@ -122,7 +137,7 @@ namespace iwp
{
// big endian assumed
// TODO: implement little endian
const uint32_t *start = (buffer + 8);
const byte_t *start = buffer + 32;
const uint64_t *msgid = (const uint64_t *)start;
return *msgid;
}
@ -133,14 +148,16 @@ namespace iwp
{
// big endian assumed
// TODO: implement little endian
return ((buffer[10] & 0xfc000000) >> 20);
const byte_t *start = buffer + 40;
const uint16_t *fragsz = (uint16_t *)start;
return *fragsz;
}
// number of full fragments
uint8_t
numfrags() const
{
return (buffer[10] & 0x07000000) >> 16;
return buffer[46];
}
// size of the entire message
@ -151,20 +168,20 @@ namespace iwp
}
// size of the last fragment
uint8_t
uint16_t
lastfrag() const
{
// big endian assumed
// TODO: implement little endian
return (buffer[10] & 0x0000ff00) >> 8;
const byte_t *start = buffer + 42;
const uint16_t *lastsz = (uint16_t *)start;
return *lastsz;
}
uint8_t
flags() const
flags()
{
// big endian assumed
// TODO: implement little endian
return (buffer[10] & 0x000000ff);
return buffer[47];
}
};
@ -173,7 +190,7 @@ namespace iwp
// forward declare
struct session;
struct transitframe
struct transit_message
{
session *parent = nullptr;
xmit msginfo;
@ -182,20 +199,48 @@ namespace iwp
std::map< uint16_t, fragment_t > frags;
fragment_t lastfrag;
transitframe()
transit_message()
{
}
// inbound
transitframe(const xmit &x) : msginfo(x)
transit_message(const xmit &x) : msginfo(x)
{
}
// outbound
transitframe(const llarp_buffer_t &buf, session *s) : parent(s)
transit_message(session *s) : parent(s)
{
}
bool reassemble(std::vector<byte_t> & buffer)
{
// TODO: implement
return false;
}
void
put_message(llarp_buffer_t &buf, const byte_t *hash, uint64_t id,
uint16_t mtu = 1024)
{
status.reset();
uint16_t fragid = 0;
uint16_t fragsize = mtu;
while((buf.cur - buf.base) > fragsize)
{
fragment_t frag(fragsize);
memcpy(frag.data(), buf.cur, fragsize);
buf.cur += fragsize;
frags[fragid++] = frag;
}
uint16_t lastfrag = buf.cur - buf.base;
// set info for xmit
msginfo.set_info(hash, id, mtu, frags.size(), lastfrag);
// copy message hash
memcpy(msginfo.buffer, hash, 32);
put_lastfrag(buf.cur, buf.cur - buf.base);
}
void
put_lastfrag(uint8_t *buf, size_t sz)
{
@ -206,33 +251,52 @@ namespace iwp
struct frame_state
{
uint64_t ids = 0;
uint64_t rxids = 0;
uint64_t txids = 0;
llarp_time_t lastEvent = 0;
std::map< uint64_t, transitframe > rx;
std::map< uint64_t, transitframe * > tx;
std::map< uint64_t, transit_message > rx;
std::map< uint64_t, transit_message * > tx;
typedef std::vector< uint8_t > sendbuf_t;
typedef std::vector< byte_t > sendbuf_t;
std::queue< sendbuf_t > sendqueue;
llarp::FrameHandler * handler = nullptr;
void
inbound_frame_complete(uint64_t id)
{
std::vector<byte_t> buf;
if(rx[id].reassemble(buf) && handler)
{
if(handler->Process(buf))
printf("processed frame %ld\n", id);
else
printf("failed to process frame %ld", id);
}
rx.erase(id);
}
void
init_sendbuf(sendbuf_t &buf, msgtype t, uint16_t sz, uint8_t flags)
{
buf.resize(4 + sz);
buf.resize(6 + sz);
buf[0] = 0;
buf[1] = t;
buf[2] = (sz & 0x00ff);
buf[3] = flags;
buf[2] = sz & 0x00ff;
buf[3] = (sz & 0xff00) >> 8;
buf[4] = 0;
buf[5] = flags;
}
void
push_ackfor(uint64_t id, uint16_t bitmask)
push_ackfor(uint64_t id, uint32_t bitmask)
{
sendbuf_t buf;
// TODO: set flags to nonzero as needed
init_sendbuf(buf, eACKS, 10, 0);
init_sendbuf(buf, eACKS, 12, 0);
// TODO: this assumes big endian
memcpy(buf.data() + 4, &id, 8);
memcpy(buf.data() + 12, &bitmask, 2);
memcpy(buf.data() + 6, &id, 8);
memcpy(buf.data() + 14, &bitmask, 4);
sendqueue.push(buf);
}
@ -252,15 +316,18 @@ namespace iwp
// extract xmit data
xmit x(hdr.data());
if(sz - 44 != x.lastfrag())
const auto bufsz = sizeof(x.buffer);
if(sz - bufsz < x.lastfrag())
{
// bad size of last fragment
printf("XMIT frag size missmatch, %ld != %d\n", sz - 44, x.lastfrag());
printf("XMIT frag size missmatch, %ld < %d\n", sz - bufsz,
x.lastfrag());
return false;
}
// check MSB set on flags
if(x.flags() & 0x80)
// check LSB set on flags
if(x.flags() & 0x01)
{
if(x.numfrags() > 0)
{
@ -268,7 +335,8 @@ namespace iwp
if(itr.second)
{
// inserted, put last fragment
itr.first->second.put_lastfrag(hdr.data() + 44, x.lastfrag());
itr.first->second.put_lastfrag(hdr.data() + sizeof(x.buffer),
x.lastfrag());
return true;
}
else
@ -277,11 +345,13 @@ namespace iwp
else
{
// short XMIT , no fragments so just ack
push_ackfor(x.msgid(), 0);
auto id = x.msgid();
push_ackfor(id, 0); // TODO: should this be before or after handling frame?
inbound_frame_complete(id);
}
}
else
printf("XMIT flags MSB not set\n");
printf("XMIT flags LSB not set\n");
return false;
}
@ -305,10 +375,9 @@ namespace iwp
// queue new outbound message
void
queue_tx(transitframe *frame)
queue_tx(uint16_t id, transit_message *msg)
{
ids++;
tx.try_emplace(ids, frame);
tx.try_emplace(id, msg);
}
// get next frame to encrypt and transmit
@ -361,6 +430,8 @@ namespace iwp
llarp_crypto *crypto;
llarp_async_iwp *iwp;
llarp_logic *logic;
llarp_rc *our_router;
llarp_seckey_t eph_seckey;
llarp_pubkey_t remote;
llarp_sharedkey_t sessionkey;
@ -413,24 +484,26 @@ namespace iwp
{
}
static void
handle_sendto(void *user)
{
transitframe *frame = static_cast< transitframe * >(user);
frame->parent->frame.queue_tx(frame);
}
static bool
sendto(llarp_link_session *s, llarp_buffer_t msg)
{
session *self = static_cast< session * >(s->impl);
void *ptr = self->mem->alloc(self->mem, sizeof(transitframe), 64);
transitframe *frame = new(ptr) transitframe(msg, self);
llarp_thread_job job = {.user = frame, .work = &handle_sendto};
llarp_logic_queue_job(self->logic, job);
session *self = static_cast< session * >(s->impl);
transit_message *m = new transit_message(self);
auto id = self->frame.txids++;
llarp_hash_t digest;
self->crypto->hash(digest, msg);
m->put_message(msg, digest, id);
self->add_outbound_message(id, m);
return true;
}
void
add_outbound_message(uint16_t id, transit_message *msg)
{
frame.queue_tx(id, msg);
pump();
}
void
pump()
{
@ -455,6 +528,10 @@ namespace iwp
// got intro ack
on_intro_ack(buf, sz);
return;
case eIntroAckSent:
// probably a session start
on_session_start(buf, sz);
return;
case eEstablished:
// session is started
decrypt_frame(buf, sz);
@ -464,6 +541,42 @@ namespace iwp
}
}
static void
handle_verify_session_start(iwp_async_session_start *s)
{
session *self = static_cast< session * >(s->user);
if(!s->buf)
{
// verify fail
// TODO: remove session?
printf("session start verify fail\n");
return;
}
printf("session start okay\n");
// auto msg = new transit_message;
// auto buffer = llarp::EncodeLIM< decltype(buf) >(buf, our_router);
}
void
on_session_start(const void *buf, size_t sz)
{
// own the buffer
memcpy(workbuf, buf, sz);
// verify session start
start.buf = workbuf;
start.sz = sz;
start.nonce = workbuf + 32;
start.token = token;
start.remote_pubkey = remote;
start.secretkey = eph_seckey;
start.sessionkey = sessionkey;
start.user = this;
start.hook = &handle_verify_session_start;
iwp_call_async_verify_session_start(iwp, &start);
}
bool
timedout(llarp_time_t now, llarp_time_t timeout = SESSION_TIMEOUT)
{
@ -540,7 +653,7 @@ namespace iwp
else
printf("decrypt frame fail\n");
self->mem->free(self->mem, frame);
delete frame;
}
void
@ -563,14 +676,13 @@ namespace iwp
session *self = static_cast< session * >(frame->user);
printf("sendto %ld\n", frame->sz);
llarp_ev_udp_sendto(self->udp, self->addr, frame->buf, frame->sz);
self->mem->free(self->mem, frame);
delete frame;
}
iwp_async_frame *
alloc_frame(const void *buf, size_t sz)
{
iwp_async_frame *frame =
(iwp_async_frame *)mem->alloc(mem, sizeof(iwp_async_frame), 2048);
iwp_async_frame *frame = new iwp_async_frame;
memcpy(frame->buf, buf, sz);
frame->sz = sz;
frame->user = this;
@ -608,6 +720,7 @@ namespace iwp
{
printf("sending introack...\n");
llarp_ev_udp_sendto(link->udp, link->addr, i->buf, i->sz);
link->EnterState(eIntroAckSent);
}
else
{

Loading…
Cancel
Save