From a88ac22711a3e46b49ad39387f50adc8c5c86a37 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Oct 2017 20:37:50 +1030 Subject: [PATCH] gossipd: include ccan/io version of handshake code, with tests. Signed-off-by: Rusty Russell --- gossipd/handshake.c | 990 +++++++++++++++++++++++++++ gossipd/handshake.h | 47 ++ gossipd/test/run-initiator-success.c | 227 ++++++ gossipd/test/run-responder-success.c | 223 ++++++ 4 files changed, 1487 insertions(+) create mode 100644 gossipd/handshake.c create mode 100644 gossipd/handshake.h create mode 100644 gossipd/test/run-initiator-success.c create mode 100644 gossipd/test/run-responder-success.c diff --git a/gossipd/handshake.c b/gossipd/handshake.c new file mode 100644 index 000000000..247a2ceda --- /dev/null +++ b/gossipd/handshake.c @@ -0,0 +1,990 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HSM_FD 3 + +#ifndef SUPERVERBOSE +#define SUPERVERBOSE(...) +#endif + +enum bolt8_side { + INITIATOR, + RESPONDER +}; + +/* BOLT #8: + * + * Act One is sent from initiator tog responder. During `Act One`, the + * initiator attempts to satisfy an implicit challenge by the responder. To + * complete this challenge, the initiator _must_ know the static public key of + * the responder. + */ +struct act_one { + u8 v; + u8 pubkey[PUBKEY_DER_LEN]; + u8 tag[crypto_aead_chacha20poly1305_ietf_ABYTES]; +}; + +/* BOLT #8: The handshake message is _exactly_ `50 bytes` */ +#define ACT_ONE_SIZE 50 /* ARM's stupid ABI adds padding. */ + +static inline void check_act_one(const struct act_one *act1) +{ + /* BOLT #8: + * + * : `1 byte` for the handshake version, `33 bytes` for the compressed + * ephemeral public key of the initiator, and `16 bytes` for the + * `poly1305` tag. + */ + BUILD_ASSERT(sizeof(act1->v) == 1); + BUILD_ASSERT(sizeof(act1->pubkey) == 33); + BUILD_ASSERT(sizeof(act1->tag) == 16); +} + +/* BOLT #8: + * + * `Act Two` is sent from the responder to the initiator. `Act Two` will + * _only_ take place if `Act One` was successful. `Act One` was successful if + * the responder was able to properly decrypt and check the `MAC` of the tag + * sent at the end of `Act One`. + */ +struct act_two { + u8 v; + u8 pubkey[PUBKEY_DER_LEN]; + u8 tag[crypto_aead_chacha20poly1305_ietf_ABYTES]; +}; + +/* BOLT #8: The handshake is _exactly_ `50 bytes:` */ +#define ACT_TWO_SIZE 50 /* ARM's stupid ABI adds padding. */ + +static inline void check_act_two(const struct act_two *act2) +{ + /* BOLT #8: + * `1 byte` for the handshake version, + * `33 bytes` for the compressed ephemeral public key of the initiator, and + * `16 bytes` for the `poly1305` tag. + */ + BUILD_ASSERT(sizeof(act2->v) == 1); + BUILD_ASSERT(sizeof(act2->pubkey) == 33); + BUILD_ASSERT(sizeof(act2->tag) == 16); +} + +/* BOLT #8: + * + * `Act Three` is the final phase in the authenticated key agreement described + * in this section. This act is sent from the initiator to the responder as a + * final concluding step. `Act Three` is only executed `iff` `Act Two` was + * successful. During `Act Three`, the initiator transports its static public + * key to the responder encrypted with _strong_ forward secrecy using the + * accumulated `HKDF` derived secret key at this point of the handshake. + */ +struct act_three { + u8 v; + u8 ciphertext[PUBKEY_DER_LEN + crypto_aead_chacha20poly1305_ietf_ABYTES]; + u8 tag[crypto_aead_chacha20poly1305_ietf_ABYTES]; +}; + +/* BOLT #8: The handshake is _exactly_ `66 bytes` */ +#define ACT_THREE_SIZE 66 /* ARM's stupid ABI adds padding. */ + +static inline void check_act_three(const struct act_three *act3) +{ + /* BOLT #8: + * + * `1 byte` for the handshake version, `33 bytes` for the ephemeral + * public key encrypted with the `ChaCha20` stream cipher, `16 bytes` + * for the encrypted public key's tag generated via the `AEAD` + * construction, and `16 bytes` for a final authenticating tag. + */ + BUILD_ASSERT(sizeof(act3->v) == 1); + BUILD_ASSERT(sizeof(act3->ciphertext) == 33 + 16); + BUILD_ASSERT(sizeof(act3->tag) == 16); +} + +/* BOLT #8: + * + * * `generateKey()` + * * where generateKey generates and returns a fresh `secp256k1` keypair + * * the object returned by `generateKey` has two attributes: + * * `.pub`: which returns an abstract object representing the + * public key + * * `.priv`: which represents the private key used to generate the + * public key + */ +struct keypair { + struct pubkey pub; + struct privkey priv; +}; + +/* BOLT #8: + * + * Throughout the handshake process, each side maintains these variables: + * + * * `ck`: The **chaining key**. This value is the accumulated hash of all + * previous ECDH outputs. At the end of the handshake, `ck` is used to + * derive the encryption keys for lightning messages. + * + * * `h`: The **handshake hash**. This value is the accumulated hash of _all_ + * handshake data that has been sent and received so far during the + * handshake process. + * + * * `temp_k1`, `temp_k2`, `temp_k3`: **intermediate keys** used to + * encrypt/decrypt the zero-length AEAD payloads at the end of each + * handshake message. + * + * * `e`: A party's **ephemeral keypair**. For each session a node MUST + * generate a new ephemeral key with strong cryptographic randomness. + * + * * `s`: A party's **static public key** (`ls` for local, `rs` for remote) + */ +struct handshake { + struct secret ck; + struct secret temp_k; + struct sha256 h; + struct keypair e; + struct secret ss; + + /* Used between the Acts */ + struct pubkey re; + struct act_one act1; + struct act_two act2; + struct act_three act3; + + /* Who we are */ + struct pubkey my_id; + /* Who they are: set already if we're initiator. */ + struct pubkey their_id; + + /* Are we initiator or responder. */ + enum bolt8_side side; + + /* Function to call once handshake complete. */ + struct io_plan *(*cb)(struct io_conn *conn, + const struct pubkey *their_id, + const struct crypto_state *cs, + void *cbarg); + void *cbarg; +}; + +static struct keypair generate_key(void) +{ + struct keypair k; + + do { + randombytes_buf(k.priv.secret.data, sizeof(k.priv.secret.data)); + } while (!secp256k1_ec_pubkey_create(secp256k1_ctx, + &k.pub.pubkey, k.priv.secret.data)); + return k; +} + +/* h = SHA-256(h || data) */ +static void sha_mix_in(struct sha256 *h, const void *data, size_t len) +{ + struct sha256_ctx shactx; + + sha256_init(&shactx); + sha256_update(&shactx, h, sizeof(*h)); + sha256_update(&shactx, data, len); + sha256_done(&shactx, h); +} + +/* h = SHA-256(h || pub.serializeCompressed()) */ +static void sha_mix_in_key(struct sha256 *h, const struct pubkey *key) +{ + u8 der[PUBKEY_DER_LEN]; + size_t len = sizeof(der); + + secp256k1_ec_pubkey_serialize(secp256k1_ctx, der, &len, &key->pubkey, + SECP256K1_EC_COMPRESSED); + assert(len == sizeof(der)); + 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 is defined in [3](#reference-3), + * 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 secret okm[2]; + + SUPERVERBOSE("# HKDF(0x%s,%s%s)", + tal_hexstr(trc, in1, sizeof(*in1)), + in2_size ? "0x" : "zero", + tal_hexstr(trc, in2, in2_size)); + BUILD_ASSERT(sizeof(okm) == 64); + 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 (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); + SUPERVERBOSE("# encryptWithAD(0x%s, 0x%s, 0x%s, %s%s)", + tal_hexstr(trc, k, sizeof(*k)), + tal_hexstr(trc, npub, sizeof(npub)), + tal_hexstr(trc, additional_data, additional_data_len), + plaintext_len ? "0x" : "", + tal_hexstr(trc, plaintext, plaintext_len)); + + 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); +} + +/* BOLT #8: + * * `decryptWithAD(k, n, ad, ciphertext)`: outputs `decrypt(k, n, ad, + * ciphertext)` + * * where `decrypt` is an evaluation of `ChaCha20-Poly1305` (IETF + * variant) with the passed arguments, with nonce `n` + */ +static bool decrypt(const struct secret *k, u64 nonce, + const void *additional_data, size_t additional_data_len, + const void *ciphertext, size_t ciphertext_len, + void *output, size_t outputlen) +{ + unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; + unsigned long long mlen; + + assert(outputlen == ciphertext_len - crypto_aead_chacha20poly1305_ietf_ABYTES); + + le64_nonce(npub, nonce); + BUILD_ASSERT(sizeof(*k) == crypto_aead_chacha20poly1305_ietf_KEYBYTES); + SUPERVERBOSE("# decryptWithAD(0x%s, 0x%s, 0x%s, 0x%s)", + tal_hexstr(trc, k, sizeof(*k)), + tal_hexstr(trc, npub, sizeof(npub)), + tal_hexstr(trc, additional_data, additional_data_len), + tal_hexstr(trc, ciphertext, ciphertext_len)); + if (crypto_aead_chacha20poly1305_ietf_decrypt(output, &mlen, NULL, + memcheck(ciphertext, ciphertext_len), + ciphertext_len, + additional_data, additional_data_len, + npub, k->data) != 0) + return false; + + assert(mlen == ciphertext_len - crypto_aead_chacha20poly1305_ietf_ABYTES); + return true; +} + +static struct io_plan *handshake_failed_(struct io_conn *conn, + struct handshake *h, + const char *function, int line) +{ + status_trace("%s: handshake failed %s:%u", + h->side == RESPONDER ? "Responder" : "Initiator", + function, line); + return io_close(conn); +} +#define handshake_failed(conn, h) \ + handshake_failed_((conn), (h), __func__, __LINE__) + +static struct io_plan *handshake_succeeded(struct io_conn *conn, + struct handshake *h) +{ + struct crypto_state cs; + struct io_plan *(*cb)(struct io_conn *conn, + const struct pubkey *their_id, + const struct crypto_state *cs, + void *cbarg); + void *cbarg; + struct pubkey their_id; + + /* BOLT #8: + * + * * `rk, sk = HKDF(ck, zero)` + * * where `zero` is a zero-length plaintext, `rk` is the key to + * be used by the responder to decrypt the messages sent by the + * initiator, and `sk` is the key to be used by the responder + * to encrypt messages to the initiator, + * + * * This step generates the final encryption keys to be used for + * sending and receiving messages for the duration of the + * session. + */ + if (h->side == RESPONDER) + hkdf_two_keys(&cs.rk, &cs.sk, &h->ck, NULL, 0); + else + hkdf_two_keys(&cs.sk, &cs.rk, &h->ck, NULL, 0); + + cs.rn = cs.sn = 0; + cs.r_ck = cs.s_ck = h->ck; + + cb = h->cb; + cbarg = h->cbarg; + their_id = h->their_id; + + tal_free(h); + return cb(conn, &their_id, &cs, cbarg); +} + +static struct handshake *new_handshake(const tal_t *ctx, + const struct pubkey *responder_id) +{ + struct handshake *handshake = tal(ctx, struct handshake); + + /* BOLT #8: + * + * Before the start of the first act, both sides initialize their + * per-sessions state as follows: + * + * 1. `h = SHA-256(protocolName)` + * * where `protocolName = "Noise_XK_secp256k1_ChaChaPoly_SHA256"` + * encoded as an ASCII string. + */ + sha256(&handshake->h, "Noise_XK_secp256k1_ChaChaPoly_SHA256", + strlen("Noise_XK_secp256k1_ChaChaPoly_SHA256")); + + /* BOLT #8: + * + * 2. `ck = h` + */ + BUILD_ASSERT(sizeof(handshake->h) == sizeof(handshake->ck)); + memcpy(&handshake->ck, &handshake->h, sizeof(handshake->ck)); + SUPERVERBOSE("# ck=%s", + tal_hexstr(trc, &handshake->ck, sizeof(handshake->ck))); + + /* BOLT #8: + * + * 3. `h = SHA-256(h || prologue)` + * * where `prologue` is the ASCII string: `lightning`. + */ + sha_mix_in(&handshake->h, "lightning", strlen("lightning")); + + /* BOLT #8: + * + * As a concluding step, both sides mix the responder's public key + * into the handshake digest: + * + * * The initiating node mixes in the responding node's static public + * key serialized in Bitcoin's DER compressed format: + * * `h = SHA-256(h || rs.pub.serializeCompressed())` + * + * * The responding node mixes in their local static public key + * serialized in Bitcoin's DER compressed format: + * * `h = SHA-256(h || ls.pub.serializeCompressed())` + */ + sha_mix_in_key(&handshake->h, responder_id); + SUPERVERBOSE("# h=%s", + tal_hexstr(trc, &handshake->h, sizeof(handshake->h))); + + return handshake; +} + +static struct io_plan *act_three_initiator(struct io_conn *conn, + struct handshake *h) +{ + u8 spub[PUBKEY_DER_LEN]; + size_t len = sizeof(spub); + + status_trace("Initiator: Act 3"); + + /* BOLT #8: + * * `c = encryptWithAD(temp_k2, 1, h, s.pub.serializeCompressed())` + * * where `s` is the static public key of the initiator. + */ + secp256k1_ec_pubkey_serialize(secp256k1_ctx, spub, &len, + &h->my_id.pubkey, + SECP256K1_EC_COMPRESSED); + encrypt_ad(&h->temp_k, 1, &h->h, sizeof(h->h), spub, sizeof(spub), + h->act3.ciphertext, sizeof(h->act3.ciphertext)); + SUPERVERBOSE("# c=0x%s", + tal_hexstr(trc,h->act3.ciphertext,sizeof(h->act3.ciphertext))); + + /* BOLT #8: + * * `h = SHA-256(h || c)` + */ + sha_mix_in(&h->h, h->act3.ciphertext, sizeof(h->act3.ciphertext)); + SUPERVERBOSE("# h=0x%s", tal_hexstr(trc, &h->h, sizeof(h->h))); + + /* BOLT #8: + * + * * `ss = ECDH(re, s.priv)` + * * where `re` is the ephemeral public key of the responder. + * + */ + if (!hsm_do_ecdh(&h->ss, &h->re)) + return handshake_failed(conn, h); + + SUPERVERBOSE("# ss=0x%s", tal_hexstr(trc, &h->ss, sizeof(h->ss))); + + /* BOLT #8: + * + * * `ck, temp_k3 = HKDF(ck, ss)` + * * Mix the final intermediate shared secret into the running chaining key. + */ + hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss)); + SUPERVERBOSE("# ck,temp_k3=0x%s,0x%s", + tal_hexstr(trc, &h->ck, sizeof(h->ck)), + tal_hexstr(trc, &h->temp_k, sizeof(h->temp_k))); + + /* BOLT #8: + * + * * `t = encryptWithAD(temp_k3, 0, h, zero)` + * * where `zero` is a zero-length plaintext + * + */ + encrypt_ad(&h->temp_k, 0, &h->h, sizeof(h->h), NULL, 0, + h->act3.tag, sizeof(h->act3.tag)); + SUPERVERBOSE("# t=0x%s", + tal_hexstr(trc, h->act3.tag, sizeof(h->act3.tag))); + + /* BOLT #8: + * + * * Send `m = 0 || c || t` over the network buffer. + * + */ + h->act3.v = 0; + + SUPERVERBOSE("output: 0x%s", tal_hexstr(trc, &h->act3, ACT_THREE_SIZE)); + return io_write(conn, &h->act3, ACT_THREE_SIZE, handshake_succeeded, h); +} + +static struct io_plan *act_two_initiator2(struct io_conn *conn, + struct handshake *h) +{ + SUPERVERBOSE("input: 0x%s", tal_hexstr(trc, &h->act2, ACT_TWO_SIZE)); + + /* BOLT #8: + * + * * If `v` is an unrecognized handshake version, then the responder + * MUST abort the connection attempt. + */ + if (h->act2.v != 0) + return handshake_failed(conn, h); + + /* BOLT #8: + * + * * The raw bytes of the remote party's ephemeral public key + * (`re`) are to be deserialized into a point on the curve using + * affine coordinates as encoded by the key's serialized + * composed format. + */ + if (secp256k1_ec_pubkey_parse(secp256k1_ctx, &h->re.pubkey, + h->act2.pubkey, sizeof(h->act2.pubkey)) != 1) + return handshake_failed(conn, h); + + SUPERVERBOSE("# re=0x%s", type_to_string(trc, struct pubkey, &h->re)); + + /* BOLT #8: + * + * * `h = SHA-256(h || re.serializeCompressed())` + */ + sha_mix_in_key(&h->h, &h->re); + SUPERVERBOSE("# h=0x%s", tal_hexstr(trc, &h->h, sizeof(h->h))); + + /* BOLT #8: + * + * * `ss = ECDH(re, e.priv)` + */ + if (!secp256k1_ecdh(secp256k1_ctx, h->ss.data, &h->re.pubkey, + h->e.priv.secret.data)) + return handshake_failed(conn, h); + + SUPERVERBOSE("# ss=0x%s", tal_hexstr(trc, &h->ss, sizeof(h->ss))); + + /* BOLT #8: + * + * * `ck, temp_k2 = HKDF(ck, ss)` + * * This phase generates a new temporary encryption key + * which is used to generate the authenticating MAC. + */ + hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss)); + SUPERVERBOSE("# ck,temp_k2=0x%s,0x%s", + tal_hexstr(trc, &h->ck, sizeof(h->ck)), + tal_hexstr(trc, &h->temp_k, sizeof(h->temp_k))); + + /* BOLT #8: + * + * * `p = decryptWithAD(temp_k2, 0, h, c)` + * * If the MAC check in this operation fails, then the initiator + * MUST terminate the connection without any further messages. + */ + if (!decrypt(&h->temp_k, 0, &h->h, sizeof(h->h), + h->act2.tag, sizeof(h->act2.tag), NULL, 0)) + return handshake_failed(conn, h); + + /* BOLT #8: + * + * * `h = SHA-256(h || c)` + * * Mix the received ciphertext into the handshake digest. This + * step serves to ensure the payload wasn't modified by a MiTM. + */ + sha_mix_in(&h->h, h->act2.tag, sizeof(h->act2.tag)); + SUPERVERBOSE("# h=0x%s", tal_hexstr(trc, &h->h, sizeof(h->h))); + + return act_three_initiator(conn, h); +} + +static struct io_plan *act_two_initiator(struct io_conn *conn, + struct handshake *h) +{ + status_trace("Initiator: Act 2"); + + /* BOLT #8: + * + * * Read _exactly_ `50-bytes` from the network buffer. + * + * * Parse out the read message (`m`) into `v = m[0]`, `re = m[1:33]` + * and `c = m[34:]` + * * where `m[0]` is the _first_ byte of `m`, `m[1:33]` are the + * next `33` bytes of `m` and `m[34:]` is the last 16 bytes of + * `m` + */ + return io_read(conn, &h->act2, ACT_TWO_SIZE, act_two_initiator2, h); +} + +static struct io_plan *act_one_initiator(struct io_conn *conn, + struct handshake *h) +{ + size_t len; + + status_trace("Initiator: Act 1"); + + /* BOLT #8: + * + * **Sender Actions:** + * + * * `e = generateKey()` + */ + h->e = generate_key(); + SUPERVERBOSE("e.priv: 0x%s", + tal_hexstr(trc, &h->e.priv, sizeof(h->e.priv))); + SUPERVERBOSE("e.pub: 0x%s", + type_to_string(trc, struct pubkey, &h->e.pub)); + + /* BOLT #8: + * + * * `h = SHA-256(h || e.pub.serializeCompressed())` + * * The newly generated ephemeral key is accumulated into our + * running handshake digest. + */ + sha_mix_in_key(&h->h, &h->e.pub); + SUPERVERBOSE("# h=0x%s", tal_hexstr(trc, &h->h, sizeof(h->h))); + + /* BOLT #8: + * + * * `ss = ECDH(rs, e.priv)` + * * The initiator performs a `ECDH` between its newly generated + * ephemeral key with the remote node's static public key. + */ + if (!secp256k1_ecdh(secp256k1_ctx, h->ss.data, + &h->their_id.pubkey, h->e.priv.secret.data)) + return handshake_failed(conn, h); + + SUPERVERBOSE("# ss=0x%s", tal_hexstr(trc, h->ss.data, sizeof(h->ss.data))); + + /* BOLT #8: + * + * * `ck, temp_k1 = HKDF(ck, ss)` + * * This phase generates a new temporary encryption key + * which is used to generate the authenticating MAC. + */ + hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss)); + SUPERVERBOSE("# ck,temp_k1=0x%s,0x%s", + tal_hexstr(trc, &h->ck, sizeof(h->ck)), + tal_hexstr(trc, &h->temp_k, sizeof(h->temp_k))); + + /* BOLT #8: + * + * * `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)); + SUPERVERBOSE("# c=%s", + tal_hexstr(trc, h->act1.tag, sizeof(h->act1.tag))); + + /* BOLT #8: + * + * * `h = SHA-256(h || c)` + * * Finally, the generated ciphertext is accumulated into the + * authenticating handshake digest. + */ + sha_mix_in(&h->h, h->act1.tag, sizeof(h->act1.tag)); + SUPERVERBOSE("# h=0x%s", tal_hexstr(trc, &h->h, sizeof(h->h))); + + /* BOLT #8: + * + * * Send `m = 0 || e.pub.serializeCompressed() || c` to the responder over the network buffer. + */ + h->act1.v = 0; + len = sizeof(h->act1.pubkey); + secp256k1_ec_pubkey_serialize(secp256k1_ctx, h->act1.pubkey, &len, + &h->e.pub.pubkey, + SECP256K1_EC_COMPRESSED); + SUPERVERBOSE("output: 0x%s", tal_hexstr(trc, &h->act1, ACT_ONE_SIZE)); + + return io_write(conn, &h->act1, ACT_ONE_SIZE, act_two_initiator, h); +} + +static struct io_plan *act_three_responder2(struct io_conn *conn, + struct handshake *h) +{ + u8 der[PUBKEY_DER_LEN]; + + SUPERVERBOSE("input: 0x%s", tal_hexstr(trc, &h->act3, ACT_THREE_SIZE)); + + /* BOLT #8: + * + * * Parse out the read message (`m`) into `v = m[0]`, `c = m[1:49]` and `t = m[50:]` + */ + + /* BOLT #8: + * + * * If `v` is an unrecognized handshake version, then the responder MUST + * abort the connection attempt. + */ + if (h->act3.v != 0) + return handshake_failed(conn, h); + + /* BOLT #8: + * + * * `rs = decryptWithAD(temp_k2, 1, h, c)` + * * At this point, the responder has recovered the static public key of the + * initiator. + */ + if (!decrypt(&h->temp_k, 1, &h->h, sizeof(h->h), + h->act3.ciphertext, sizeof(h->act3.ciphertext), + der, sizeof(der))) + return handshake_failed(conn, h); + + SUPERVERBOSE("# rs=0x%s", tal_hexstr(trc, der, sizeof(der))); + + if (secp256k1_ec_pubkey_parse(secp256k1_ctx, &h->their_id.pubkey, + der, sizeof(der)) != 1) + return handshake_failed(conn, h); + + /* BOLT #8: + * + * * `h = SHA-256(h || c)` + * + */ + sha_mix_in(&h->h, h->act3.ciphertext, sizeof(h->act3.ciphertext)); + SUPERVERBOSE("# h=0x%s", tal_hexstr(trc, &h->h, sizeof(h->h))); + + /* BOLT #8: + * + * * `ss = ECDH(rs, e.priv)` + * * where `e` is the responder's original ephemeral key + */ + if (!secp256k1_ecdh(secp256k1_ctx, h->ss.data, &h->their_id.pubkey, + h->e.priv.secret.data)) + return handshake_failed(conn, h); + + SUPERVERBOSE("# ss=0x%s", tal_hexstr(trc, &h->ss, sizeof(h->ss))); + + /* BOLT #8: + * * `ck, temp_k3 = HKDF(ck, ss)` + */ + hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss)); + SUPERVERBOSE("# ck,temp_k3=0x%s,0x%s", + tal_hexstr(trc, &h->ck, sizeof(h->ck)), + tal_hexstr(trc, &h->temp_k, sizeof(h->temp_k))); + + /* BOLT #8: + * * `p = decryptWithAD(temp_k3, 0, h, t)` + * * If the MAC check in this operation fails, then the responder MUST + * terminate the connection without any further messages. + * + */ + if (!decrypt(&h->temp_k, 0, &h->h, sizeof(h->h), + h->act3.tag, sizeof(h->act3.tag), NULL, 0)) + return handshake_failed(conn, h); + + return handshake_succeeded(conn, h); +} + +static struct io_plan *act_three_responder(struct io_conn *conn, + struct handshake *h) +{ + status_trace("Responder: Act 3"); + + /* BOLT #8: + * + * **Receiver Actions:** + * + * * Read _exactly_ `66-bytes` from the network buffer. + */ + return io_read(conn, &h->act3, ACT_THREE_SIZE, act_three_responder2, h); +} + +static struct io_plan *act_two_responder(struct io_conn *conn, + struct handshake *h) +{ + size_t len; + + status_trace("Responder: Act 2"); + + /* BOLT #8: + * + * **Sender Actions:** + * + * * `e = generateKey()` + */ + h->e = generate_key(); + SUPERVERBOSE("# e.pub=0x%s e.priv=0x%s", + type_to_string(trc, struct pubkey, &h->e.pub), + tal_hexstr(trc, &h->e.priv, sizeof(h->e.priv))); + + /* BOLT #8: + * + * * `h = SHA-256(h || e.pub.serializeCompressed())` + * * The newly generated ephemeral key is accumulated into our + * running handshake digest. + */ + sha_mix_in_key(&h->h, &h->e.pub); + SUPERVERBOSE("# h=0x%s", tal_hexstr(trc, &h->h, sizeof(h->h))); + + /* BOLT #8: + * + * * `ss = ECDH(re, e.priv)` + * * where `re` is the ephemeral key of the initiator which was + * received during `ActOne`. + */ + if (!secp256k1_ecdh(secp256k1_ctx, h->ss.data, &h->re.pubkey, + h->e.priv.secret.data)) + return handshake_failed(conn, h); + SUPERVERBOSE("# ss=0x%s", tal_hexstr(trc, &h->ss, sizeof(h->ss))); + + /* BOLT #8: + * + * * `ck, temp_k2 = HKDF(ck, ss)` + * * This phase generates a new temporary encryption key + * which is used to generate the authenticating MAC. + */ + hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss)); + SUPERVERBOSE("# ck,temp_k2=0x%s,0x%s", + tal_hexstr(trc, &h->ck, sizeof(h->ck)), + tal_hexstr(trc, &h->temp_k, sizeof(h->temp_k))); + + /* BOLT #8: + * + * * `c = encryptWithAD(temp_k2, 0, h, zero)` + * * where `zero` is a zero-length plaintext + */ + encrypt_ad(&h->temp_k, 0, &h->h, sizeof(h->h), NULL, 0, + h->act2.tag, sizeof(h->act2.tag)); + SUPERVERBOSE("# c=0x%s", tal_hexstr(trc, h->act2.tag, sizeof(h->act2.tag))); + + /* BOLT #8: + * + * * `h = SHA-256(h || c)` + * * Finally, the generated ciphertext is accumulated into the + * authenticating handshake digest. + */ + sha_mix_in(&h->h, h->act2.tag, sizeof(h->act2.tag)); + SUPERVERBOSE("# h=0x%s", tal_hexstr(trc, &h->h, sizeof(h->h))); + + /* BOLT #8: + * + * * Send `m = 0 || e.pub.serializeCompressed() || c` to the initiator over the network buffer. + */ + h->act2.v = 0; + len = sizeof(h->act2.pubkey); + secp256k1_ec_pubkey_serialize(secp256k1_ctx, h->act2.pubkey, &len, + &h->e.pub.pubkey, + SECP256K1_EC_COMPRESSED); + SUPERVERBOSE("output: 0x%s", tal_hexstr(trc, &h->act2, ACT_TWO_SIZE)); + + return io_write(conn, &h->act2, ACT_TWO_SIZE, act_three_responder, h); +} + + +static struct io_plan *act_one_responder2(struct io_conn *conn, + struct handshake *h) +{ + /* BOLT #8: + * + * * If `v` is an unrecognized handshake version, then the responder + * MUST abort the connection attempt. + */ + if (h->act1.v != 0) + return handshake_failed(conn, h); + + /* BOLT #8: + * * The raw bytes of the remote party's ephemeral public key + * (`e`) are to be deserialized into a point on the curve using + * affine coordinates as encoded by the key's serialized + * composed format. + */ + if (secp256k1_ec_pubkey_parse(secp256k1_ctx, &h->re.pubkey, + h->act1.pubkey, sizeof(h->act1.pubkey)) != 1) + return handshake_failed(conn, h); + + SUPERVERBOSE("# re=0x%s", type_to_string(trc, struct pubkey, &h->re)); + + /* BOLT #8: + * + * * `h = SHA-256(h || re.serializeCompressed())` + * * Accumulate the initiator's ephemeral key into the + * authenticating handshake digest. + */ + sha_mix_in_key(&h->h, &h->re); + SUPERVERBOSE("# h=0x%s", tal_hexstr(trc, &h->h, sizeof(h->h))); + + /* BOLT #8: + * * `ss = ECDH(re, s.priv)` + * * The responder performs an `ECDH` between its static public + * key and the initiator's ephemeral public key. + */ + if (!hsm_do_ecdh(&h->ss, &h->re)) + return handshake_failed(conn, h); + + SUPERVERBOSE("# ss=0x%s", tal_hexstr(trc, &h->ss, sizeof(h->ss))); + + /* BOLT #8: + * + * * `ck, temp_k1 = HKDF(ck, ss)` + * * This phase generates a new temporary encryption key + * which will be used to shortly check the + * authenticating MAC. + */ + hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss)); + SUPERVERBOSE("# ck,temp_k1=0x%s,0x%s", + tal_hexstr(trc, &h->ck, sizeof(h->ck)), + tal_hexstr(trc, &h->temp_k, sizeof(h->temp_k))); + + /* BOLT #8: + * + * * `p = decryptWithAD(temp_k1, 0, h, c)` + * * If the MAC check in this operation fails, then the initiator + * does _not_ know our static public key. If so, then the + * responder MUST terminate the connection without any further + * messages. + */ + if (!decrypt(&h->temp_k, 0, &h->h, sizeof(h->h), + h->act1.tag, sizeof(h->act1.tag), NULL, 0)) + return handshake_failed(conn, h); + + /* BOLT #8: + * + * * `h = SHA-256(h || c)` + * * Mix the received ciphertext into the handshake digest. This + * step serves to ensure the payload wasn't modified by a MiTM. + */ + sha_mix_in(&h->h, h->act1.tag, sizeof(h->act1.tag)); + SUPERVERBOSE("# h=0x%s", tal_hexstr(trc, &h->h, sizeof(h->h))); + + return act_two_responder(conn, h); +} + +static struct io_plan *act_one_responder(struct io_conn *conn, + struct handshake *h) +{ + + status_trace("Responder: Act 1"); + + /* BOLT #8: + * + * * Read _exactly_ `50-bytes` from the network buffer. + * + * * Parse out the read message (`m`) into `v = m[0]`, `re = + * m[1:33]` and `c = m[34:]` + * * where `m[0]` is the _first_ byte of `m`, `m[1:33]` are the + * next `33` bytes of `m` and `m[34:]` is the last 16 bytes of + * `m` + */ + return io_read(conn, &h->act1, ACT_ONE_SIZE, act_one_responder2, h); +} + +struct io_plan *responder_handshake_(struct io_conn *conn, + const struct pubkey *my_id, + struct io_plan *(*cb)(struct io_conn *, + const struct pubkey *, + const struct crypto_state *, + void *cbarg), + void *cbarg) +{ + struct handshake *h = new_handshake(conn, my_id); + + h->side = RESPONDER; + h->my_id = *my_id; + h->cbarg = cbarg; + h->cb = cb; + + return act_one_responder(conn, h); +} + +struct io_plan *initiator_handshake_(struct io_conn *conn, + const struct pubkey *my_id, + const struct pubkey *their_id, + struct io_plan *(*cb)(struct io_conn *, + const struct pubkey *, + const struct crypto_state *, + void *cbarg), + void *cbarg) +{ + struct handshake *h = new_handshake(conn, their_id); + + h->side = INITIATOR; + h->my_id = *my_id; + h->their_id = *their_id; + h->cbarg = cbarg; + h->cb = cb; + + return act_one_initiator(conn, h); +} diff --git a/gossipd/handshake.h b/gossipd/handshake.h new file mode 100644 index 000000000..41167ae73 --- /dev/null +++ b/gossipd/handshake.h @@ -0,0 +1,47 @@ +#ifndef LIGHTNING_LIGHTNINGD_GOSSIP_HANDSHAKE_H +#define LIGHTNING_LIGHTNINGD_GOSSIP_HANDSHAKE_H +#include "config.h" +#include + +struct crypto_state; +struct io_conn; +struct pubkey; + +#define initiator_handshake(conn, my_id, their_id, cb, cbarg) \ + initiator_handshake_((conn), (my_id), (their_id), \ + typesafe_cb_preargs(struct io_plan *, void *, \ + (cb), (cbarg), \ + struct io_conn *, \ + const struct pubkey *, \ + const struct crypto_state *), \ + (cbarg)) + + +struct io_plan *initiator_handshake_(struct io_conn *conn, + const struct pubkey *my_id, + const struct pubkey *their_id, + struct io_plan *(*cb)(struct io_conn *, + const struct pubkey *, + const struct crypto_state *, + void *cbarg), + void *cbarg); + + +#define responder_handshake(conn, my_id, cb, cbarg) \ + responder_handshake_((conn), (my_id), \ + typesafe_cb_preargs(struct io_plan *, void *, \ + (cb), (cbarg), \ + struct io_conn *, \ + const struct pubkey *, \ + const struct crypto_state *), \ + (cbarg)) + +struct io_plan *responder_handshake_(struct io_conn *conn, + const struct pubkey *my_id, + struct io_plan *(*cb)(struct io_conn *, + const struct pubkey *, + const struct crypto_state *, + void *cbarg), + void *cbarg); + +#endif /* LIGHTNING_LIGHTNINGD_GOSSIP_HANDSHAKE_H */ diff --git a/gossipd/test/run-initiator-success.c b/gossipd/test/run-initiator-success.c new file mode 100644 index 000000000..7bb32eb0d --- /dev/null +++ b/gossipd/test/run-initiator-success.c @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* AUTOGENERATED MOCKS END */ + +/* No randomness please, we want to replicate test vectors. */ +#include + +static void seed_randomness(u8 *secret, size_t len); +#define randombytes_buf(secret, len) seed_randomness((secret), (len)) + +struct handshake; +static struct io_plan *test_write(struct io_conn *conn, + const void *data, size_t len, + struct io_plan *(*next)(struct io_conn *, + struct handshake *), + struct handshake *h); + +static struct io_plan *test_read(struct io_conn *conn, + void *data, size_t len, + struct io_plan *(*next)(struct io_conn *, + struct handshake *), + struct handshake *h); + +#define SUPERVERBOSE status_trace +void status_trace(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vprintf(fmt, ap); + printf("\n"); + va_end(ap); +} + +#undef io_write +#undef io_read + +#define io_write(conn, data, len, cb, cb_arg) \ + test_write((conn), (data), (len), (cb), (cb_arg)) + +#define io_read(conn, data, len, cb, cb_arg) \ + test_read((conn), (data), (len), (cb), (cb_arg)) + +#include "../handshake.c" +#include +#include +#include + +static struct pubkey pubkey(const char *str) +{ + struct pubkey p; + if (!pubkey_from_hexstr(str, strlen(str), &p)) + abort(); + return p; +} + +static struct privkey privkey(const char *str) +{ + struct privkey p; + if (!hex_decode(str, strlen(str), &p, sizeof(p))) + abort(); + return p; +} + +static bool secret_eq(const struct secret *s, const char *str) +{ + struct secret expect; + if (!hex_decode(str, strlen(str), &expect, sizeof(expect))) + abort(); + return structeq(s, &expect); +} + +secp256k1_context *secp256k1_ctx; +const void *trc; +static struct pubkey rs_pub, ls_pub, e_pub; +static struct privkey ls_priv, e_priv; + +static void seed_randomness(u8 *secret, size_t len) +{ + assert(len == sizeof(e_priv)); + memcpy(secret, &e_priv, len); +} + +/* BOLT #8: + * # Act One + * # h=0x9e0e7de8bb75554f21db034633de04be41a2b8a18da7a319a03c803bf02b396c + * # ss=0x1e2fb3c8fe8fb9f262f649f64d26ecf0f2c0a805a767cf02dc2d77a6ef1fdcc3 + * # HKDF(0x2640f52eebcd9e882958951c794250eedb28002c05d7dc2ea0f195406042caf1,0x1e2fb3c8fe8fb9f262f649f64d26ecf0f2c0a805a767cf02dc2d77a6ef1fdcc3) + * # ck,temp_k1=0xb61ec1191326fa240decc9564369dbb3ae2b34341d1e11ad64ed89f89180582f,0xe68f69b7f096d7917245f5e5cf8ae1595febe4d4644333c99f9c4a1282031c9f + * # encryptWithAD(0xe68f69b7f096d7917245f5e5cf8ae1595febe4d4644333c99f9c4a1282031c9f, 0x000000000000000000000000, 0x9e0e7de8bb75554f21db034633de04be41a2b8a18da7a319a03c803bf02b396c, ) + * # c=0df6086551151f58b8afe6c195782c6a + * # h=0x9d1ffbb639e7e20021d9259491dc7b160aab270fb1339ef135053f6f2cebe9ce + * output: 0x00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a + * # Act Two + * input: 0x0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae + * # re=0x02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27 + * # h=0x38122f669819f906000621a14071802f93f2ef97df100097bcac3ae76c6dc0bf + * # ss=0xc06363d6cc549bcb7913dbb9ac1c33fc1158680c89e972000ecd06b36c472e47 + * # HKDF(0xb61ec1191326fa240decc9564369dbb3ae2b34341d1e11ad64ed89f89180582f,0xc06363d6cc549bcb7913dbb9ac1c33fc1158680c89e972000ecd06b36c472e47) + * # ck,temp_k2=0xe89d31033a1b6bf68c07d22e08ea4d7884646c4b60a9528598ccb4ee2c8f56ba,0x908b166535c01a935cf1e130a5fe895ab4e6f3ef8855d87e9b7581c4ab663ddc + * # decryptWithAD(0x908b166535c01a935cf1e130a5fe895ab4e6f3ef8855d87e9b7581c4ab663ddc, 0x000000000000000000000000, 0x38122f669819f906000621a14071802f93f2ef97df100097bcac3ae76c6dc0bf, 0x6e2470b93aac583c9ef6eafca3f730ae) + * # h=0x90578e247e98674e661013da3c5c1ca6a8c8f48c90b485c0dfa1494e23d56d72 + * # Act Three + * # encryptWithAD(0x908b166535c01a935cf1e130a5fe895ab4e6f3ef8855d87e9b7581c4ab663ddc, 0x000000000100000000000000, 0x90578e247e98674e661013da3c5c1ca6a8c8f48c90b485c0dfa1494e23d56d72, 0x034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa) + * # c=0xb9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c3822 + * # h=0x5dcb5ea9b4ccc755e0e3456af3990641276e1d5dc9afd82f974d90a47c918660 + * # ss=0xb36b6d195982c5be874d6d542dc268234379e1ae4ff1709402135b7de5cf0766 + * # HKDF(0xe89d31033a1b6bf68c07d22e08ea4d7884646c4b60a9528598ccb4ee2c8f56ba,0xb36b6d195982c5be874d6d542dc268234379e1ae4ff1709402135b7de5cf0766) + * # ck,temp_k3=0x919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01,0x981a46c820fb7a241bc8184ba4bb1f01bcdfafb00dde80098cb8c38db9141520 + * # encryptWithAD(0x981a46c820fb7a241bc8184ba4bb1f01bcdfafb00dde80098cb8c38db9141520, 0x000000000000000000000000, 0x5dcb5ea9b4ccc755e0e3456af3990641276e1d5dc9afd82f974d90a47c918660, ) + * # t=0x8dc68b1c466263b47fdf31e560e139ba + * output: 0x00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba + * # HKDF(0x919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01,zero) + * output: sk,rk=0x969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9,0xbb9020b8965f4df047e07f955f3c4b88418984aadc5cdb35096b9ea8fa5c3442 + */ + +/* Here's what we expect: */ +static const char *expect_output[] = { + "00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a", + "00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba" +}; + +static const char *expect_input[] = { + "0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae" +}; + +static const char expect_sk[] = + "969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9"; +static const char expect_rk[] = + "bb9020b8965f4df047e07f955f3c4b88418984aadc5cdb35096b9ea8fa5c3442"; + +static struct io_plan *test_write(struct io_conn *conn, + const void *data, size_t len, + struct io_plan *(*next)(struct io_conn *, + struct handshake *), + struct handshake *h) +{ + static int upto; + char *got; + + assert(upto < ARRAY_SIZE(expect_output)); + got = tal_hexstr(NULL, data, len); + assert(streq(expect_output[upto], got)); + tal_free(got); + upto++; + + return next(conn, h); +} + +static struct io_plan *test_read(struct io_conn *conn, + void *data, size_t len, + struct io_plan *(*next)(struct io_conn *, + struct handshake *), + struct handshake *h) +{ + static int upto; + + assert(upto < ARRAY_SIZE(expect_input)); + if (!hex_decode(expect_input[upto], strlen(expect_input[upto]), + data, len)) + abort(); + upto++; + + return next(conn, h); +} + +static struct io_plan *success(struct io_conn *conn, + const struct pubkey *them, + const struct crypto_state *cs, + void *ctx) +{ + assert(pubkey_eq(them, &rs_pub)); + + assert(secret_eq(&cs->sk, expect_sk)); + assert(secret_eq(&cs->rk, expect_rk)); + + /* No memory leaks please */ + secp256k1_context_destroy(secp256k1_ctx); + tal_free(ctx); + exit(0); +} + +bool hsm_do_ecdh(struct secret *ss, const struct pubkey *point) +{ + return secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, + ls_priv.secret.data) == 1; +} + +int main(void) +{ + tal_t *ctx = tal_tmpctx(NULL); + + trc = tal_tmpctx(ctx); + + secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY + | SECP256K1_CONTEXT_SIGN); + + + /* BOLT #8: + * + * name: transport-initiator successful handshake + * rs.pub: 0x028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7 + * ls.priv: 0x1111111111111111111111111111111111111111111111111111111111111111 + * ls.pub: 0x034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa + * e.priv: 0x1212121212121212121212121212121212121212121212121212121212121212 + * e.pub: 0x036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f7 + */ + rs_pub = pubkey("028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7"); + ls_priv = privkey("1111111111111111111111111111111111111111111111111111111111111111"); + ls_pub = pubkey("034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa"); + e_priv = privkey("1212121212121212121212121212121212121212121212121212121212121212"); + e_pub = pubkey("036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f7"); + + initiator_handshake(ctx, &ls_pub, &rs_pub, success, ctx); + /* Should not exit! */ + abort(); +} diff --git a/gossipd/test/run-responder-success.c b/gossipd/test/run-responder-success.c new file mode 100644 index 000000000..e50816803 --- /dev/null +++ b/gossipd/test/run-responder-success.c @@ -0,0 +1,223 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* AUTOGENERATED MOCKS END */ + +/* No randomness please, we want to replicate test vectors. */ +#include + +static void seed_randomness(u8 *secret, size_t len); +#define randombytes_buf(secret, len) seed_randomness((secret), (len)) + +struct handshake; +static struct io_plan *test_write(struct io_conn *conn, + const void *data, size_t len, + struct io_plan *(*next)(struct io_conn *, + struct handshake *), + struct handshake *h); + +static struct io_plan *test_read(struct io_conn *conn, + void *data, size_t len, + struct io_plan *(*next)(struct io_conn *, + struct handshake *), + struct handshake *h); + +#define SUPERVERBOSE status_trace +void status_trace(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vprintf(fmt, ap); + printf("\n"); + va_end(ap); +} + +#undef io_write +#undef io_read + +#define io_write(conn, data, len, cb, cb_arg) \ + test_write((conn), (data), (len), (cb), (cb_arg)) + +#define io_read(conn, data, len, cb, cb_arg) \ + test_read((conn), (data), (len), (cb), (cb_arg)) + +#include "../handshake.c" +#include +#include +#include + +static struct pubkey pubkey(const char *str) +{ + struct pubkey p; + if (!pubkey_from_hexstr(str, strlen(str), &p)) + abort(); + return p; +} + +static struct privkey privkey(const char *str) +{ + struct privkey p; + if (!hex_decode(str, strlen(str), &p, sizeof(p))) + abort(); + return p; +} + +static bool secret_eq(const struct secret *s, const char *str) +{ + struct secret expect; + if (!hex_decode(str, strlen(str), &expect, sizeof(expect))) + abort(); + return structeq(s, &expect); +} + +secp256k1_context *secp256k1_ctx; +const void *trc; +static struct pubkey ls_pub, e_pub; +static struct privkey ls_priv, e_priv; + +static void seed_randomness(u8 *secret, size_t len) +{ + assert(len == sizeof(e_priv)); + memcpy(secret, &e_priv, len); +} + +/* BOLT #8: + * # Act One + * input: 0x00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a + * # re=0x036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f7 + * # h=0x9e0e7de8bb75554f21db034633de04be41a2b8a18da7a319a03c803bf02b396c + * # ss=0x1e2fb3c8fe8fb9f262f649f64d26ecf0f2c0a805a767cf02dc2d77a6ef1fdcc3 + * # HKDF(0x2640f52eebcd9e882958951c794250eedb28002c05d7dc2ea0f195406042caf1,0x1e2fb3c8fe8fb9f262f649f64d26ecf0f2c0a805a767cf02dc2d77a6ef1fdcc3) + * # ck,temp_k1=0xb61ec1191326fa240decc9564369dbb3ae2b34341d1e11ad64ed89f89180582f,0xe68f69b7f096d7917245f5e5cf8ae1595febe4d4644333c99f9c4a1282031c9f + * # decryptWithAD(0xe68f69b7f096d7917245f5e5cf8ae1595febe4d4644333c99f9c4a1282031c9f, 0x000000000000000000000000, 0x9e0e7de8bb75554f21db034633de04be41a2b8a18da7a319a03c803bf02b396c, 0x0df6086551151f58b8afe6c195782c6a) + * # h=0x9d1ffbb639e7e20021d9259491dc7b160aab270fb1339ef135053f6f2cebe9ce + * # Act Two + * # e.pub=0x02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27 e.priv=0x2222222222222222222222222222222222222222222222222222222222222222 + * # h=0x38122f669819f906000621a14071802f93f2ef97df100097bcac3ae76c6dc0bf + * # ss=0xc06363d6cc549bcb7913dbb9ac1c33fc1158680c89e972000ecd06b36c472e47 + * # HKDF(0xb61ec1191326fa240decc9564369dbb3ae2b34341d1e11ad64ed89f89180582f,0xc06363d6cc549bcb7913dbb9ac1c33fc1158680c89e972000ecd06b36c472e47) + * # ck,temp_k2=0xe89d31033a1b6bf68c07d22e08ea4d7884646c4b60a9528598ccb4ee2c8f56ba,0x908b166535c01a935cf1e130a5fe895ab4e6f3ef8855d87e9b7581c4ab663ddc + * # encryptWithAD(0x908b166535c01a935cf1e130a5fe895ab4e6f3ef8855d87e9b7581c4ab663ddc, 0x000000000000000000000000, 0x38122f669819f906000621a14071802f93f2ef97df100097bcac3ae76c6dc0bf, ) + * # c=0x6e2470b93aac583c9ef6eafca3f730ae + * # h=0x90578e247e98674e661013da3c5c1ca6a8c8f48c90b485c0dfa1494e23d56d72 + * output: 0x0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae + * # Act Three + * input: 0x00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba + * # decryptWithAD(0x908b166535c01a935cf1e130a5fe895ab4e6f3ef8855d87e9b7581c4ab663ddc, 0x000000000100000000000000, 0x90578e247e98674e661013da3c5c1ca6a8c8f48c90b485c0dfa1494e23d56d72, 0xb9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c3822) + * # rs=0x034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa + * # h=0x5dcb5ea9b4ccc755e0e3456af3990641276e1d5dc9afd82f974d90a47c918660 + * # ss=0xb36b6d195982c5be874d6d542dc268234379e1ae4ff1709402135b7de5cf0766 + * # HKDF(0xe89d31033a1b6bf68c07d22e08ea4d7884646c4b60a9528598ccb4ee2c8f56ba,0xb36b6d195982c5be874d6d542dc268234379e1ae4ff1709402135b7de5cf0766) + * # ck,temp_k3=0x919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01,0x981a46c820fb7a241bc8184ba4bb1f01bcdfafb00dde80098cb8c38db9141520 + * # decryptWithAD(0x981a46c820fb7a241bc8184ba4bb1f01bcdfafb00dde80098cb8c38db9141520, 0x000000000000000000000000, 0x5dcb5ea9b4ccc755e0e3456af3990641276e1d5dc9afd82f974d90a47c918660, 0x8dc68b1c466263b47fdf31e560e139ba) + * # HKDF(0x919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01,zero) + * output: rk,sk=0x969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9,0xbb9020b8965f4df047e07f955f3c4b88418984aadc5cdb35096b9ea8fa5c3442 + */ + +/* Here's what we expect: */ +static const char *expect_output[] = { + "0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae" +}; + +static const char *expect_input[] = { + "00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a", + "00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba" +}; + +static const char expect_sk[] = + "bb9020b8965f4df047e07f955f3c4b88418984aadc5cdb35096b9ea8fa5c3442"; +static const char expect_rk[] = + "969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9"; + +static struct io_plan *test_write(struct io_conn *conn, + const void *data, size_t len, + struct io_plan *(*next)(struct io_conn *, + struct handshake *), + struct handshake *h) +{ + static int upto; + char *got; + + assert(upto < ARRAY_SIZE(expect_output)); + got = tal_hexstr(NULL, data, len); + assert(streq(expect_output[upto], got)); + tal_free(got); + upto++; + + return next(conn, h); +} + +static struct io_plan *test_read(struct io_conn *conn, + void *data, size_t len, + struct io_plan *(*next)(struct io_conn *, + struct handshake *), + struct handshake *h) +{ + static int upto; + + assert(upto < ARRAY_SIZE(expect_input)); + if (!hex_decode(expect_input[upto], strlen(expect_input[upto]), + data, len)) + abort(); + upto++; + + return next(conn, h); +} + +static struct io_plan *success(struct io_conn *conn, + const struct pubkey *them, + const struct crypto_state *cs, + void *ctx) +{ + assert(secret_eq(&cs->sk, expect_sk)); + assert(secret_eq(&cs->rk, expect_rk)); + + /* No memory leaks please */ + secp256k1_context_destroy(secp256k1_ctx); + tal_free(ctx); + exit(0); +} + +bool hsm_do_ecdh(struct secret *ss, const struct pubkey *point) +{ + return secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, + ls_priv.secret.data) == 1; +} + +int main(void) +{ + tal_t *ctx = tal_tmpctx(NULL); + + trc = tal_tmpctx(ctx); + + secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY + | SECP256K1_CONTEXT_SIGN); + + + /* BOLT #8: + * + * name: transport-responder successful handshake + * ls.priv=2121212121212121212121212121212121212121212121212121212121212121 + * ls.pub=028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7 + * e.priv=0x2222222222222222222222222222222222222222222222222222222222222222 + * e.pub=0x02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27 + */ + ls_priv = privkey("2121212121212121212121212121212121212121212121212121212121212121"); + ls_pub = pubkey("028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7"); + e_priv = privkey("2222222222222222222222222222222222222222222222222222222222222222"); + e_pub = pubkey("02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27"); + + responder_handshake(ctx, &ls_pub, success, ctx); + /* Should not exit! */ + abort(); +}