lnsocket

A minimal C library for connecting to the lightning network
git clone git://jb55.com/lnsocket
Log | Files | Refs | Submodules | README | LICENSE

commit ba5e344f0adc2dd9cce774a970d64b27176b0b47
parent f9791a1d58272c921861c08a361a0006a00b82bc
Author: William Casarin <jb55@jb55.com>
Date:   Sun, 16 Jan 2022 07:42:34 -0800

initial test program, make more lib friendly

Diffstat:
M.gitignore | 2+-
MMakefile | 11++++++-----
Acursor.h | 398+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aerror.c | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Aerror.h | 32++++++++++++++++++++++++++++++++
Ahandshake.c | 433+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mhandshake.h | 14+++++++++++---
Mlnsocket.c | 523++++++++++---------------------------------------------------------------------
Alnsocket.h | 12++++++++++++
Alnsocket_internal.h | 11+++++++++++
Atest.c | 22++++++++++++++++++++++
Atypedefs.h | 13+++++++++++++
Dtypes.h | 8--------
Avarint.c | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avarint.h | 14++++++++++++++
15 files changed, 1139 insertions(+), 478 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -4,4 +4,4 @@ /tags *.o /configurator -/lnsocket +/test diff --git a/Makefile b/Makefile @@ -3,10 +3,10 @@ CFLAGS=-Wall -g -Og -Ideps/secp256k1/include -Ideps/libsodium/src/libsodium/incl LDFLAGS= ARS=deps/secp256k1/.libs/libsecp256k1.a deps/libsodium/src/libsodium/.libs/libsodium.a -OBJS=sha256.o hkdf.o hmac.o sha512.o lnsocket.o +OBJS=sha256.o hkdf.o hmac.o sha512.o lnsocket.o error.o handshake.o DEPS=$(OBJS) config.h -all: lnsocket +all: test config.h: configurator ./configurator > $@ @@ -37,14 +37,15 @@ deps/libsodium/src/libsodium/.libs/libsodium.a: deps/libsodium/configure cd deps/libsodium/src/libsodium; \ make -j2 libsodium.la -lnsocket: $(DEPS) $(ARS) - $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ +test: test.o $(DEPS) $(ARS) + @echo "ld test" + @$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ tags: fake find . -name '*.c' -or -name '*.h' | xargs ctags clean: fake - rm -f lnsocket $(DEPS) + rm -f test lnsocket $(DEPS) deepclean: clean rm -f $(ARS) deps/secp256k1/src/libsecp256k1-config.h diff --git a/cursor.h b/cursor.h @@ -0,0 +1,398 @@ + + +#ifndef PROTOVERSE_CURSOR_H +#define PROTOVERSE_CURSOR_H + +#include "typedefs.h" +#include "varint.h" + +#include <stdio.h> +#include <assert.h> +#include <string.h> + +#define unlikely(x) __builtin_expect((x),0) +#define likely(x) __builtin_expect((x),1) + +struct cursor { + unsigned char *start; + unsigned char *p; + unsigned char *end; +}; + +struct array { + struct cursor cur; + unsigned int elem_size; +}; + +static inline void reset_cursor(struct cursor *cursor) +{ + cursor->p = cursor->start; +} + +static inline void wipe_cursor(struct cursor *cursor) +{ + reset_cursor(cursor); + memset(cursor->start, 0, cursor->end - cursor->start); +} + +static inline void make_cursor(u8 *start, u8 *end, struct cursor *cursor) +{ + cursor->start = start; + cursor->p = start; + cursor->end = end; +} + +static inline void make_array(struct array *a, u8* start, u8 *end, unsigned int elem_size) +{ + make_cursor(start, end, &a->cur); + a->elem_size = elem_size; +} + +static inline int cursor_eof(struct cursor *c) +{ + return c->p == c->end; +} + +static inline void *cursor_malloc(struct cursor *mem, unsigned long size) +{ + void *ret; + + if (mem->p + size > mem->end) { + return NULL; + } + + ret = mem->p; + mem->p += size; + + return ret; +} + +static inline void *cursor_alloc(struct cursor *mem, unsigned long size) +{ + void *ret; + if (!(ret = cursor_malloc(mem, size))) { + return 0; + } + + memset(ret, 0, size); + return ret; +} + +static inline int cursor_slice(struct cursor *mem, struct cursor *slice, size_t size) +{ + u8 *p; + if (!(p = cursor_alloc(mem, size))) { + return 0; + } + make_cursor(p, mem->p, slice); + return 1; +} + + +static inline void copy_cursor(struct cursor *src, struct cursor *dest) +{ + dest->start = src->start; + dest->p = src->p; + dest->end = src->end; +} + +static inline int pull_byte(struct cursor *cursor, u8 *c) +{ + if (unlikely(cursor->p + 1 > cursor->end)) + return 0; + + *c = *cursor->p; + cursor->p++; + + return 1; +} + +static inline int cursor_pull_c_str(struct cursor *cursor, const char **str) +{ + *str = (const char*)cursor->p; + + for (; cursor->p < cursor->end; cursor->p++) { + if (*cursor->p == 0) { + cursor->p++; + return 1; + } + } + + return 0; +} + + +static inline int cursor_push_byte(struct cursor *cursor, u8 c) +{ + if (unlikely(cursor->p + 1 > cursor->end)) { + return 0; + } + + *cursor->p = c; + cursor->p++; + + return 1; +} + +static inline int cursor_pull(struct cursor *cursor, u8 *data, int len) +{ + if (unlikely(cursor->p + len > cursor->end)) { + return 0; + } + + memcpy(data, cursor->p, len); + cursor->p += len; + + return 1; +} + +static inline int pull_data_into_cursor(struct cursor *cursor, + struct cursor *dest, + unsigned char **data, + int len) +{ + int ok; + + if (unlikely(dest->p + len > dest->end)) { + printf("not enough room in dest buffer\n"); + return 0; + } + + ok = cursor_pull(cursor, dest->p, len); + if (!ok) return 0; + + *data = dest->p; + dest->p += len; + + return 1; +} + +static inline int cursor_dropn(struct cursor *cur, int size, int n) +{ + if (n == 0) + return 1; + + if (unlikely(cur->p - size*n < cur->start)) { + return 0; + } + + cur->p -= size*n; + return 1; +} + +static inline int cursor_drop(struct cursor *cur, int size) +{ + return cursor_dropn(cur, size, 1); +} + +static inline unsigned char *cursor_topn(struct cursor *cur, int len, int n) +{ + n += 1; + if (unlikely(cur->p - len*n < cur->start)) { + return NULL; + } + return cur->p - len*n; +} + +static inline unsigned char *cursor_top(struct cursor *cur, int len) +{ + if (unlikely(cur->p - len < cur->start)) { + return NULL; + } + return cur->p - len; +} + +static inline int cursor_top_int(struct cursor *cur, int *i) +{ + u8 *p; + if (unlikely(!(p = cursor_top(cur, sizeof(*i))))) { + return 0; + } + *i = *((int*)p); + return 1; +} + +static inline int cursor_pop(struct cursor *cur, u8 *data, int len) +{ + if (unlikely(cur->p - len < cur->start)) { + return 0; + } + + cur->p -= len; + memcpy(data, cur->p, len); + + return 1; +} + +static inline int cursor_push(struct cursor *cursor, u8 *data, int len) +{ + if (unlikely(cursor->p + len >= cursor->end)) { + return 0; + } + + if (cursor->p != data) + memcpy(cursor->p, data, len); + + cursor->p += len; + + return 1; +} + +static inline int cursor_push_int(struct cursor *cursor, int i) +{ + return cursor_push(cursor, (u8*)&i, sizeof(i)); +} + +static inline size_t cursor_count(struct cursor *cursor, size_t elem_size) +{ + return (cursor->p - cursor->start)/elem_size; +} + +/* TODO: push_varint */ +static inline int push_varint(struct cursor *cursor, int n) +{ + int ok, len; + unsigned char b; + len = 0; + + while (1) { + b = (n & 0xFF) | 0x80; + n >>= 7; + if (n == 0) { + b &= 0x7F; + ok = cursor_push_byte(cursor, b); + len++; + if (!ok) return 0; + break; + } + + ok = cursor_push_byte(cursor, b); + len++; + if (!ok) return 0; + } + + return len; +} + +/* TODO: pull_varint */ +static inline int pull_varint(struct cursor *cursor, int *n) +{ + int ok, i; + unsigned char b; + *n = 0; + + for (i = 0;; i++) { + ok = pull_byte(cursor, &b); + if (!ok) return 0; + + *n |= ((int)b & 0x7F) << (i * 7); + + /* is_last */ + if ((b & 0x80) == 0) { + return i+1; + } + + if (i == 4) return 0; + } + + return 0; +} + +static inline int cursor_pull_int(struct cursor *cursor, int *i) +{ + return cursor_pull(cursor, (u8*)i, sizeof(*i)); +} + +static inline int cursor_push_u16(struct cursor *cursor, u16 i) +{ + return cursor_push(cursor, (u8*)&i, sizeof(i)); +} + +static inline void *index_cursor(struct cursor *cursor, unsigned int index, int elem_size) +{ + u8 *p; + p = &cursor->start[elem_size * index]; + + if (unlikely(p >= cursor->end)) + return NULL; + + return (void*)p; +} + + +static inline int push_sized_str(struct cursor *cursor, const char *str, int len) +{ + return cursor_push(cursor, (u8*)str, len); +} + +static inline int cursor_push_str(struct cursor *cursor, const char *str) +{ + return cursor_push(cursor, (u8*)str, strlen(str)); +} + +static inline int cursor_push_c_str(struct cursor *cursor, const char *str) +{ + return cursor_push_str(cursor, str) && cursor_push_byte(cursor, 0); +} + +/* TODO: push varint size */ +static inline int push_prefixed_str(struct cursor *cursor, const char *str) +{ + int ok, len; + len = strlen(str); + ok = push_varint(cursor, len); + if (!ok) return 0; + return push_sized_str(cursor, str, len); +} + +static inline int pull_prefixed_str(struct cursor *cursor, struct cursor *dest_buf, const char **str) +{ + int len, ok; + + ok = pull_varint(cursor, &len); + if (!ok) return 0; + + if (unlikely(dest_buf->p + len > dest_buf->end)) { + return 0; + } + + ok = pull_data_into_cursor(cursor, dest_buf, (unsigned char**)str, len); + if (!ok) return 0; + + ok = cursor_push_byte(dest_buf, 0); + + return 1; +} + +static inline int cursor_remaining_capacity(struct cursor *cursor) +{ + return cursor->end - cursor->p; +} + + +#define max(a,b) ((a) > (b) ? (a) : (b)) +static inline void cursor_print_around(struct cursor *cur, int range) +{ + unsigned char *c; + + printf("[%ld/%ld]\n", cur->p - cur->start, cur->end - cur->start); + + c = max(cur->p - range, cur->start); + for (; c < cur->end && c < (cur->p + range); c++) { + printf("%02x", *c); + } + printf("\n"); + + c = max(cur->p - range, cur->start); + for (; c < cur->end && c < (cur->p + range); c++) { + if (c == cur->p) { + printf("^"); + continue; + } + printf(" "); + } + printf("\n"); +} +#undef max + +#endif diff --git a/error.c b/error.c @@ -0,0 +1,49 @@ + +#include "error.h" + +#include <stdlib.h> +#include <stdarg.h> + +int note_error_(struct errors *errs_, const char *fmt, ...) +{ + static char buf[512]; + struct error err; + struct cursor *errs; + va_list ap; + + errs = &errs_->cur; + + if (errs_->enabled == 0) + return 0; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + err.msg = buf; + + if (!cursor_push_error(errs, &err)) { + fprintf(stderr, "arena OOM when recording error, "); + fprintf(stderr, "errs->p at %ld, remaining %ld, strlen %ld\n", + errs->p - errs->start, errs->end - errs->p, strlen(buf)); + } + + return 0; +} + +void print_error_backtrace(struct errors *errors) +{ + struct cursor errs; + struct error err; + + copy_cursor(&errors->cur, &errs); + errs.p = errs.start; + + while (errs.p < errors->cur.p) { + if (!cursor_pull_error(&errs, &err)) { + printf("backtrace: couldn't pull error\n"); + return; + } + printf("%s\n", err.msg); + } +} diff --git a/error.h b/error.h @@ -0,0 +1,32 @@ + +#ifndef LNSOCKET_ERROR_H +#define LNSOCKET_ERROR_H + +#include "cursor.h" + +struct error { + const char *msg; +}; + +struct errors { + struct cursor cur; + int enabled; +}; + +#define note_error(errs, fmt, ...) note_error_(errs, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) + +static inline int cursor_push_error(struct cursor *cur, struct error *err) +{ + return cursor_push_c_str(cur, err->msg); +} + +static inline int cursor_pull_error(struct cursor *cur, struct error *err) +{ + return cursor_pull_c_str(cur, &err->msg); +} + +int note_error_(struct errors *errs, const char *fmt, ...); + +void print_error_backtrace(struct errors *errors); + +#endif /* LNSOCKET_ERROR_H */ diff --git a/handshake.c b/handshake.c @@ -0,0 +1,433 @@ + +#include "compiler.h" +#include "endian.h" +#include "error.h" +#include "handshake.h" +#include "hkdf.h" +#include "lnsocket_internal.h" +#include "sha256.h" +#include <errno.h> +#include <secp256k1_ecdh.h> +#include <unistd.h> +#include <sodium/randombytes.h> + +struct keypair generate_key(secp256k1_context *ctx) +{ + struct keypair k; + + do { + randombytes_buf(k.priv.secret.data, sizeof(k.priv.secret.data)); + } while (!secp256k1_ec_pubkey_create(ctx, &k.pub.pubkey, + k.priv.secret.data)); + return k; +} + + +/* h = SHA-256(h || data) */ +static void sha_mix_in(struct sha256 *h, const void *data, size_t len) +{ + struct sha256_ctx shactx; + + sha256_init(&shactx); + sha256_update(&shactx, h->u.u8, sizeof(*h)); + sha256_update(&shactx, data, len); + sha256_done(&shactx, h); +} + +/* h = SHA-256(h || pub.serializeCompressed()) */ +static void sha_mix_in_key(secp256k1_context *ctx, struct sha256 *h, + const struct pubkey *key) +{ + u8 der[PUBKEY_CMPR_LEN]; + size_t len = sizeof(der); + + secp256k1_ec_pubkey_serialize(ctx, der, &len, &key->pubkey, + SECP256K1_EC_COMPRESSED); + assert(len == sizeof(der)); + sha_mix_in(h, der, sizeof(der)); +} + +/* out1, out2 = HKDF(in1, in2)` */ +static void hkdf_two_keys(struct secret *out1, struct secret *out2, + const struct secret *in1, + const void *in2, size_t in2_size) +{ + /* BOLT #8: + * + * * `HKDF(salt,ikm)`: a function defined in `RFC 5869`<sup>[3](#reference-3)</sup>, + * evaluated with a zero-length `info` field + * * All invocations of `HKDF` implicitly return 64 bytes + * of cryptographic randomness using the extract-and-expand + * component of the `HKDF`. + */ + struct secret okm[2]; + + hkdf_sha256(okm, sizeof(okm), in1, sizeof(*in1), in2, in2_size, + NULL, 0); + *out1 = okm[0]; + *out2 = okm[1]; +} + + +static void le64_nonce(unsigned char *npub, u64 nonce) +{ + /* BOLT #8: + * + * ...with nonce `n` encoded as 32 zero bits, followed by a + * *little-endian* 64-bit value. Note: this follows the Noise Protocol + * convention, rather than our normal endian + */ + le64 le_nonce = cpu_to_le64(nonce); + const size_t zerolen = crypto_aead_chacha20poly1305_ietf_NPUBBYTES - sizeof(le_nonce); + + BUILD_ASSERT(crypto_aead_chacha20poly1305_ietf_NPUBBYTES >= sizeof(le_nonce)); + /* First part is 0, followed by nonce. */ + memset(npub, 0, zerolen); + memcpy(npub + zerolen, &le_nonce, sizeof(le_nonce)); +} + +/* BOLT #8: + * * `encryptWithAD(k, n, ad, plaintext)`: outputs `encrypt(k, n, ad, + * plaintext)` + * * Where `encrypt` is an evaluation of `ChaCha20-Poly1305` (IETF + * variant) with the passed arguments, with nonce `n` + */ +static void encrypt_ad(const struct secret *k, u64 nonce, + const void *additional_data, size_t additional_data_len, + const void *plaintext, size_t plaintext_len, + void *output, size_t outputlen) +{ + unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; + unsigned long long clen; + int ret; + + assert(outputlen == plaintext_len + crypto_aead_chacha20poly1305_ietf_ABYTES); + le64_nonce(npub, nonce); + BUILD_ASSERT(sizeof(*k) == crypto_aead_chacha20poly1305_ietf_KEYBYTES); + + ret = crypto_aead_chacha20poly1305_ietf_encrypt( + output, &clen, memcheck(plaintext, plaintext_len), + plaintext_len, additional_data, additional_data_len, + NULL, npub, k->data); + + assert(ret == 0); + assert(clen == plaintext_len + crypto_aead_chacha20poly1305_ietf_ABYTES); +} + +static inline void check_act_one(const struct act_one *act1) +{ + /* BOLT #8: + * + * : 1 byte for the handshake version, 33 bytes for the compressed + * ephemeral public key of the initiator, and 16 bytes for the + * `poly1305` tag. + */ + BUILD_ASSERT(sizeof(act1->v) == 1); + BUILD_ASSERT(sizeof(act1->pubkey) == 33); + BUILD_ASSERT(sizeof(act1->tag) == 16); +} + +static void print_hex(u8 *bytes, int len) { + int i; + for (i = 0; i < len; ++i) { + printf("%02x", bytes[i]); + } +} + +void new_handshake(secp256k1_context *secp, struct handshake *handshake, + const struct pubkey *responder_id) +{ + /* BOLT #8: + * + * Before the start of Act One, both sides initialize their + * per-sessions state as follows: + * + * 1. `h = SHA-256(protocolName)` + * * where `protocolName = "Noise_XK_secp256k1_ChaChaPoly_SHA256"` + * encoded as an ASCII string + */ + sha256(&handshake->h, "Noise_XK_secp256k1_ChaChaPoly_SHA256", + strlen("Noise_XK_secp256k1_ChaChaPoly_SHA256")); + + /* BOLT #8: + * + * 2. `ck = h` + */ + BUILD_ASSERT(sizeof(handshake->h) == sizeof(handshake->ck)); + memcpy(&handshake->ck, &handshake->h, sizeof(handshake->ck)); + + /* BOLT #8: + * + * 3. `h = SHA-256(h || prologue)` + * * where `prologue` is the ASCII string: `lightning` + */ + sha_mix_in(&handshake->h, "lightning", strlen("lightning")); + + /* BOLT #8: + * + * As a concluding step, both sides mix the responder's public key + * into the handshake digest: + * + * * The initiating node mixes in the responding node's static public + * key serialized in Bitcoin's compressed format: + * * `h = SHA-256(h || rs.pub.serializeCompressed())` + * + * * The responding node mixes in their local static public key + * serialized in Bitcoin's compressed format: + * * `h = SHA-256(h || ls.pub.serializeCompressed())` + */ + sha_mix_in_key(secp, &handshake->h, responder_id); +} + +static void print_act_two(struct act_two *two) +{ + printf("ACT2 v %d pubkey ", two->v); + print_hex(two->pubkey, sizeof(two->pubkey)); + printf(" tag "); + print_hex(two->tag, sizeof(two->tag)); + printf("\n"); +} + +/* BOLT #8: + * * `decryptWithAD(k, n, ad, ciphertext)`: outputs `decrypt(k, n, ad, + * ciphertext)` + * * Where `decrypt` is an evaluation of `ChaCha20-Poly1305` (IETF + * variant) with the passed arguments, with nonce `n` + */ +static int decrypt(const struct secret *k, u64 nonce, + const void *additional_data, size_t additional_data_len, + const void *ciphertext, size_t ciphertext_len, + void *output, size_t outputlen) +{ + unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; + unsigned long long mlen; + + assert(outputlen == ciphertext_len - crypto_aead_chacha20poly1305_ietf_ABYTES); + + le64_nonce(npub, nonce); + BUILD_ASSERT(sizeof(*k) == crypto_aead_chacha20poly1305_ietf_KEYBYTES); + if (crypto_aead_chacha20poly1305_ietf_decrypt(output, &mlen, NULL, + memcheck(ciphertext, ciphertext_len), + ciphertext_len, + additional_data, additional_data_len, + npub, k->data) != 0) { + return 0; + } + + assert(mlen == ciphertext_len - crypto_aead_chacha20poly1305_ietf_ABYTES); + return 1; +} + +static int act_three_initiator(struct lnsocket *ln, struct handshake *h) +{ + u8 spub[PUBKEY_CMPR_LEN]; + size_t len = sizeof(spub); + + /* BOLT #8: + * 1. `c = encryptWithAD(temp_k2, 1, h, s.pub.serializeCompressed())` + * * where `s` is the static public key of the initiator + */ + secp256k1_ec_pubkey_serialize(ln->secp, spub, &len, + &ln->key.pub.pubkey, + SECP256K1_EC_COMPRESSED); + encrypt_ad(&h->temp_k, 1, &h->h, sizeof(h->h), spub, sizeof(spub), + h->act3.ciphertext, sizeof(h->act3.ciphertext)); + + /* BOLT #8: + * 2. `h = SHA-256(h || c)` + */ + sha_mix_in(&h->h, h->act3.ciphertext, sizeof(h->act3.ciphertext)); + + /* BOLT #8: + * + * 3. `se = ECDH(s.priv, re)` + * * where `re` is the ephemeral public key of the responder + */ + if (!secp256k1_ecdh(ln->secp, h->ss.data, &h->re.pubkey, + ln->key.priv.secret.data, NULL, NULL)) + return note_error(&ln->errs, "act3 ecdh handshake failed"); + + /* BOLT #8: + * + * 4. `ck, temp_k3 = HKDF(ck, se)` + * * The final intermediate shared secret is mixed into the running chaining key. + */ + hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss)); + + /* BOLT #8: + * + * 5. `t = encryptWithAD(temp_k3, 0, h, zero)` + * * where `zero` is a zero-length plaintext + * + */ + encrypt_ad(&h->temp_k, 0, &h->h, sizeof(h->h), NULL, 0, + h->act3.tag, sizeof(h->act3.tag)); + + /* BOLT #8: + * + * 8. Send `m = 0 || c || t` over the network buffer. + * + */ + h->act3.v = 0; + + if (write(ln->socket, &h->act3, ACT_THREE_SIZE) != ACT_THREE_SIZE) { + return note_error(&ln->errs, "handshake failed on initial send"); + } + + return note_error(&ln->errs, "handshake success!\n"); +} + +// act2: read the response to the message sent in act1 +static int act_two_initiator(struct lnsocket *ln, struct handshake *h) +{ + /* BOLT #8: + * + * 1. Read _exactly_ 50 bytes from the network buffer. + * + * 2. Parse the read message (`m`) into `v`, `re`, and `c`: + * * where `v` is the _first_ byte of `m`, `re` is the next 33 + * bytes of `m`, and `c` is the last 16 bytes of `m`. + */ + ssize_t size; + + if ((size = read(ln->socket, &h->act2, ACT_TWO_SIZE)) != ACT_TWO_SIZE) { + printf("read %ld bytes, expected %d\n", size, ACT_TWO_SIZE); + return note_error(&ln->errs, "%s", strerror(errno)); + } + + print_act_two(&h->act2); + + /* BOLT #8: + * + * 3. If `v` is an unrecognized handshake version, then the responder + * MUST abort the connection attempt. + */ + if (h->act2.v != 0) + return note_error(&ln->errs, "unrecognized handshake version"); + + /* BOLT #8: + * + * * The raw bytes of the remote party's ephemeral public key + * (`re`) are to be deserialized into a point on the curve using + * affine coordinates as encoded by the key's serialized + * composed format. + */ + if (secp256k1_ec_pubkey_parse(ln->secp, &h->re.pubkey, h->act2.pubkey, + sizeof(h->act2.pubkey)) != 1) { + return note_error(&ln->errs, "failed to parse remote pubkey"); + } + + /* BOLT #8: + * + * 4. `h = SHA-256(h || re.serializeCompressed())` + */ + sha_mix_in_key(ln->secp, &h->h, &h->re); + + /* BOLT #8: + * + * 5. `es = ECDH(s.priv, re)` + */ + if (!secp256k1_ecdh(ln->secp, h->ss.data, &h->re.pubkey, + h->e.priv.secret.data, NULL, NULL)) { + return note_error(&ln->errs, "act2 ecdh failed"); + } + + /* BOLT #8: + * + * 6. `ck, temp_k2 = HKDF(ck, ee)` + * * A new temporary encryption key is generated, which is + * used to generate the authenticating MAC. + */ + hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss)); + + /* BOLT #8: + * + * 7. `p = decryptWithAD(temp_k2, 0, h, c)` + * * If the MAC check in this operation fails, then the initiator + * MUST terminate the connection without any further messages. + */ + if (!decrypt(&h->temp_k, 0, &h->h, sizeof(h->h), + h->act2.tag, sizeof(h->act2.tag), NULL, 0)) { + return note_error(&ln->errs, "handshake decrypt failed"); + } + + /* BOLT #8: + * + * 8. `h = SHA-256(h || c)` + * * The received ciphertext is mixed into the handshake digest. + * This step serves to ensure the payload wasn't modified by a + * MITM. + */ + sha_mix_in(&h->h, h->act2.tag, sizeof(h->act2.tag)); + + return act_three_initiator(ln, h); +} + +// Prepare the very first message and send it the connected node +// Wait for a response in act2 +int act_one_initiator(struct lnsocket *ln, struct handshake *h) +{ + h->e = generate_key(ln->secp); + + /* BOLT #8: + * + * 2. `h = SHA-256(h || e.pub.serializeCompressed())` + * * The newly generated ephemeral key is accumulated into the + * running handshake digest. + */ + sha_mix_in_key(ln->secp, &h->h, &h->e.pub); + + /* BOLT #8: + * + * 3. `es = ECDH(e.priv, rs)` + * * The initiator performs an ECDH between its newly generated ephemeral + * key and the remote node's static public key. + */ + if (!secp256k1_ecdh(ln->secp, h->ss.data, + &h->their_id.pubkey, h->e.priv.secret.data, + NULL, NULL)) { + note_error(&ln->errs, "handshake failed, secp256k1_ecdh error"); + return 0; + } + + /* BOLT #8: + * + * 4. `ck, temp_k1 = HKDF(ck, es)` + * * A new temporary encryption key is generated, which is + * used to generate the authenticating MAC. + */ + hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss)); + + /* BOLT #8: + * 5. `c = encryptWithAD(temp_k1, 0, h, zero)` + * * where `zero` is a zero-length plaintext + */ + encrypt_ad(&h->temp_k, 0, &h->h, sizeof(h->h), NULL, 0, + h->act1.tag, sizeof(h->act1.tag)); + + /* BOLT #8: + * 6. `h = SHA-256(h || c)` + * * Finally, the generated ciphertext is accumulated into the + * authenticating handshake digest. + */ + sha_mix_in(&h->h, h->act1.tag, sizeof(h->act1.tag)); + + /* BOLT #8: + * + * 7. Send `m = 0 || e.pub.serializeCompressed() || c` to the responder over the network buffer. + */ + h->act1.v = 0; + size_t len = sizeof(h->act1.pubkey); + secp256k1_ec_pubkey_serialize(ln->secp, h->act1.pubkey, &len, + &h->e.pub.pubkey, + SECP256K1_EC_COMPRESSED); + + check_act_one(&h->act1); + + if (write(ln->socket, &h->act1, ACT_ONE_SIZE) != ACT_ONE_SIZE) { + note_error(&ln->errs, "handshake failed on initial send"); + return 0; + } + + return act_two_initiator(ln, h); +} diff --git a/handshake.h b/handshake.h @@ -22,9 +22,11 @@ THE SOFTWARE. #ifndef LNLINK_HANDSHAKE_H #define LNLINK_HANDSHAKE_H -#include "types.h" +#include "typedefs.h" #include "sha256.h" +#include <netdb.h> + #include <sodium/crypto_aead_chacha20poly1305.h> #include <secp256k1_extrakeys.h> @@ -138,8 +140,6 @@ struct handshake { /* Where is connection from/to */ struct addrinfo addr; - /* Who we are */ - struct keypair my_id; /* Who they are: set already if we're initiator. */ struct pubkey their_id; @@ -157,4 +157,12 @@ struct handshake { */ }; +void new_handshake(secp256k1_context *secp, struct handshake *handshake, + const struct pubkey *responder_id); + +struct lnsocket; + +int act_one_initiator(struct lnsocket *ln, struct handshake *h); +struct keypair generate_key(secp256k1_context *ctx); + #endif /* LNLINK_HANDSHAKE_H */ diff --git a/lnsocket.c b/lnsocket.c @@ -15,49 +15,43 @@ #include <sodium/crypto_aead_chacha20poly1305.h> #include <sodium/randombytes.h> -#include "sha256.h" -#include "compiler.h" -#include "hkdf.h" #include "handshake.h" -#include "endian.h" +#include "error.h" +#include "compiler.h" +#include "lnsocket_internal.h" #define array_len(x) (sizeof(x)/sizeof(x[0])) -struct lnsocket { - const char *errors[8]; - int socket; - int num_errors; - secp256k1_context *secp; -}; - +int push_error(struct lnsocket *lnsocket, const char *err); -static bool char_to_hex(unsigned char *val, char c) +static int char_to_hex(unsigned char *val, char c) { if (c >= '0' && c <= '9') { *val = c - '0'; - return true; + return 1; } if (c >= 'a' && c <= 'f') { *val = c - 'a' + 10; - return true; + return 1; } if (c >= 'A' && c <= 'F') { *val = c - 'A' + 10; - return true; + return 1; } - return false; + return 0; } -static bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize) + +static int hex_decode(const char *str, size_t slen, void *buf, size_t bufsize) { unsigned char v1, v2; unsigned char *p = buf; while (slen > 1) { if (!char_to_hex(&v1, str[0]) || !char_to_hex(&v2, str[1])) - return false; + return 0; if (!bufsize) - return false; + return 0; *(p++) = (v1 << 4) | v2; str += 2; slen -= 2; @@ -66,12 +60,13 @@ static bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize) return slen == 0 && bufsize == 0; } -int parse_node_id(const char *str, struct node_id *dest) + +static int parse_node_id(const char *str, struct node_id *dest) { return hex_decode(str, strlen(str), dest->k, sizeof(*dest)); } -int pubkey_from_node_id(secp256k1_context *secp, struct pubkey *key, +static int pubkey_from_node_id(secp256k1_context *secp, struct pubkey *key, const struct node_id *id) { return secp256k1_ec_pubkey_parse(secp, &key->pubkey, @@ -79,513 +74,119 @@ int pubkey_from_node_id(secp256k1_context *secp, struct pubkey *key, sizeof(id->k)); } - -static struct keypair generate_key(secp256k1_context *ctx) -{ - struct keypair k; - - do { - randombytes_buf(k.priv.secret.data, sizeof(k.priv.secret.data)); - } while (!secp256k1_ec_pubkey_create(ctx, &k.pub.pubkey, - k.priv.secret.data)); - return k; -} - - -static int push_error(struct lnsocket *lnsocket, const char *err) +struct lnsocket *lnsocket_create_with(int memory) { - if (lnsocket->num_errors >= array_len(lnsocket->errors)) { - // TODO: push out old errors instead - return 0; - } + struct cursor mem; + void *arena = malloc(memory); - lnsocket->errors[lnsocket->num_errors++] = err; + if (!arena) + return NULL; - return 0; -} + make_cursor(arena, arena + memory, &mem); + struct lnsocket *lnsocket = cursor_alloc(&mem, sizeof(*lnsocket)); -static void lnsocket_init(struct lnsocket *lnsocket) -{ - memset(lnsocket, 0, sizeof(*lnsocket)); + if (!lnsocket) + return NULL; lnsocket->secp = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); -} - -/* h = SHA-256(h || data) */ -static void sha_mix_in(struct sha256 *h, const void *data, size_t len) -{ - struct sha256_ctx shactx; - - sha256_init(&shactx); - sha256_update(&shactx, h->u.u8, sizeof(*h)); - sha256_update(&shactx, data, len); - sha256_done(&shactx, h); -} - -/* h = SHA-256(h || pub.serializeCompressed()) */ -static void sha_mix_in_key(secp256k1_context *ctx, struct sha256 *h, - const struct pubkey *key) -{ - u8 der[PUBKEY_CMPR_LEN]; - size_t len = sizeof(der); - - secp256k1_ec_pubkey_serialize(ctx, der, &len, &key->pubkey, - SECP256K1_EC_COMPRESSED); - assert(len == sizeof(der)); - sha_mix_in(h, der, sizeof(der)); -} - -/* out1, out2 = HKDF(in1, in2)` */ -static void hkdf_two_keys(struct secret *out1, struct secret *out2, - const struct secret *in1, - const void *in2, size_t in2_size) -{ - /* BOLT #8: - * - * * `HKDF(salt,ikm)`: a function defined in `RFC 5869`<sup>[3](#reference-3)</sup>, - * evaluated with a zero-length `info` field - * * All invocations of `HKDF` implicitly return 64 bytes - * of cryptographic randomness using the extract-and-expand - * component of the `HKDF`. - */ - struct secret okm[2]; - - hkdf_sha256(okm, sizeof(okm), in1, sizeof(*in1), in2, in2_size, - NULL, 0); - *out1 = okm[0]; - *out2 = okm[1]; -} - - -static void le64_nonce(unsigned char *npub, u64 nonce) -{ - /* BOLT #8: - * - * ...with nonce `n` encoded as 32 zero bits, followed by a - * *little-endian* 64-bit value. Note: this follows the Noise Protocol - * convention, rather than our normal endian - */ - le64 le_nonce = cpu_to_le64(nonce); - const size_t zerolen = crypto_aead_chacha20poly1305_ietf_NPUBBYTES - sizeof(le_nonce); - - BUILD_ASSERT(crypto_aead_chacha20poly1305_ietf_NPUBBYTES >= sizeof(le_nonce)); - /* First part is 0, followed by nonce. */ - memset(npub, 0, zerolen); - memcpy(npub + zerolen, &le_nonce, sizeof(le_nonce)); -} - -/* BOLT #8: - * * `encryptWithAD(k, n, ad, plaintext)`: outputs `encrypt(k, n, ad, - * plaintext)` - * * Where `encrypt` is an evaluation of `ChaCha20-Poly1305` (IETF - * variant) with the passed arguments, with nonce `n` - */ -static void encrypt_ad(const struct secret *k, u64 nonce, - const void *additional_data, size_t additional_data_len, - const void *plaintext, size_t plaintext_len, - void *output, size_t outputlen) -{ - unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; - unsigned long long clen; - int ret; - - assert(outputlen == plaintext_len + crypto_aead_chacha20poly1305_ietf_ABYTES); - le64_nonce(npub, nonce); - BUILD_ASSERT(sizeof(*k) == crypto_aead_chacha20poly1305_ietf_KEYBYTES); - - ret = crypto_aead_chacha20poly1305_ietf_encrypt( - output, &clen, memcheck(plaintext, plaintext_len), - plaintext_len, additional_data, additional_data_len, - NULL, npub, k->data); - - assert(ret == 0); - assert(clen == plaintext_len + crypto_aead_chacha20poly1305_ietf_ABYTES); -} -static inline void check_act_one(const struct act_one *act1) -{ - /* BOLT #8: - * - * : 1 byte for the handshake version, 33 bytes for the compressed - * ephemeral public key of the initiator, and 16 bytes for the - * `poly1305` tag. - */ - BUILD_ASSERT(sizeof(act1->v) == 1); - BUILD_ASSERT(sizeof(act1->pubkey) == 33); - BUILD_ASSERT(sizeof(act1->tag) == 16); -} - -static void print_hex(u8 *bytes, int len) { - int i; - for (i = 0; i < len; ++i) { - printf("%02x", bytes[i]); + if (!cursor_slice(&mem, &lnsocket->errs.cur, memory / 2)) { + return NULL; } -} + lnsocket->errs.enabled = 1; -static void new_handshake(secp256k1_context *secp, struct handshake *handshake, - const struct pubkey *responder_id) -{ - /* BOLT #8: - * - * Before the start of Act One, both sides initialize their - * per-sessions state as follows: - * - * 1. `h = SHA-256(protocolName)` - * * where `protocolName = "Noise_XK_secp256k1_ChaChaPoly_SHA256"` - * encoded as an ASCII string - */ - sha256(&handshake->h, "Noise_XK_secp256k1_ChaChaPoly_SHA256", - strlen("Noise_XK_secp256k1_ChaChaPoly_SHA256")); - - /* BOLT #8: - * - * 2. `ck = h` - */ - BUILD_ASSERT(sizeof(handshake->h) == sizeof(handshake->ck)); - memcpy(&handshake->ck, &handshake->h, sizeof(handshake->ck)); - - /* BOLT #8: - * - * 3. `h = SHA-256(h || prologue)` - * * where `prologue` is the ASCII string: `lightning` - */ - sha_mix_in(&handshake->h, "lightning", strlen("lightning")); - - /* BOLT #8: - * - * As a concluding step, both sides mix the responder's public key - * into the handshake digest: - * - * * The initiating node mixes in the responding node's static public - * key serialized in Bitcoin's compressed format: - * * `h = SHA-256(h || rs.pub.serializeCompressed())` - * - * * The responding node mixes in their local static public key - * serialized in Bitcoin's compressed format: - * * `h = SHA-256(h || ls.pub.serializeCompressed())` - */ - sha_mix_in_key(secp, &handshake->h, responder_id); + lnsocket->mem = mem; + return lnsocket; } -static void print_act_two(struct act_two *two) +struct lnsocket *lnsocket_create() { - printf("ACT2 v %d pubkey ", two->v); - print_hex(two->pubkey, sizeof(two->pubkey)); - printf(" tag "); - print_hex(two->tag, sizeof(two->tag)); - printf("\n"); + return lnsocket_create_with(16384); } -/* BOLT #8: - * * `decryptWithAD(k, n, ad, ciphertext)`: outputs `decrypt(k, n, ad, - * ciphertext)` - * * Where `decrypt` is an evaluation of `ChaCha20-Poly1305` (IETF - * variant) with the passed arguments, with nonce `n` - */ -static int decrypt(const struct secret *k, u64 nonce, - const void *additional_data, size_t additional_data_len, - const void *ciphertext, size_t ciphertext_len, - void *output, size_t outputlen) +void lnsocket_destroy(struct lnsocket *lnsocket) { - unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; - unsigned long long mlen; - - assert(outputlen == ciphertext_len - crypto_aead_chacha20poly1305_ietf_ABYTES); - - le64_nonce(npub, nonce); - BUILD_ASSERT(sizeof(*k) == crypto_aead_chacha20poly1305_ietf_KEYBYTES); - if (crypto_aead_chacha20poly1305_ietf_decrypt(output, &mlen, NULL, - memcheck(ciphertext, ciphertext_len), - ciphertext_len, - additional_data, additional_data_len, - npub, k->data) != 0) { - return 0; - } - - assert(mlen == ciphertext_len - crypto_aead_chacha20poly1305_ietf_ABYTES); - return 1; -} - -static int act_three_initiator(struct lnsocket *ln, struct handshake *h) -{ - u8 spub[PUBKEY_CMPR_LEN]; - size_t len = sizeof(spub); - - /* BOLT #8: - * 1. `c = encryptWithAD(temp_k2, 1, h, s.pub.serializeCompressed())` - * * where `s` is the static public key of the initiator - */ - secp256k1_ec_pubkey_serialize(ln->secp, spub, &len, - &h->my_id.pub.pubkey, - SECP256K1_EC_COMPRESSED); - encrypt_ad(&h->temp_k, 1, &h->h, sizeof(h->h), spub, sizeof(spub), - h->act3.ciphertext, sizeof(h->act3.ciphertext)); - - /* BOLT #8: - * 2. `h = SHA-256(h || c)` - */ - sha_mix_in(&h->h, h->act3.ciphertext, sizeof(h->act3.ciphertext)); - - /* BOLT #8: - * - * 3. `se = ECDH(s.priv, re)` - * * where `re` is the ephemeral public key of the responder - */ - if (!secp256k1_ecdh(ln->secp, h->ss.data, &h->re.pubkey, - h->my_id.priv.secret.data, NULL, NULL)) - return push_error(ln, "act3 ecdh handshake failed"); - - /* BOLT #8: - * - * 4. `ck, temp_k3 = HKDF(ck, se)` - * * The final intermediate shared secret is mixed into the running chaining key. - */ - hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss)); - - /* BOLT #8: - * - * 5. `t = encryptWithAD(temp_k3, 0, h, zero)` - * * where `zero` is a zero-length plaintext - * - */ - encrypt_ad(&h->temp_k, 0, &h->h, sizeof(h->h), NULL, 0, - h->act3.tag, sizeof(h->act3.tag)); - - /* BOLT #8: - * - * 8. Send `m = 0 || c || t` over the network buffer. - * - */ - h->act3.v = 0; - - if (write(ln->socket, &h->act3, ACT_THREE_SIZE) != ACT_THREE_SIZE) { - return push_error(ln, "handshake failed on initial send"); - } - - printf("handshake success!\n"); - return 0; -} - -// act2: read the response to the message sent in act1 -static int act_two_initiator(struct lnsocket *ln, struct handshake *h) -{ - /* BOLT #8: - * - * 1. Read _exactly_ 50 bytes from the network buffer. - * - * 2. Parse the read message (`m`) into `v`, `re`, and `c`: - * * where `v` is the _first_ byte of `m`, `re` is the next 33 - * bytes of `m`, and `c` is the last 16 bytes of `m`. - */ - ssize_t size; - - if ((size = read(ln->socket, &h->act2, ACT_TWO_SIZE)) != ACT_TWO_SIZE) { - printf("read %ld bytes, expected %d\n", size, ACT_TWO_SIZE); - return push_error(ln, strerror(errno)); - } - - print_act_two(&h->act2); - - /* BOLT #8: - * - * 3. If `v` is an unrecognized handshake version, then the responder - * MUST abort the connection attempt. - */ - if (h->act2.v != 0) - return push_error(ln, "unrecognized handshake version"); - - /* BOLT #8: - * - * * The raw bytes of the remote party's ephemeral public key - * (`re`) are to be deserialized into a point on the curve using - * affine coordinates as encoded by the key's serialized - * composed format. - */ - if (secp256k1_ec_pubkey_parse(ln->secp, &h->re.pubkey, h->act2.pubkey, - sizeof(h->act2.pubkey)) != 1) { - return push_error(ln, "failed to parse remote pubkey"); - } + if (!lnsocket) + return; - /* BOLT #8: - * - * 4. `h = SHA-256(h || re.serializeCompressed())` - */ - sha_mix_in_key(ln->secp, &h->h, &h->re); - - /* BOLT #8: - * - * 5. `es = ECDH(s.priv, re)` - */ - if (!secp256k1_ecdh(ln->secp, h->ss.data, &h->re.pubkey, - h->e.priv.secret.data, NULL, NULL)) { - return push_error(ln, "act2 ecdh failed"); - } - - /* BOLT #8: - * - * 6. `ck, temp_k2 = HKDF(ck, ee)` - * * A new temporary encryption key is generated, which is - * used to generate the authenticating MAC. - */ - hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss)); - - /* BOLT #8: - * - * 7. `p = decryptWithAD(temp_k2, 0, h, c)` - * * If the MAC check in this operation fails, then the initiator - * MUST terminate the connection without any further messages. - */ - if (!decrypt(&h->temp_k, 0, &h->h, sizeof(h->h), - h->act2.tag, sizeof(h->act2.tag), NULL, 0)) { - return push_error(ln, "handshake decrypt failed"); - } - - /* BOLT #8: - * - * 8. `h = SHA-256(h || c)` - * * The received ciphertext is mixed into the handshake digest. - * This step serves to ensure the payload wasn't modified by a - * MITM. - */ - sha_mix_in(&h->h, h->act2.tag, sizeof(h->act2.tag)); - - return act_three_initiator(ln, h); + secp256k1_context_destroy(lnsocket->secp); + free(lnsocket->mem.start); } -// Prepare the very first message and send it the connected node -// Wait for a response in act2 -int act_one_initiator(struct lnsocket *ln, struct handshake *h) +static int is_zero(void *vp, int size) { - h->e = generate_key(ln->secp); - - /* BOLT #8: - * - * 2. `h = SHA-256(h || e.pub.serializeCompressed())` - * * The newly generated ephemeral key is accumulated into the - * running handshake digest. - */ - sha_mix_in_key(ln->secp, &h->h, &h->e.pub); - - /* BOLT #8: - * - * 3. `es = ECDH(e.priv, rs)` - * * The initiator performs an ECDH between its newly generated ephemeral - * key and the remote node's static public key. - */ - if (!secp256k1_ecdh(ln->secp, h->ss.data, - &h->their_id.pubkey, h->e.priv.secret.data, - NULL, NULL)) { - push_error(ln, "handshake failed, secp256k1_ecdh error"); - return 0; - } + u8 *p = (u8*)vp; + const u8 *start = p; - /* BOLT #8: - * - * 4. `ck, temp_k1 = HKDF(ck, es)` - * * A new temporary encryption key is generated, which is - * used to generate the authenticating MAC. - */ - hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss)); - - /* BOLT #8: - * 5. `c = encryptWithAD(temp_k1, 0, h, zero)` - * * where `zero` is a zero-length plaintext - */ - encrypt_ad(&h->temp_k, 0, &h->h, sizeof(h->h), NULL, 0, - h->act1.tag, sizeof(h->act1.tag)); - - /* BOLT #8: - * 6. `h = SHA-256(h || c)` - * * Finally, the generated ciphertext is accumulated into the - * authenticating handshake digest. - */ - sha_mix_in(&h->h, h->act1.tag, sizeof(h->act1.tag)); - - /* BOLT #8: - * - * 7. Send `m = 0 || e.pub.serializeCompressed() || c` to the responder over the network buffer. - */ - h->act1.v = 0; - size_t len = sizeof(h->act1.pubkey); - secp256k1_ec_pubkey_serialize(ln->secp, h->act1.pubkey, &len, - &h->e.pub.pubkey, - SECP256K1_EC_COMPRESSED); - - check_act_one(&h->act1); - - if (write(ln->socket, &h->act1, ACT_ONE_SIZE) != ACT_ONE_SIZE) { - push_error(ln, "handshake failed on initial send"); - return 0; + for (; p < start+size; p++) { + if (*p != 0) + return 0; } - - return act_two_initiator(ln, h); + return 1; } -int connect_node(struct lnsocket *ln, const char *node_id, const char *host) +int lnsocket_connect(struct lnsocket *ln, const char *node_id, const char *host) { int ret; struct addrinfo *addrs = NULL; struct handshake h; - struct keypair my_id; struct pubkey their_id; struct node_id their_node_id; + if (is_zero(&ln->key, sizeof(ln->key))) { + note_error(&ln->errs, "key not initialized, use lnsocket_set_key() or lnsocket_genkey()"); + return 0; + } + // convert node_id string to bytes if (!parse_node_id(node_id, &their_node_id)) { - push_error(ln, "failed to parse node id"); + note_error(&ln->errs, "failed to parse node id"); return 0; } // encode node_id bytes to secp pubkey if (!pubkey_from_node_id(ln->secp, &their_id, &their_node_id)) { - push_error(ln, "failed to convert node_id to pubkey"); + note_error(&ln->errs, "failed to convert node_id to pubkey"); return 0; } // parse ip into addrinfo if ((ret = getaddrinfo(host, "9735", NULL, &addrs)) || !addrs) { - push_error(ln, gai_strerror(ret)); + note_error(&ln->errs, "%s", gai_strerror(ret)); return 0; } // create our network socket for comms if (!(ln->socket = socket(AF_INET, SOCK_STREAM, 0))) { - push_error(ln, "creating socket failed"); + note_error(&ln->errs, "creating socket failed"); return 0; } // connect to the node! if (connect(ln->socket, addrs->ai_addr, addrs->ai_addrlen) == -1) { - push_error(ln, strerror(errno)); + note_error(&ln->errs, "%s", strerror(errno)); return 0; } // prepare some data for ACT1 - my_id = generate_key(ln->secp); new_handshake(ln->secp, &h, &their_id); h.side = INITIATOR; - h.my_id = my_id; h.their_id = their_id; // let's do this! return act_one_initiator(ln, &h); } -int main(int argc, const char *argv[]) +void lnsocket_genkey(struct lnsocket *ln) { - struct lnsocket ln; - lnsocket_init(&ln); - - const char *nodeid = "03f3c108ccd536b8526841f0a5c58212bb9e6584a1eb493080e7c1cc34f82dad71"; + ln->key = generate_key(ln->secp); +} - if (!connect_node(&ln, nodeid, "24.84.152.187")) { - printf("connection failed: %s\n", ln.errors[0]); - return 1; - } +void lnsocket_print_errors(struct lnsocket *ln) +{ + print_error_backtrace(&ln->errs); +} - printf("connected!\n"); - return 0; -} diff --git a/lnsocket.h b/lnsocket.h @@ -0,0 +1,12 @@ +#ifndef LNSOCKET_H +#define LNSOCKET_H + +struct lnsocket; + +int lnsocket_connect(struct lnsocket *, const char *node_id, const char *host); +void lnsocket_genkey(struct lnsocket *); +struct lnsocket *lnsocket_create(); +void lnsocket_destroy(struct lnsocket *); +void lnsocket_print_errors(struct lnsocket *); + +#endif /* LNSOCKET_H */ diff --git a/lnsocket_internal.h b/lnsocket_internal.h @@ -0,0 +1,11 @@ + +struct lnsocket { + const char *errors[8]; + struct cursor mem; + struct errors errs; + int socket; + int num_errors; + struct keypair key; + secp256k1_context *secp; +}; + diff --git a/test.c b/test.c @@ -0,0 +1,22 @@ + +#include "lnsocket.h" +#include <stdio.h> + +int main(int argc, const char *argv[]) +{ + struct lnsocket *ln = lnsocket_create(); + int ok = 1; + + const char *nodeid = "03f3c108ccd536b8526841f0a5c58212bb9e6584a1eb493080e7c1cc34f82dad71"; + + lnsocket_genkey(ln); + if (!(ok = lnsocket_connect(ln, nodeid, "24.84.152.187"))) { + lnsocket_print_errors(ln); + goto done; + } + + printf("connected!\n"); +done: + lnsocket_destroy(ln); + return !ok; +} diff --git a/typedefs.h b/typedefs.h @@ -0,0 +1,13 @@ + +#ifndef LNSOCKET_TYPES_H +#define LNSOCKET_TYPES_H +#include <stdint.h> + +typedef unsigned char u8; +typedef unsigned int u32; +typedef unsigned short u16; +typedef uint64_t u64; +typedef int64_t s64; + + +#endif /* LNSOCKET_TYPES_H */ diff --git a/types.h b/types.h @@ -1,8 +0,0 @@ - -#ifndef LNSOCKET_TYPES_H -#define LNSOCKET_TYPES_H - -typedef unsigned char u8; -typedef uint64_t u64; - -#endif /* LNSOCKET_TYPES_H */ diff --git a/varint.c b/varint.c @@ -0,0 +1,75 @@ + +#include "varint.h" + +size_t varint_size(uint64_t v) +{ + if (v < 0xfd) + return 1; + if (v <= 0xffff) + return 3; + if (v <= 0xffffffff) + return 5; + return 9; +} + +size_t varint_put(unsigned char buf[VARINT_MAX_LEN], uint64_t v) +{ + unsigned char *p = buf; + + if (v < 0xfd) { + *(p++) = v; + } else if (v <= 0xffff) { + (*p++) = 0xfd; + (*p++) = v; + (*p++) = v >> 8; + } else if (v <= 0xffffffff) { + (*p++) = 0xfe; + (*p++) = v; + (*p++) = v >> 8; + (*p++) = v >> 16; + (*p++) = v >> 24; + } else { + (*p++) = 0xff; + (*p++) = v; + (*p++) = v >> 8; + (*p++) = v >> 16; + (*p++) = v >> 24; + (*p++) = v >> 32; + (*p++) = v >> 40; + (*p++) = v >> 48; + (*p++) = v >> 56; + } + return p - buf; +} + +size_t varint_get(const unsigned char *p, size_t max, int64_t *val) +{ + if (max < 1) + return 0; + + switch (*p) { + case 0xfd: + if (max < 3) + return 0; + *val = ((uint64_t)p[2] << 8) + p[1]; + return 3; + case 0xfe: + if (max < 5) + return 0; + *val = ((uint64_t)p[4] << 24) + ((uint64_t)p[3] << 16) + + ((uint64_t)p[2] << 8) + p[1]; + return 5; + case 0xff: + if (max < 9) + return 0; + *val = ((uint64_t)p[8] << 56) + ((uint64_t)p[7] << 48) + + ((uint64_t)p[6] << 40) + ((uint64_t)p[5] << 32) + + ((uint64_t)p[4] << 24) + ((uint64_t)p[3] << 16) + + ((uint64_t)p[2] << 8) + p[1]; + return 9; + default: + *val = *p; + return 1; + } +} + diff --git a/varint.h b/varint.h @@ -0,0 +1,14 @@ + +#ifndef LNSOCKET_VARINT_H +#define LNSOCKET_VARINT_H + +#define VARINT_MAX_LEN 9 + +#include <stddef.h> +#include <stdint.h> + +size_t varint_put(unsigned char buf[VARINT_MAX_LEN], uint64_t v); +size_t varint_size(uint64_t v); +size_t varint_get(const unsigned char *p, size_t max, int64_t *val); + +#endif /* PROTOVERSE_VARINT_H */