diff --git a/Makefile b/Makefile index 3b95c479e..0e5c960d0 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ LIBUV_FLAGS = $(shell pkg-config --cflags libuv) LIBUV_LIBS = $(shell pkg-config --libs libuv) REQUIRED_CFLAGS = $(LIBUV_FLAGS) $(SODIUM_FLAGS) -I$(REPO)/include -std=c99 $(CFLAGS) -REQUIRED_CXXFLAGS = $(LIBUV_FLAGS) $(SODIUM_FLAGS) -I$(REPO)/include -std=c++17 $(CXXFLAGS) +REQUIRED_CXXFLAGS = $(LIBUV_FLAGS) $(SODIUM_FLAGS) -I$(REPO)/include -std=c++14 $(CXXFLAGS) REQUIRED_LDFLAGS = $(LDFLAGS) -ljemalloc $(SODIUM_LIBS) $(LIBUV_LIBS) all: build diff --git a/doc/proto_v0.txt b/doc/proto_v0.txt index e24c22662..0b89cca1a 100644 --- a/doc/proto_v0.txt +++ b/doc/proto_v0.txt @@ -36,7 +36,9 @@ BD(x) is bittorrent decode x cryptography: H(x) is 512 bit blake2b digest of x +HS(x) is 256 bit blake2b digest of x MD(x, k) is 512 bit blake2b hmac of x with secret value k +MDS(x, k) is 256 bit blake2b hmac of x with secret value k NE(k, x) is sntrup4591761 encrypt data x to public key k ND(k, x) is sntrup4591761 decrypt data x with private key k SE(k, n, x) is chacha20 encrypt data x using symettric key k and nounce n @@ -45,24 +47,203 @@ S(k, x) is sign x with ed25519 using seed k V(k, x, sig) is verify x data using signature sig using public key k DH(x, y) is a ecdh key exchange using ed25519 scalarmult between public keys x and y -KE(x, y) is a ecdh key exchange using H(DH(x, y) + x) - +KE(x, y) is a ecdh key exchange using H(x + y + DH(x, y)) +PKE(x, y, n) is a path key exchange using MDS(n, KE(x, y)) +TKE(x, y, n) is a transport key exchange using MD(n, KE(x, y)) +RAND(n) is n random bytes --- wire protocol: -as of version 0 plaintext sctp is used, future versions will use an encrypted udp transport (IWP). +as of version 0 plaintext sctp is used, future versions will use an encrypted +udp transport (IWP). + + +frame decryption: + +the first 32 bytes are message authentication bytes, h +the next 32 bytes are nounce for shared secret, n +the remaining bytes are interpreted as ciphertext, x + +a shared secret s is generated via TKE(us, them, 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) + +frame 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) + +the resulting frame is: + +h + n + x + + +handshake: + +0) intro frame: + +32 bytes hmac, h +32 bytes nounce, n +64 bytes elligator sqaured encoded alice's transport public encryption key, k +variadic bytes padding, w0 + +Alice sends ( h + n + k + w0 ) to Bob from the transport address matching her +public transport encryption key. + +1) intro ack frame + +in reply to an intro frame, bob sends an intro ack frame encrypted to Alice +using + + +32 bytes hmac, h +32 bytes nounce, n +32 bytes ciphertext, x +variadic bytes padding, w1 + +token = RAND(32) +k = TKE(a.k, b.k, n) +x = SE(k, token, n[0:24]) +h = MDS(n + x, k) + +Bob sends ( h + n + x + w1 ) to Alice + +2) token frame: + +Alice sends the token from the intro ack frame back to Bob + +32 bytes hmac, h +32 bytes nounce, n +32 bytes ciphertext, x +variadic byttes padding, w2 + +k = TKE(a.k, b.k, n) +x = SE(k, token, n[0:24]) +h = MDS(n + x, k) + +Alice sends ( h + n + x + w2 ) to Bob + +4) token ack frame: + +Bob acks the token that he got from Alice + +32 bytes hmac, h +32 bytes nounce, n +32 bytes ciphertext, x +variadic byttes padding, w3 + +S = TKE(a.k, b.k, token) +x = SE(S, token, n[0:24]) +h = MDS(n + x, S) + +Alice sends ( h + n + x + w3 ) to Bob and the session is now established using +shared secret S + + +IWP frame format: + +ciphertext: +32 bytes hmac, h +32 bytes nounce, n +N bytes of ciphertext, x + +plaintext frame header, H +8 bits protocol version, v (currently 0) +8 bits message type, t +12 bits payload size, s +4 bits flags, f + +plaintext payload: P +s bytes of data +N bytes remaining data is discarded + +x = SE(H + P, S, n) +h = MDS(n + x, S) + +transmit h + n + x + +message types: +XMIT = 0x01 +begin link layer message transmission -IWP inbound handshake: +ACKS = 0x02 +acknolege link layer message fragment -IWP outbound handshake: +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 msgid computed as HS(msg_bytes) +12 bits unsigned int fragment size bytes, s +4 bits unsigned int number of fragments, n +8 bits size of last fragment in bytes, l + +msg_bytes is s * (n - 1) + l bytes long + +FRAG payload: + +transmit a link layer message fragment + +32 bytes msgid +4 bits ignored +4 bits unsigned int fragment number +remaining bytes of payload are fragment data + +ACKS payload: + +indicates we which chunks we have recieved + +32 bytes msgid +16 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. --- @@ -94,9 +275,10 @@ router contact (RC) { a: [ one, or, many, AI, here ... ], - k: "<32 bytes public sigining/encryption key>", + k: "<32 bytes public signing/encryption identity key>", x: [ Exit, Infos ], - z: "<64 bytes signature using signing key>" + v: 0, + z: "<64 bytes signature using identity key>" } service info (SI) @@ -104,6 +286,7 @@ service info (SI) { n: "", s: "<32 bytes public signing key>", + v: 0, x: "" } @@ -116,6 +299,7 @@ introducer (I) { i: "<32 bytes public key of router>", p: path_id_uint64, + v: 0, x: time_expires_seconds_since_epoch_uint64 } @@ -125,6 +309,7 @@ introducer set (IS) a: "<64 bytes SA>", e: "<1218 bytes ntru public encryption key>", i: [ I, I, I, ... ], + v: 0, z: "<64 bytes signature using service info signing key>" } @@ -144,20 +329,23 @@ link relay commit message (LRCM) { a: "c", - b: [ list, of, encrypted, RCR, as, bytes ], + b: [ list, of, encrypted, frames ], + v: 0, } + relay commit record (RCR) -record requesting path with tunnel id p relay messages for x seconds to router +record requesting path with id p relay messages for x seconds to router on network who's i is equal to RC.k and decrypt data any messages using -MD(n, KE(c, RC.k)) as symettric key for encryption and decryption. +PKE(n, rc.K, c) as symettric key for encryption and decryption. { c: "<32 byte public signing/encryption key used for further communication>", i: "<32 byte RC.k of next hop>", n: "<32 bytes nounce for key exchange>", p: path_id_uint64, + v: 0, x: seconds_lifetime_uint64 } @@ -186,6 +374,7 @@ is RECOMMENDED. c: "r", p: path_id_uint64, r: "", + v: 0, x: "" } @@ -197,6 +386,7 @@ for path with id p. { c: "a", p: path_id_uint64, + v: 0, x: "" } @@ -208,7 +398,8 @@ path build and send the result of the build. { a: "s", - p: [list, of, encrypted, replies] + p: [list, of, encrypted, replies], + v: 0, } @@ -224,6 +415,7 @@ new_y = y ^ new_z[0:24] { a: "u", p: path_id_uint64, + v: 0, y: "", z: "" } @@ -240,6 +432,7 @@ new_z = SE(k, new_y, z) { a: "d", p: path_id_uint64, + v: 0, y: "", z: "" } @@ -251,7 +444,8 @@ verify signature using cancel key c in relay commit message. { a: "x", - b: [ list, of, exit, records, as, bytes ] + b: [ list, of, exit, records, as, bytes ], + v: 0, } link relay exit record (LRXR) @@ -259,19 +453,30 @@ link relay exit record (LRXR) { c: "x", p: path_id_uint64, + v: 0, x: "", z: "<64 bytes signature>" } --- +direct paths: + +a direct path is a "0 hop" path built by Alice to communicate directly to Bob for point to point transmission of routing layer messages. + +these are built by sending a LRCM where B has 1 entry + +--- + routing layer: -the routing layer provides inter network communication between the SARP link -layer and ip (internet protocol) for exit traffic or hp (hidden protocol) for -SARP hidden services. replies to messages are sent back via the path they +the routing layer provides inter network communication between the LLARP link +layer and ip (internet protocol) for exit traffic or ap (anonymous protocol) for +hidden services. replies to messages are sent back via the path they originated from inside a LRDM. +for direct communication between routers a direct path MUST be used, these messages MUST NOT be sent on the link leyer. + obtain exit address message (OXAM) sent to an exit router to obtain a NAT ip address for ip exit traffic. @@ -280,6 +485,7 @@ replies are sent down the path that messages originate from. { A: "X", I: "<32 bytes signing public key for future communication>", + V: 0, X: lifetime_of_address_mapping_in_seconds_uint64, } @@ -292,6 +498,7 @@ ip address used for exit traffic. A: "G", E: "<16 byte big endian externally reachable ipv6 address>", I: "<32 bytes signing public key of requester>", + V: 0, Z: "<64 bytes signature using exit's signing key>" } @@ -302,6 +509,7 @@ reject exit address message (RXAM) B: backoff_milliseconds_uint64, I: "<32 bytes signing public key of requester>", R: "", + V: 0, Z: "<64 bytes signature signed by exit>" } @@ -317,6 +525,7 @@ y and z values in LRDM message respectively. { A: "T", P: path_id_uint64, + V: 0, X: "", Y: "<24 bytes nounce>", Z: "<64 bytes signature of entire message where Z is set to NUL>", @@ -328,6 +537,7 @@ transfer ip traffic for exit { A: "T", + V: 0, X: "", Z: "<64 bytes signature of previously provided signing key>" } @@ -337,6 +547,7 @@ find introduction message (FIM) { A: "F", S: "<64 bytes dht key>", + V: 0, T: transaction_id_uint64 } @@ -345,6 +556,7 @@ got introduction message (GIM) { A: "G", T: transaction_id_uint64, + V: 0, X: [ IS, IS, IS, ... ] } @@ -362,19 +574,20 @@ which uses a shared random source to obfuscate keyspace location. { A: "P", T: transaction_id_uint64, - X: [ IS, IS, IS, ... ] + V: 0, + X: [ IS, IS, IS, ... ], } acknoleged introduction message (AIM) -acknolege the publishing of a previous PIM, back off publishing for B ms. -nonzero B value indicates failure to publish. +acknolege the publishing of a previous PIM, X contains the backoff values in ms +for the previously provided IS, if backoff is 0 the operation was successfull { A: "A", - B: backoff_milliseconds_uint64, - P: number_of_IS_published_uint, T: transaction_id_uint64, + V: 0, + X: [ 0, 0, backoff, ...], } find router contact message (FRCM) @@ -383,21 +596,18 @@ find a router by public key { A: "F", - R: "<32 byte public key of router>", + K: "<32 byte public key of router>", T: transaction_id_uint64 + V: 0 } got router contact message (GRCM) +R is a list containing a single RC if found or is an empty list if not found +sent in reply to FRCM only { A: "G", - R: [RC, RC, RC ...], - T: transaction_id_uint64 -} - -store router contact message (SRCM) - -{ - A: "S", - R: RC + R: [RC], + T: transaction_id_uint64, + V: 0 } diff --git a/include/sarp/buffer.h b/include/sarp/buffer.h index 8fa152c2b..e5e318ee2 100644 --- a/include/sarp/buffer.h +++ b/include/sarp/buffer.h @@ -4,18 +4,14 @@ extern "C" { #endif #include -#include typedef struct sarp_buffer_t { - uint8_t * base; + char * base; size_t sz; - uint8_t * cur; + char * cur; } sarp_buffer_t; - static inline size_t sarp_buffer_size_left(sarp_buffer_t * buff) - { - return buff->sz - (buff->cur - buff->base); - } + size_t sarp_buffer_size_left(sarp_buffer_t * buff); #ifdef __cplusplus } diff --git a/include/sarp/crypto.h b/include/sarp/crypto.h index e26bac3a0..c5caf639d 100644 --- a/include/sarp/crypto.h +++ b/include/sarp/crypto.h @@ -1,41 +1,42 @@ #ifndef SARP_CRYPTO_H_ #define SARP_CRYPTO_H_ #include - +#include +#include #ifdef __cplusplus extern "C" { #endif #define PUBKEYSIZE 32 #define SECKEYSIZE 32 -#define SYMKEYSIZE 32 #define NOUNCESIZE 24 -#define SHAREDKEYSIZE 64 +#define SHAREDKEYSIZE 32 #define HASHSIZE 64 #define HMACSECSIZE 32 #define SIGSIZE 64 +#define TUNNOUNCESIZE 32 -typedef uint8_t sarp_pubkey_t[PUBKEYSIZE]; -typedef uint8_t sarp_seckey_t[SECKEYSIZE]; -typedef uint8_t sarp_symkey_t[SYMKEYSIZE]; -typedef uint8_t sarp_nounce_t[NOUNCESIZE]; -typedef uint8_t sarp_sharedkey_t[SHAREDKEYSIZE]; -typedef uint8_t sarp_hash_t[HASHSIZE]; -typedef uint8_t sarp_hmacsec_t[HMACSECSIZE]; -typedef uint8_t sarp_sig_t[SIGSIZE]; + typedef uint8_t sarp_pubkey_t[PUBKEYSIZE]; + typedef uint8_t sarp_seckey_t[SECKEYSIZE]; + typedef uint8_t sarp_nounce_t[NOUNCESIZE]; + typedef uint8_t sarp_sharedkey_t[SHAREDKEYSIZE]; + typedef uint8_t sarp_hash_t[HASHSIZE]; + typedef uint8_t sarp_hmacsec_t[HMACSECSIZE]; + typedef uint8_t sarp_sig_t[SIGSIZE]; + typedef uint8_t sarp_tunnel_nounce_t[TUNNOUNCESIZE]; -struct sarp_crypto -{ - int (*xchacha20)(sarp_buffer_t, sarp_symkey_t, sarp_nounce_t); - int (*dh_client)(sarp_sharedkey_t *, sarp_pubkey_t, sarp_seckey_t); - int (*dh_server)(sarp_sharedkey_t *, sarp_pubkey_t, sarp_seckey_t); - int (*hash)(sarp_hash_t *, sarp_buffer_t); - int (*mhac)(sarp_hash_t *, sarp_buffer_t, sarp_hmacsec_t); - int (*sign)(sarp_sig_t *, sarp_seckey_t, sarp_buffer_t); - int (*verify)(sarp_pubkey_t, sarp_buffer_t, sarp_sig_t); -}; + struct sarp_crypto + { + bool (*xchacha20)(sarp_buffer_t, sarp_sharedkey_t, sarp_nounce_t); + bool (*dh_client)(sarp_sharedkey_t *, sarp_pubkey_t, sarp_tunnel_nounce_t, sarp_seckey_t); + bool (*dh_server)(sarp_sharedkey_t *, sarp_pubkey_t, sarp_tunnel_nounce_t, sarp_seckey_t); + bool (*hash)(sarp_hash_t *, sarp_buffer_t); + bool (*hmac)(sarp_hash_t *, sarp_buffer_t, sarp_hmacsec_t); + bool (*sign)(sarp_sig_t *, sarp_seckey_t, sarp_buffer_t); + bool (*verify)(sarp_pubkey_t, sarp_buffer_t, sarp_sig_t); + }; -void sarp_crypto_libsodium_init(struct sarp_crypto * c); + void sarp_crypto_libsodium_init(struct sarp_crypto * c); #ifdef __cplusplus } diff --git a/include/sarp/ev.h b/include/sarp/ev.h index 91dfc84b7..0fe3d62c3 100644 --- a/include/sarp/ev.h +++ b/include/sarp/ev.h @@ -18,15 +18,17 @@ extern "C" { struct sarp_udp_listener { - const char * host; + char * host; uint16_t port; void * user; void * impl; - void (*recvfrom)(struct sarp_udp_listener *, struct sockaddr, uint8_t *, size_t); + void (*recvfrom)(struct sarp_udp_listener *, const struct sockaddr *, char *, ssize_t); + void (*closed)(struct sarp_udp_listener *); }; int sarp_ev_add_udp_listener(struct sarp_ev_loop * ev, struct sarp_udp_listener * listener); - int srap_ev_close_udp_listener(struct sarp_ev_loop * ev, struct sarp_udp_listener * listener); + + int sarp_ev_close_udp_listener(struct sarp_udp_listener * listener); #ifdef __cplusplus } diff --git a/include/sarp/time.h b/include/sarp/time.h new file mode 100644 index 000000000..88e084938 --- /dev/null +++ b/include/sarp/time.h @@ -0,0 +1,14 @@ +#ifndef SARP_TIME_H +#define SARP_TIME_H +#include +#ifdef __cplusplus +extern "C" { +#endif + + uint64_t sarp_time_now_ms(); + uint64_t sarp_time_now_sec(); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libsarp/address_info.c b/libsarp/address_info.c index 2a637a178..692f60557 100644 --- a/libsarp/address_info.c +++ b/libsarp/address_info.c @@ -4,9 +4,9 @@ bool sarp_address_info_bencode(struct sarp_address_info * ai, sarp_buffer_t * buff) { - uint8_t * ptr = buff->cur; + char * ptr = buff->cur; size_t sz = sarp_buffer_size_left(buff); - uint8_t * end = ptr + sz; + char * end = ptr + sz; int r = 0; r = snprintf(ptr, (end - ptr), "d1:ci%de1:e32:", ai->rank); if (r == -1) return false; diff --git a/libsarp/buffer.cpp b/libsarp/buffer.cpp new file mode 100644 index 000000000..f99c00cb9 --- /dev/null +++ b/libsarp/buffer.cpp @@ -0,0 +1,16 @@ +#include + +extern "C" { + + size_t sarp_buffer_size_left(sarp_buffer_t * buff) + { + std::ptrdiff_t diff = buff->cur - buff->base; + if(diff < 0) + { + return 0; + } + else if(diff > buff->sz) return 0; + else return buff->sz - diff; + } + +} diff --git a/libsarp/config.cpp b/libsarp/config.cpp index e45cab00d..0d1b1c2e5 100644 --- a/libsarp/config.cpp +++ b/libsarp/config.cpp @@ -7,10 +7,10 @@ namespace sarp { template - static Section find_section(Config & c, const std::string & name, const Section & sect) + static Section find_section(Config & c, const std::string & name, const Section & fallback) { if(c.sections.find(name) == c.sections.end()) - return sect; + return fallback; return c.sections[name].values; } @@ -26,6 +26,7 @@ namespace sarp router = find_section(top, "router", section_t{}); network = find_section(top, "network", section_t{}); netdb = find_section(top, "netdb", section_t{}); + links = find_section(top, "links", section_t{}); return true; } return false; @@ -64,6 +65,7 @@ extern "C" { std::map sections = { {"router", conf->impl.router}, {"network", conf->impl.network}, + {"links", conf->impl.links}, {"netdb", conf->impl.netdb} }; for(const auto & section : sections) diff --git a/libsarp/config.hpp b/libsarp/config.hpp index 6d5bd9376..f500e2c7b 100644 --- a/libsarp/config.hpp +++ b/libsarp/config.hpp @@ -14,6 +14,7 @@ namespace sarp section_t router; section_t network; section_t netdb; + section_t links; bool Load(const char * fname); diff --git a/libsarp/crypto_libsodium.cpp b/libsarp/crypto_libsodium.cpp index 7ef42486a..a38cd6292 100644 --- a/libsarp/crypto_libsodium.cpp +++ b/libsarp/crypto_libsodium.cpp @@ -2,42 +2,76 @@ #include #include #include +#include namespace sarp { namespace sodium { - int xchacha20(sarp_buffer_t buff, sarp_symkey_t k, sarp_nounce_t n) + bool xchacha20(sarp_buffer_t buff, sarp_sharedkey_t k, sarp_nounce_t n) { - return crypto_stream_xchacha20_xor(buff.base, buff.base, buff.sz, n, k); + uint8_t * base = (uint8_t*)buff.base; + return crypto_stream_xchacha20_xor(base, base, buff.sz, n, k) == 0; } - int dh(sarp_sharedkey_t * shared, uint8_t * client_pk, uint8_t * server_pk, uint8_t * remote_key, uint8_t * local_key) + bool dh(sarp_sharedkey_t * shared, uint8_t * client_pk, uint8_t * server_pk, uint8_t * remote_key, uint8_t * local_key) { uint8_t * out = *shared; - const size_t outsz = sizeof(sarp_sharedkey_t); + const size_t outsz = SHAREDKEYSIZE; crypto_generichash_state h; - if(crypto_scalarmult(out, local_key, remote_key) == -1) return -1; + if(crypto_scalarmult(out, local_key, remote_key) == -1) return false; crypto_generichash_init(&h, NULL, 0U, outsz); crypto_generichash_update(&h, client_pk, sizeof(sarp_pubkey_t)); crypto_generichash_update(&h, server_pk, sizeof(sarp_pubkey_t)); crypto_generichash_update(&h, out, crypto_scalarmult_BYTES); crypto_generichash_final(&h, out, outsz); - return 0; + return true; } - int dh_client(sarp_sharedkey_t * shared, sarp_pubkey_t pk, sarp_seckey_t sk) + bool dh_client(sarp_sharedkey_t * shared, sarp_pubkey_t pk, sarp_tunnel_nounce_t n, sarp_seckey_t sk) { sarp_pubkey_t local_pk; crypto_scalarmult_base(local_pk, sk); - return dh(shared, local_pk, pk, pk, sk); + if(dh(shared, local_pk, pk, pk, sk)) + { + return crypto_generichash(*shared, SHAREDKEYSIZE, *shared, SHAREDKEYSIZE, n, TUNNOUNCESIZE) != -1; + } + return false; } - int dh_server(sarp_sharedkey_t * shared, sarp_pubkey_t pk, sarp_seckey_t sk) + bool dh_server(sarp_sharedkey_t * shared, sarp_pubkey_t pk, sarp_tunnel_nounce_t n, sarp_seckey_t sk) { sarp_pubkey_t local_pk; crypto_scalarmult_base(local_pk, sk); - return dh(shared, pk, local_pk, pk, sk); + if(dh(shared, pk, local_pk, pk, sk)) + { + return crypto_generichash(*shared, SHAREDKEYSIZE, *shared, SHAREDKEYSIZE, n, TUNNOUNCESIZE) != -1; + } + return false; + } + + bool hash(sarp_hash_t * result, sarp_buffer_t buff) + { + const uint8_t * base = (const uint8_t *) buff.base; + return crypto_generichash(*result, HASHSIZE, base, buff.sz, nullptr, 0) != -1; + } + + bool hmac(sarp_hash_t * result, sarp_buffer_t buff, sarp_seckey_t secret) + { + const uint8_t * base = (const uint8_t *) buff.base; + return crypto_generichash(*result, sizeof(sarp_hash_t), base, buff.sz, secret, HMACSECSIZE) != -1; + } + + bool sign(sarp_sig_t * result, sarp_seckey_t secret, sarp_buffer_t buff) + { + const uint8_t * base = (const uint8_t *) buff.base; + return crypto_sign_detached(*result, nullptr, base, buff.sz, secret) != -1; + } + + bool verify(sarp_pubkey_t pub, sarp_buffer_t buff, sarp_sig_t sig) + { + const uint8_t * base = (const uint8_t *) buff.base; + return crypto_sign_verify_detached(sig, base, buff.sz, pub) != -1; } } } @@ -48,5 +82,9 @@ extern "C" { c->xchacha20 = sarp::sodium::xchacha20; c->dh_client = sarp::sodium::dh_client; c->dh_server = sarp::sodium::dh_server; + c->hash = sarp::sodium::hash; + c->hmac = sarp::sodium::hmac; + c->sign = sarp::sodium::sign; + c->verify = sarp::sodium::verify; } } diff --git a/libsarp/ev.cpp b/libsarp/ev.cpp index 6ca1840ce..6679918dd 100644 --- a/libsarp/ev.cpp +++ b/libsarp/ev.cpp @@ -15,8 +15,22 @@ namespace sarp { uv_udp_t _handle; struct sarp_udp_listener * listener; + + void recvfrom(const struct sockaddr * addr, char * buff, ssize_t sz) + { + if(listener->recvfrom) + listener->recvfrom(listener, addr, buff, sz); + } - uv_udp_t * handle() { return &_handle; } + /** called after closed */ + void closed() + { + if(listener->closed) + listener->closed(listener); + listener->impl = nullptr; + } + + uv_udp_t * udp() { return &_handle; } }; static void udp_alloc_cb(uv_handle_t * h, size_t sz, uv_buf_t * buf) @@ -28,6 +42,15 @@ namespace sarp static void udp_recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) { udp_listener * l = static_cast(handle->data); + l->recvfrom(addr, buf->base, nread); + sarp_g_mem.free(buf->base); + } + + static void udp_close_cb(uv_handle_t * handle) + { + udp_listener * l = static_cast(handle->data); + l->closed(); + sarp_g_mem.free(l); } } @@ -64,18 +87,37 @@ extern "C" { int ret = 0; sarp::udp_listener * l = static_cast(sarp_g_mem.malloc(sizeof(sarp::udp_listener))); listener->impl = l; - l->handle()->data = l; + l->udp()->data = l; l->listener = listener; - ret = uv_udp_init(ev->loop(), l->handle()); + ret = uv_udp_init(ev->loop(), l->udp()); if (ret == 0) { - ret = uv_udp_bind(l->handle(), (const sockaddr *)&addr, 0); + ret = uv_udp_bind(l->udp(), (const sockaddr *)&addr, 0); if (ret == 0) { - ret = uv_udp_recv_start(l->handle(), sarp::udp_alloc_cb, sarp::udp_recv_cb); + ret = uv_udp_recv_start(l->udp(), sarp::udp_alloc_cb, sarp::udp_recv_cb); } } + return ret; + } + + int sarp_ev_close_udp_listener(struct sarp_udp_listener * listener) + { + int ret = -1; + if(listener) + { + sarp::udp_listener * l = static_cast(listener->impl); + if(l) + { + if(!uv_udp_recv_stop(l->udp())) + { + l->closed(); + sarp_g_mem.free(l); + ret = 0; + } + } + } + return ret; } - } diff --git a/libsarp/link.cpp b/libsarp/link.cpp index 406048e8c..982e0a1d1 100644 --- a/libsarp/link.cpp +++ b/libsarp/link.cpp @@ -1,5 +1,6 @@ #include "link.hpp" #include +#include bool operator < (const sockaddr_in6 addr0, const sockaddr_in6 addr1) { @@ -9,13 +10,13 @@ bool operator < (const sockaddr_in6 addr0, const sockaddr_in6 addr1) namespace sarp { - static void link_recv_from(struct sarp_udp_listener * l, struct sockaddr src, uint8_t * buff, size_t sz) + static void link_recv_from(struct sarp_udp_listener * l, const struct sockaddr * src, char * buff, ssize_t sz) { - if(src.sa_family == AF_INET6) + if(src && src->sa_family == AF_INET6) { Link * link = static_cast(l->user); struct sockaddr_in6 remote; - memcpy(&remote, &src, sizeof(sockaddr_in6)); + memcpy(&remote, src, sizeof(sockaddr_in6)); auto itr = link->sessions.find(remote); if(itr == link->sessions.end()) { @@ -31,5 +32,18 @@ namespace sarp listener.recvfrom = link_recv_from; } - + PeerSession::PeerSession(sarp_crypto * crypto, sockaddr_in6 remote) : + lastRX(0), + remoteAddr(remote), + _crypto(crypto), + state(eHandshakeInboundInit) + { + memset(remotePubkey, 0, sizeof(remotePubkey)); + memset(sessionKey, 0, sizeof(sessionKey)); + } + + void PeerSession::RecvFrom(const char * buff, ssize_t sz) + { + lastRX = sarp_time_now_ms(); + } } diff --git a/libsarp/link.hpp b/libsarp/link.hpp index 3bdf2392f..79b356cde 100644 --- a/libsarp/link.hpp +++ b/libsarp/link.hpp @@ -24,10 +24,13 @@ namespace sarp enum State { - eHandshake0, - eHandshake1, - eHandshake2, - eHandshake3, + eStateNULL, + eHandshakeInboundInit, + eHandshakeOutboundInit, + eHandshakeInboundRepliedInit, + eHandshakeOutboundGotReply, + eHandshakeInboundGotAck, + eHandshakeOutboundGotAck, eEstablished, eTimeout }; @@ -42,9 +45,9 @@ namespace sarp PeerSession & operator=(const PeerSession & other); - void SendTo(Link * link, const uint8_t * buff, std::size_t sz); + void SendTo(Link * link, const char * buff, std::size_t sz); - void RecvFrom(const uint8_t * buff, std::size_t sz); + void RecvFrom(const char * buff, ssize_t sz); }; @@ -65,6 +68,8 @@ namespace sarp sarp_udp_listener listener; }; + + typedef std::unique_ptr Link_ptr; } #endif diff --git a/libsarp/router.cpp b/libsarp/router.cpp index cc6d4875c..aec9e7a44 100644 --- a/libsarp/router.cpp +++ b/libsarp/router.cpp @@ -1,46 +1,59 @@ #include #include #include "link.hpp" -#include +#include +#include "str.hpp" namespace sarp { + void router_iter_config(sarp_config_iterator * iter, const char * section, const char * key, const char * val); + + struct Router { - std::vector Links; - - static void iter_config(sarp_config_iterator * iter, const char * section, const char * key, const char * val) + std::list Links; + sarp_crypto * crypto; + void Close() { - sarp_router * self = static_cast(iter->user); + for(auto & itr : Links) + { + sarp_ev_close_udp_listener(&itr->listener); + } + Links.clear(); } - - bool Configured() { + if(Links.size()) return true; return false; } }; } + extern "C" { struct sarp_router { sarp::Router impl; + sarp_crypto crypto; }; void sarp_init_router(struct sarp_router ** router) { *router = static_cast(sarp_g_mem.malloc(sizeof(sarp_router))); + if(*router) + { + sarp_crypto_libsodium_init(&(*router)->crypto); + } } int sarp_configure_router(struct sarp_router * router, struct sarp_config * conf) { sarp_config_iterator iter; iter.user = router; - iter.visit = sarp::Router::iter_config; + iter.visit = sarp::router_iter_config; sarp_config_iter(conf, iter); return router->impl.Configured() ? 0 : -1; } @@ -48,13 +61,37 @@ extern "C" { void sarp_run_router(struct sarp_router * router, struct sarp_ev_loop * loop) { for(auto & iter : router->impl.Links) - sarp_ev_add_udp_listener(loop, &iter.listener); + sarp_ev_add_udp_listener(loop, &iter->listener); } void sarp_free_router(struct sarp_router ** router) { if(*router) + { + sarp_router * r = *router; + r->impl.Close(); sarp_g_mem.free(*router); + } *router = nullptr; } } + +namespace sarp +{ + + void router_iter_config(sarp_config_iterator * iter, const char * section, const char * key, const char * val) + { + sarp_router * self = static_cast(iter->user); + if (streq(section, "links")) + { + if(streq(val, "ip")) + { + self->impl.Links.push_back(std::make_unique(&self->crypto)); + } + else if (streq(val, "eth")) + { + /** todo: ethernet link */ + } + } + } +} diff --git a/libsarp/str.hpp b/libsarp/str.hpp new file mode 100644 index 000000000..7a32517c2 --- /dev/null +++ b/libsarp/str.hpp @@ -0,0 +1,20 @@ +#ifndef LIBSARP_STR_HPP +#define LIBSARP_STR_HPP +#include + +namespace sarp +{ + static bool streq(const char * s1, const char * s2) + { + size_t sz1 = strlen(s1); + size_t sz2 = strlen(s2); + if (sz1 == sz2) + { + return strncmp(s1, s2, sz1) == 0; + } + else + return false; + } +} + +#endif diff --git a/libsarp/time.cpp b/libsarp/time.cpp new file mode 100644 index 000000000..0202f2596 --- /dev/null +++ b/libsarp/time.cpp @@ -0,0 +1,25 @@ +#include +#include + +namespace sarp +{ + typedef std::chrono::steady_clock clock_t; + + template + static uint64_t time_since_epoch() + { + return std::chrono::duration_cast(sarp::clock_t::now().time_since_epoch()).count(); + } +} + +extern "C" { + uint64_t sarp_time_now_ms() + { + return sarp::time_since_epoch(); + } + + uint64_t sarp_time_now_sec() + { + return sarp::time_since_epoch(); + } +}