lnsocket

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

commit 3edf959aa0976e7dd209d71e9895fd524023f7f6
parent 9d1251e9dec600e08da7dbe7484aeb82967d3ef2
Author: William Casarin <jb55@jb55.com>
Date:   Fri, 14 Jan 2022 16:48:50 -0800

another initiator step

Diffstat:
MMakefile | 14++++++++++----
Mcompiler.h | 28++++++++++++++++++++++++++++
Mconfig.h | 5+++++
Mconfigurator.c | 41++++++++++++++++++++---------------------
Mendian.h | 10+++++-----
Mlnsocket.c | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtypes.h | 1+
7 files changed, 125 insertions(+), 30 deletions(-)

diff --git a/Makefile b/Makefile @@ -4,7 +4,7 @@ LDFLAGS= ARS=deps/secp256k1/.libs/libsecp256k1.a deps/libsodium/src/libsodium/.libs/libsodium.a OBJS=sha256.o hkdf.o hmac.o sha512.o -DEPS=$(ARS) $(OBJS) config.h +DEPS=$(OBJS) config.h all: lnsocket @@ -14,6 +14,10 @@ config.h: configurator configurator: configurator.c $(CC) $< -o $@ +%.o: %.c config.h + @echo "cc $<" + @$(CC) $(CFLAGS) -c $< -o $@ + deps/secp256k1/src/libsecp256k1-config.h: deps/secp256k1/configure ./configure --enable-module-ecdh @@ -29,19 +33,21 @@ deps/secp256k1/.libs/libsecp256k1.a: deps/secp256k1/configure cd deps/secp256k1; \ make -j2 libsecp256k1.la - deps/libsodium/src/libsodium/.libs/libsodium.a: deps/libsodium/configure cd deps/libsodium/src/libsodium; \ make -j2 libsodium.la -lnsocket: lnsocket.c $(DEPS) +lnsocket: lnsocket.c $(DEPS) $(ARS) $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ tags: fake find . -name '*.c' -or -name '*.h' | xargs ctags clean: fake - rm -f lnsocket $(DEPS) deps/secp256k1/src/libsecp256k1-config.h + rm -f lnsocket $(DEPS) + +deepclean: clean + rm -f $(ARS) deps/secp256k1/src/libsecp256k1-config.h cd deps/secp256k1; \ make clean cd deps/libsodium; \ diff --git a/compiler.h b/compiler.h @@ -54,4 +54,32 @@ #define memclear_2(m1, s1, m2, s2) { memclear(m1, s1); memclear(m2, s2); } #define memclear_3(m1, s1, m2, s2, m3, s3) { memclear(m1, s1); memclear(m2, s2); memclear(m3, s3); } +static inline void *memcheck_(const void *data, size_t len) +{ + (void)len; + return (void *)data; +} + +#if HAVE_TYPEOF +/** + * memcheck - check that a memory region is initialized + * @data: start of region + * @len: length in bytes + * + * When running under valgrind, this causes an error to be printed + * if the entire region is not defined. Otherwise valgrind only + * reports an error when an undefined value is used for a branch, or + * written out. + * + * Example: + * // Search for space, but make sure it's all initialized. + * if (memchr(memcheck(somebytes, bytes_len), ' ', bytes_len)) { + * printf("space was found!\n"); + * } + */ +#define memcheck(data, len) ((__typeof__((data)+0))memcheck_((data), (len))) +#else +#define memcheck(data, len) memcheck_((data), (len)) +#endif + #endif /* COMPILER_H */ diff --git a/config.h b/config.h @@ -10,4 +10,9 @@ #define HAVE_CCAN 1 #define HAVE_UNALIGNED_ACCESS 1 +#define HAVE_TYPEOF 1 +#define HAVE_BIG_ENDIAN 0 +#define HAVE_BYTESWAP_H 1 +#define HAVE_BSWAP_64 1 +#define HAVE_LITTLE_ENDIAN 1 #endif /* CCAN_CONFIG_H */ diff --git a/configurator.c b/configurator.c @@ -119,7 +119,26 @@ static const struct test base_tests[] = { " int *x = (int *)pad, *y = (int *)(pad + 1);\n" " return *x == *y;\n" "}\n" }, - + { "HAVE_TYPEOF", "__typeof__ support", + "INSIDE_MAIN", NULL, NULL, + "__typeof__(argc) i; i = argc; return i == argc ? 0 : 1;" }, + { "HAVE_BIG_ENDIAN", "big endian", + "INSIDE_MAIN|EXECUTE", NULL, NULL, + "union { int i; char c[sizeof(int)]; } u;\n" + "u.i = 0x01020304;\n" + "return u.c[0] == 0x01 && u.c[1] == 0x02 && u.c[2] == 0x03 && u.c[3] == 0x04 ? 0 : 1;" }, + { "HAVE_BYTESWAP_H", "<byteswap.h>", + "OUTSIDE_MAIN", NULL, NULL, + "#include <byteswap.h>\n" }, + { "HAVE_BSWAP_64", "bswap64 in byteswap.h", + "DEFINES_FUNC", "HAVE_BYTESWAP_H", NULL, + "#include <byteswap.h>\n" + "static int func(int x) { return bswap_64(x); }" }, + { "HAVE_LITTLE_ENDIAN", "little endian", + "INSIDE_MAIN|EXECUTE", NULL, NULL, + "union { int i; char c[sizeof(int)]; } u;\n" + "u.i = 0x01020304;\n" + "return u.c[0] == 0x04 && u.c[1] == 0x03 && u.c[2] == 0x02 && u.c[3] == 0x01 ? 0 : 1;" }, /* { "HAVE_32BIT_OFF_T", "off_t is 32 bits", "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE", NULL, NULL, @@ -186,15 +205,6 @@ static const struct test base_tests[] = { " void *bt[10];\n" " return backtrace(bt, 10) < x;\n" "}" }, - { "HAVE_BIG_ENDIAN", "big endian", - "INSIDE_MAIN|EXECUTE", NULL, NULL, - "union { int i; char c[sizeof(int)]; } u;\n" - "u.i = 0x01020304;\n" - "return u.c[0] == 0x01 && u.c[1] == 0x02 && u.c[2] == 0x03 && u.c[3] == 0x04 ? 0 : 1;" }, - { "HAVE_BSWAP_64", "bswap64 in byteswap.h", - "DEFINES_FUNC", "HAVE_BYTESWAP_H", NULL, - "#include <byteswap.h>\n" - "static int func(int x) { return bswap_64(x); }" }, { "HAVE_BUILTIN_CHOOSE_EXPR", "__builtin_choose_expr support", "INSIDE_MAIN", NULL, NULL, "return __builtin_choose_expr(1, 0, \"garbage\");" }, @@ -249,9 +259,6 @@ static const struct test base_tests[] = { "int func(int v) {\n" " return __CLZ(__RBIT(v));\n" "}" }, - { "HAVE_BYTESWAP_H", "<byteswap.h>", - "OUTSIDE_MAIN", NULL, NULL, - "#include <byteswap.h>\n" }, { "HAVE_CLOCK_GETTIME", "clock_gettime() declaration", "DEFINES_FUNC", "HAVE_STRUCT_TIMESPEC", NULL, "#include <time.h>\n" @@ -325,11 +332,6 @@ static const struct test base_tests[] = { "#endif\n" "#include <ctype.h>\n" "static int func(void) { return isblank(' '); }" }, - { "HAVE_LITTLE_ENDIAN", "little endian", - "INSIDE_MAIN|EXECUTE", NULL, NULL, - "union { int i; char c[sizeof(int)]; } u;\n" - "u.i = 0x01020304;\n" - "return u.c[0] == 0x04 && u.c[1] == 0x03 && u.c[2] == 0x02 && u.c[3] == 0x01 ? 0 : 1;" }, { "HAVE_MEMMEM", "memmem in <string.h>", "DEFINES_FUNC", NULL, NULL, "#ifndef _GNU_SOURCE\n" @@ -416,9 +418,6 @@ static const struct test base_tests[] = { { "HAVE_SYS_UNISTD_H", "<sys/unistd.h>", "OUTSIDE_MAIN", NULL, NULL, "#include <sys/unistd.h>\n" }, - { "HAVE_TYPEOF", "__typeof__ support", - "INSIDE_MAIN", NULL, NULL, - "__typeof__(argc) i; i = argc; return i == argc ? 0 : 1;" }, { "HAVE_UTIME", "utime() declaration", "DEFINES_FUNC", NULL, NULL, "#include <sys/types.h>\n" diff --git a/endian.h b/endian.h @@ -1,7 +1,8 @@ -/* CC0 (Public domain) */ +/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_ENDIAN_H #define CCAN_ENDIAN_H #include <stdint.h> +#include "config.h" /** * BSWAP_16 - reverse bytes in a constant uint16_t value. @@ -107,7 +108,9 @@ static inline uint64_t bswap_64(uint64_t val) #define __BIG_ENDIAN 4321 /* Sanity check the defines. We don't handle weird endianness. */ -#if HAVE_LITTLE_ENDIAN && HAVE_BIG_ENDIAN +#if !HAVE_LITTLE_ENDIAN && !HAVE_BIG_ENDIAN +#error "Unknown endian" +#elif HAVE_LITTLE_ENDIAN && HAVE_BIG_ENDIAN #error "Can't compile for both big and little endian." #elif HAVE_LITTLE_ENDIAN #ifndef __BYTE_ORDER @@ -341,8 +344,6 @@ static inline uint16_t be16_to_cpu(beint16_t be_val) return BE16_TO_CPU(be_val); } -/* Whichever they include first, they get these definitions. */ -#ifdef CCAN_SHORT_TYPES_H /** * be64/be32/be16 - 64/32/16 bit big-endian representation. */ @@ -356,5 +357,4 @@ typedef beint16_t be16; typedef leint64_t le64; typedef leint32_t le32; typedef leint16_t le16; -#endif #endif /* CCAN_ENDIAN_H */ diff --git a/lnsocket.c b/lnsocket.c @@ -15,8 +15,10 @@ #include <sodium/randombytes.h> #include "sha256.h" +#include "compiler.h" #include "hkdf.h" #include "handshake.h" +#include "endian.h" #define array_len(x) (sizeof(x)/sizeof(x[0])) @@ -109,6 +111,53 @@ static void hkdf_two_keys(struct secret *out1, struct secret *out2, *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); +} + + int act_one_initiator(struct lnsocket *ln, struct handshake *h) { h->e = generate_key(ln->secp_ctx); @@ -140,6 +189,13 @@ int act_one_initiator(struct lnsocket *ln, struct handshake *h) */ 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)); + return 1; } diff --git a/types.h b/types.h @@ -3,5 +3,6 @@ #define LNSOCKET_TYPES_H typedef unsigned char u8; +typedef uint64_t u64; #endif /* LNSOCKET_TYPES_H */