commit 3ce5ac8682fc4bb84bf59317dab05ebb4efd1d41
parent 02f5edef454f8d59b5036b3bc8744159ab548062
Author: William Casarin <jb55@jb55.com>
Date: Sun, 16 Jan 2022 11:55:30 -0800
import cryptomsg from clightning
Diffstat:
M | Makefile | | | 2 | +- |
A | crypto.c | | | 213 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | crypto.h | | | 6 | ++++++ |
M | handshake.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)`