diff --git a/lightningd/Makefile b/lightningd/Makefile index e34c7c5a4..334dca540 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -8,7 +8,7 @@ lightningd-all: lightningd/lightningd default: lightningd-all -LIGHTNINGD_LIB_SRC := \ +LIGHTNINGD_OLD_SRC := \ daemon/configdir.c \ daemon/json.c \ daemon/log.c \ @@ -17,8 +17,14 @@ LIGHTNINGD_LIB_SRC := \ daemon/pseudorand.c \ daemon/routing.c \ daemon/watch.c +LIGHTNINGD_OLD_OBJS := $(LIGHTNINGD_OLD_SRC:.c=.o) +LIGHTNINGD_OLD_HEADERS := $(LIGHTNINGD_OLD_SRC:.c=.h) + +LIGHTNINGD_LIB_SRC := \ + lightningd/cryptomsg.c LIGHTNINGD_LIB_OBJS := $(LIGHTNINGD_LIB_SRC:.c=.o) +LIGHTNINGD_LIB_HEADERS := $(LIGHTNINGD_LIB_SRC:.c=.h) LIGHTNINGD_SRC := \ lightningd/lightningd.c \ @@ -32,7 +38,7 @@ LIGHTNINGD_JSMN_HEADERS := daemon/jsmn/jsmn.h LIGHTNINGD_HEADERS := lightningd/lightningd.h \ lightningd/subdaemon.h -$(LIGHTNINGD_OBJS): $(LIGHTNINGD_HEADERS) $(LIGHTNINGD_JSMN_HEADERS) $(BITCOIN_HEADERS) $(CORE_HEADERS) $(GEN_HEADERS) $(CCAN_HEADERS) $(DAEMON_HEADERS) $(LIBBASE58_HEADERS) +$(LIGHTNINGD_OBJS) $(LIGHTNINGD_LIB_OBJS): $(LIGHTNINGD_HEADERS) $(LIGHTNINGD_JSMN_HEADERS) $(BITCOIN_HEADERS) $(CORE_HEADERS) $(GEN_HEADERS) $(CCAN_HEADERS) $(DAEMON_HEADERS) $(LIBBASE58_HEADERS) check-source: $(LIGHTNINGD_SRC:%=check-src-include-order/%) check-source: $(LIGHTNINGD_LIB_SRC:%=check-src-include-order/%) @@ -40,12 +46,12 @@ check-source: $(LIGHTNINGD_CLI_SRC:%=check-src-include-order/%) check-source: $(LIGHTNINGD_HEADERS:%=check-hdr-include-order/%) check-source-bolt: $(LIGHTNINGD_SRC:%=bolt-check/%) $(LIGHTNINGD_HEADERS:%=bolt-check/%) -check-whitespace: $(LIGHTNINGD_SRC:%=check-whitespace/%) $(LIGHTNINGD_HEADERS:%=check-whitespace/%) +check-whitespace: $(LIGHTNINGD_SRC:%=check-whitespace/%) $(LIGHTNINGD_HEADERS:%=check-whitespace/%) $(LIGHTNINGD_LIB_SRC:%=check-whitespace/%) $(LIGHTNINGD_LIB_HEADERS:%=check-whitespace/%) check-lightningd-makefile: @if [ "`ls lightningd/*.h | grep -v lightningd/gen | tr '\012' ' '`" != "`echo $(LIGHTNINGD_HEADERS) ''`" ]; then echo LIGHTNINGD_HEADERS incorrect; exit 1; fi -lightningd/lightningd: $(LIGHTNINGD_OBJS) $(LIGHTNINGD_LIB_OBJS) $(LIGHTNINGD_JSMN_OBJS) $(CORE_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(CCAN_OBJS) $(LIBBASE58_OBJS) libsecp256k1.a +lightningd/lightningd: $(LIGHTNINGD_OBJS) $(LIGHTNINGD_OLD_OBJS) $(LIGHTNINGD_LIB_OBJS) $(LIGHTNINGD_JSMN_OBJS) $(CORE_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(CCAN_OBJS) $(LIBBASE58_OBJS) libsecp256k1.a clean: lightningd-clean diff --git a/lightningd/cryptomsg.c b/lightningd/cryptomsg.c new file mode 100644 index 000000000..75835393f --- /dev/null +++ b/lightningd/cryptomsg.c @@ -0,0 +1,308 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct crypto_state { + /* Received and sent nonces. */ + u64 rn, sn; + /* Sending and receiving keys. */ + struct sha256 sk, rk; + /* Chaining key for re-keying */ + struct sha256 s_ck, r_ck; + + /* Peer who owns us: peer->crypto_state == this */ + struct peer *peer; + + /* Output and input buffers. */ + u8 *out, *in; + struct io_plan *(*next_in)(struct io_conn *, struct peer *, u8 *); + struct io_plan *(*next_out)(struct io_conn *, struct peer *); +}; + +static void hkdf_two_keys(struct sha256 *out1, struct sha256 *out2, + const struct sha256 *in1, + const struct sha256 *in2) +{ + /* BOLT #8: + * + * * `HKDF(salt,ikm)`: a function is defined in [5](#reference-5), + * evaluated with a zero-length `info` field. + * * All invocations of the `HKDF` implicitly return `64-bytes` + * of cryptographic randomness using the extract-and-expand + * component of the `HKDF`. + */ + struct sha256 okm[2]; + + BUILD_ASSERT(sizeof(okm) == 64); + hkdf_sha256(okm, sizeof(okm), in1, sizeof(*in1), in2, sizeof(*in2), + NULL, 0); + *out1 = okm[0]; + *out2 = okm[1]; +} + +static void maybe_rotate_key(u64 *n, struct sha256 *k, struct sha256 *ck) +{ + struct sha256 new_k, new_ck; + + /* BOLT #8: + * + * A key is to be rotated after a party sends of decrypts + * `1000` messages with it. This can be properly accounted + * for by rotating the key once the nonce dedicated to it + * exceeds `1000`. + */ + if (*n != 1000) + return; + + /* BOLT #8: + * + * Key rotation for a key `k` is performed according to the following: + * + * * Let `ck` be the chaining key obtained at the end of `Act Three`. + * * `ck', k' = HKDF(ck, k)` + * * Reset the nonce for the key to `n = 0`. + * * `k = k'` + * * `ck = ck'` + */ + hkdf_two_keys(&new_ck, &new_k, ck, k); + status_trace("# 0x%s, 0x%s = HKDF(0x%s, 0x%s)", + tal_hexstr(trc, &new_ck, sizeof(new_ck)), + tal_hexstr(trc, &new_k, sizeof(new_k)), + tal_hexstr(trc, ck, sizeof(*ck)), + tal_hexstr(trc, k, sizeof(*k))); + *ck = new_ck; + *k = new_k; + *n = 0; +} + +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 (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)); +} + +static struct io_plan *peer_decrypt_body(struct io_conn *conn, + struct crypto_state *cs) +{ + unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; + unsigned long long mlen; + u8 *decrypted = tal_arr(cs->in, u8, tal_count(cs->in) - 16), *in; + struct io_plan *plan; + + le64_nonce(npub, cs->rn++); + + /* BOLT #8: + * + * * Decrypt `c` using `ChaCha20-Poly1305`, `rn`, and `rk` to + * obtain decrypted plaintext packet `p`. + * + * * The nonce `rn` MUST be incremented after this step. + */ + if (crypto_aead_chacha20poly1305_ietf_decrypt(decrypted, + &mlen, NULL, + memcheck(cs->in, + tal_count(cs->in)), + tal_count(cs->in), + NULL, 0, + npub, cs->rk.u.u8) != 0) { + /* FIXME: Report error! */ + return io_close(conn); + } + assert(mlen == tal_count(decrypted)); + + maybe_rotate_key(&cs->rn, &cs->rk, &cs->r_ck); + + /* Steal cs->in: we free it after, and decrypted too unless + * they steal but be careful not to touch anything after + * next_in (could free itself) */ + in = tal_steal(NULL, cs->in); + cs->in = NULL; + + plan = cs->next_in(conn, cs->peer, decrypted); + tal_free(in); + return plan; +} + +static struct io_plan *peer_decrypt_header(struct io_conn *conn, + struct crypto_state *cs) +{ + unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; + unsigned long long mlen; + be16 len; + + le64_nonce(npub, cs->rn++); + + /* BOLT #8: + * + * * Let the encrypted length prefix be known as `lc` + * + * * Decrypt `lc` using `ChaCha20-Poy1305`, `rn`, and `rk` to + * obtain size of the encrypted packet `l`. + * * A zero-length byte slice is to be passed as the AD + * (associated data). + * * The nonce `rn` MUST be incremented after this step. + */ + if (crypto_aead_chacha20poly1305_ietf_decrypt((unsigned char *)&len, + &mlen, NULL, + memcheck(cs->in, + tal_count(cs->in)), + tal_count(cs->in), + NULL, 0, + npub, cs->rk.u.u8) != 0) { + /* FIXME: Report error! */ + return io_close(conn); + } + assert(mlen == sizeof(len)); + tal_free(cs->in); + + /* BOLT #8: + * + * * Read _exactly_ `l+16` bytes from the network buffer, let + * the bytes be known as `c`. + */ + cs->in = tal_arr(cs, u8, (u32)be16_to_cpu(len) + 16); + return io_read(conn, cs->in, tal_count(cs->in), peer_decrypt_body, cs); +} + +struct io_plan *peer_read_message(struct io_conn *conn, + struct crypto_state *cs, + struct io_plan *(*next)(struct io_conn *, + struct peer *, + u8 *msg)) +{ + assert(!cs->in); + /* BOLT #8: + * + * ### Decrypting Messages + * + * In order to decrypt the _next_ message in the network + * stream, the following is done: + * + * * Read _exactly_ `18-bytes` from the network buffer. + */ + cs->in = tal_arr(cs, u8, 18); + cs->next_in = next; + return io_read(conn, cs->in, 18, peer_decrypt_header, cs); +} + +static struct io_plan *peer_write_done(struct io_conn *conn, + struct crypto_state *cs) +{ + cs->out = tal_free(cs->out); + return cs->next_out(conn, cs->peer); +} + +struct io_plan *peer_write_message(struct io_conn *conn, + struct crypto_state *cs, + const u8 *msg, + struct io_plan *(*next)(struct io_conn *, + struct peer *)) +{ + unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; + unsigned long long clen, mlen = tal_count(msg); + be16 l; + int ret; + + assert(!cs->out); + + cs->out = tal_arr(cs, u8, sizeof(l) + 16 + mlen + 16); + cs->next_out = next; + + /* BOLT #8: + * + * In order to encrypt a lightning message (`m`), given a + * sending key (`sk`), and a nonce (`sn`), the following is done: + * + * + * * let `l = len(m)`, + * where `len` obtains the length in bytes of the lightning message. + * + * * Serialize `l` into `2-bytes` encoded as a big-endian integer. + */ + l = cpu_to_be16(mlen); + + /* BOLT #8: + * + * * Encrypt `l` using `ChaChaPoly-1305`, `sn`, and `sk` to obtain `lc` + * (`18-bytes`) + * * The nonce `sn` is encoded as a 96-bit big-endian number. + * * The nonce `sn` MUST be incremented after this step. + * * A zero-length byte slice is to be passed as the AD + */ + le64_nonce(npub, cs->sn++); + ret = crypto_aead_chacha20poly1305_ietf_encrypt(cs->out, &clen, + (unsigned char *) + memcheck(&l, sizeof(l)), + sizeof(l), + NULL, 0, + NULL, npub, + cs->sk.u.u8); + assert(ret == 0); + assert(clen == sizeof(l) + 16); + + /* BOLT #8: + * + * * Finally encrypt the message itself (`m`) using the same + * procedure used to encrypt the length prefix. Let + * encrypted ciphertext be known as `c`. + * + * * The nonce `sn` MUST be incremented after this step. + */ + le64_nonce(npub, cs->sn++); + ret = crypto_aead_chacha20poly1305_ietf_encrypt(cs->out + clen, &clen, + memcheck(msg, mlen), + mlen, + NULL, 0, + NULL, npub, + cs->sk.u.u8); + assert(ret == 0); + assert(clen == mlen + 16); + + maybe_rotate_key(&cs->sn, &cs->sk, &cs->s_ck); + + /* BOLT #8: + * * Send `lc || c` over the network buffer. + */ + return io_write(conn, cs->out, tal_count(cs->out), peer_write_done, cs); +} + +struct crypto_state *crypto_state(struct peer *peer, + const struct sha256 *sk, + const struct sha256 *rk, + const struct sha256 *rck, + const struct sha256 *sck, + u64 rn, u64 sn) +{ + struct crypto_state *cs = tal(peer, struct crypto_state); + + cs->rn = rn; + cs->sn = sn; + cs->sk = *sk; + cs->rk = *rk; + cs->s_ck = *sck; + cs->r_ck = *rck; + cs->peer = peer; + cs->out = cs->in = NULL; + + return cs; +} diff --git a/lightningd/cryptomsg.h b/lightningd/cryptomsg.h new file mode 100644 index 000000000..47ae10b59 --- /dev/null +++ b/lightningd/cryptomsg.h @@ -0,0 +1,32 @@ +#ifndef LIGHTNING_LIGHTNINGD_CRYPTOMSG_H +#define LIGHTNING_LIGHTNINGD_CRYPTOMSG_H +#include "config.h" +#include + +struct io_conn; +struct peer; +struct sha256; + +/* Initializes peer->crypto_state */ +struct crypto_state *crypto_state(struct peer *peer, + const struct sha256 *sk, + const struct sha256 *rk, + const struct sha256 *rck, + const struct sha256 *sck, + u64 rn, u64 sn); + +/* Get decrypted message */ +struct io_plan *peer_read_message(struct io_conn *conn, + struct crypto_state *cs, + struct io_plan *(*next)(struct io_conn *, + struct peer *, + u8 *msg)); + +/* Sends and frees message */ +struct io_plan *peer_write_message(struct io_conn *conn, + struct crypto_state *cs, + const u8 *msg, + struct io_plan *(*next)(struct io_conn *, + struct peer *)); + +#endif /* LIGHTNING_LIGHTNINGD_CRYPTOMSG_H */