lnsocket

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

commit 3ce5ac8682fc4bb84bf59317dab05ebb4efd1d41
parent 02f5edef454f8d59b5036b3bc8744159ab548062
Author: William Casarin <jb55@jb55.com>
Date:   Sun, 16 Jan 2022 11:55:30 -0800

import cryptomsg from clightning

Diffstat:
MMakefile | 2+-
Acrypto.c | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrypto.h | 6++++++
Mhandshake.c | 48+++++-------------------------------------------
4 files changed, 225 insertions(+), 44 deletions(-)

diff --git a/Makefile b/Makefile @@ -5,7 +5,7 @@ LDFLAGS= SUBMODULES=deps/libsodium deps/secp256k1 ARS=deps/secp256k1/.libs/libsecp256k1.a deps/libsodium/src/libsodium/.libs/libsodium.a -OBJS=sha256.o hkdf.o hmac.o sha512.o lnsocket.o error.o handshake.o +OBJS=sha256.o hkdf.o hmac.o sha512.o lnsocket.o error.o handshake.o crypto.o DEPS=$(OBJS) config.h all: test diff --git a/crypto.c b/crypto.c @@ -0,0 +1,213 @@ + +#include "config.h" +#include "crypto.h" +#include "endian.h" +#include "typedefs.h" +#include "compiler.h" +#include "hkdf.h" + +#include <assert.h> +#include <sodium/crypto_aead_chacha20poly1305.h> + +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)); +} + +/* out1, out2 = HKDF(in1, in2)` */ +void hkdf_two_keys(struct secret *out1, struct secret *out2, + const struct secret *in1, + const struct secret *in2) +{ + /* 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]; + size_t in2_size = in2 == NULL ? 0 : sizeof(*in2); + + hkdf_sha256(okm, sizeof(okm), in1, sizeof(*in1), in2, in2_size, + NULL, 0); + *out1 = okm[0]; + *out2 = okm[1]; +} + + +static void maybe_rotate_key(u64 *n, struct secret *k, struct secret *ck) +{ + struct secret new_k, new_ck; + + /* BOLT #8: + * + * A key is to be rotated after a party encrypts or decrypts 1000 times + * with it (i.e. every 500 messages). 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 + * steps: + * + * 1. Let `ck` be the chaining key obtained at the end of Act Three. + * 2. `ck', k' = HKDF(ck, k)` + * 3. Reset the nonce for the key to `n = 0`. + * 4. `k = k'` + * 5. `ck = ck'` + */ + hkdf_two_keys(&new_ck, &new_k, ck, k); + + *ck = new_ck; + *k = new_k; + *n = 0; +} + +int cryptomsg_decrypt_body(struct crypto_state *cs, const u8 *in, size_t inlen, u8 *out, size_t outcap) +{ + unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; + unsigned long long mlen; + + if (inlen < 16 || outcap < inlen - 16) + return 0; + + le64_nonce(npub, cs->rn++); + + /* BOLT #8: + * + * 5. 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(out, + &mlen, NULL, + memcheck(in, inlen), + inlen, + NULL, 0, + npub, cs->rk.data) != 0) { + /* FIXME: Report error! */ + return 0; + } + + assert(mlen == inlen - 16); + + maybe_rotate_key(&cs->rn, &cs->rk, &cs->r_ck); + + return 1; +} + +int cryptomsg_decrypt_header(struct crypto_state *cs, u8 hdr[18], u16 *lenp) +{ + unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; + unsigned long long mlen; + be16 len; + + le64_nonce(npub, cs->rn++); + + /* BOLT #8: + * + * 2. Let the encrypted length prefix be known as `lc`. + * 3. Decrypt `lc` (using `ChaCha20-Poly1305`, `rn`, and `rk`), to + * obtain the 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(hdr, 18), 18, + NULL, 0, + npub, cs->rk.data) != 0) { + return 0; + } + assert(mlen == sizeof(len)); + *lenp = be16_to_cpu(len); + return 1; +} + +u8 *cryptomsg_encrypt_msg(struct crypto_state *cs, const u8 *msg, unsigned long long mlen, u8 *out, size_t *outlen, size_t outcap) +{ + unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; + unsigned long long clen; + be16 l; + int ret; + + if (outcap < sizeof(l) + 16 + mlen + 16) + return NULL; + + /* BOLT #8: + * + * In order to encrypt and send a Lightning message (`m`) to the + * network stream, given a sending key (`sk`) and a nonce (`sn`), the + * following steps are completed: + * + * 1. Let `l = len(m)`. + * * where `len` obtains the length in bytes of the Lightning + * message + * + * 2. Serialize `l` into 2 bytes encoded as a big-endian integer. + */ + l = cpu_to_be16(mlen); + + /* BOLT #8: + * + * 3. Encrypt `l` (using `ChaChaPoly-1305`, `sn`, and `sk`), to obtain + * `lc` (18 bytes) + * * The nonce `sn` is encoded as a 96-bit little-endian number. As + * the decoded nonce is 64 bits, the 96-bit nonce is encoded as: + * 32 bits of leading 0s followed by a 64-bit value. + * * The nonce `sn` MUST be incremented after this step. + * * A zero-length byte slice is to be passed as the AD (associated + data). + */ + le64_nonce(npub, cs->sn++); + ret = crypto_aead_chacha20poly1305_ietf_encrypt(out, &clen, + (unsigned char *) + memcheck(&l, sizeof(l)), + sizeof(l), + NULL, 0, + NULL, npub, + cs->sk.data); + assert(ret == 0); + assert(clen == sizeof(l) + 16); + + /* BOLT #8: + * + * 4. 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(out + clen, &clen, + memcheck(msg, mlen), + mlen, + NULL, 0, + NULL, npub, + cs->sk.data); + assert(ret == 0); + assert(clen == mlen + 16); + + maybe_rotate_key(&cs->sn, &cs->sk, &cs->s_ck); + + return out; +} diff --git a/crypto.h b/crypto.h @@ -3,6 +3,7 @@ #define LNSOCKET_CRYPTO_H #include <secp256k1.h> +#include "typedefs.h" #define PUBKEY_CMPR_LEN 33 @@ -36,5 +37,10 @@ struct crypto_state { struct secret s_ck, r_ck; }; +void le64_nonce(unsigned char *npub, u64 nonce); + +void hkdf_two_keys(struct secret *out1, struct secret *out2, + const struct secret *in1, + const struct secret *in2); #endif /* LNSOCKET_CRYPTO_H */ diff --git a/handshake.c b/handshake.c @@ -68,44 +68,6 @@ static void sha_mix_in_key(secp256k1_context *ctx, struct sha256 *h, 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, @@ -260,9 +222,9 @@ static int handshake_success(struct lnsocket *ln, struct handshake *h) * generated. */ if (h->side == RESPONDER) - hkdf_two_keys(&cs->rk, &cs->sk, &h->ck, NULL, 0); + hkdf_two_keys(&cs->rk, &cs->sk, &h->ck, NULL); else - hkdf_two_keys(&cs->sk, &cs->rk, &h->ck, NULL, 0); + hkdf_two_keys(&cs->sk, &cs->rk, &h->ck, NULL); cs->rn = cs->sn = 0; cs->r_ck = cs->s_ck = h->ck; @@ -304,7 +266,7 @@ static int act_three_initiator(struct lnsocket *ln, struct handshake *h) * 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)); + hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss); /* BOLT #8: * @@ -390,7 +352,7 @@ static int act_two_initiator(struct lnsocket *ln, struct handshake *h) * * 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)); + hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss); /* BOLT #8: * @@ -448,7 +410,7 @@ int act_one_initiator(struct lnsocket *ln, struct handshake *h) * * 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)); + hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss); /* BOLT #8: * 5. `c = encryptWithAD(temp_k1, 0, h, zero)`