Browse Source
After the handshake, it's a simple matter of AEAD and key rotation. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>ppa-0.6.1
Rusty Russell
8 years ago
3 changed files with 350 additions and 4 deletions
@ -0,0 +1,308 @@ |
|||
#include <assert.h> |
|||
#include <ccan/build_assert/build_assert.h> |
|||
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h> |
|||
#include <ccan/crypto/sha256/sha256.h> |
|||
#include <ccan/endian/endian.h> |
|||
#include <ccan/mem/mem.h> |
|||
#include <ccan/short_types/short_types.h> |
|||
#include <lightningd/cryptomsg.h> |
|||
#include <sodium/crypto_aead_chacha20poly1305.h> |
|||
#include <status.h> |
|||
#include <utils.h> |
|||
#include <wire/wire_io.h> |
|||
|
|||
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; |
|||
} |
@ -0,0 +1,32 @@ |
|||
#ifndef LIGHTNING_LIGHTNINGD_CRYPTOMSG_H |
|||
#define LIGHTNING_LIGHTNINGD_CRYPTOMSG_H |
|||
#include "config.h" |
|||
#include <ccan/short_types/short_types.h> |
|||
|
|||
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 */ |
Loading…
Reference in new issue