commit 87ec074b41f316efabac3279dc8d2786cdcbc3ef
parent 3edf959aa0976e7dd209d71e9895fd524023f7f6
Author: William Casarin <jb55@jb55.com>
Date: Sat, 15 Jan 2022 07:44:43 -0800
got to ACT2!
Diffstat:
3 files changed, 204 insertions(+), 19 deletions(-)
diff --git a/Makefile b/Makefile
@@ -3,7 +3,7 @@ CFLAGS=-Wall -g -Og -Ideps/secp256k1/include -Ideps/libsodium/src/libsodium/incl
LDFLAGS=
ARS=deps/secp256k1/.libs/libsecp256k1.a deps/libsodium/src/libsodium/.libs/libsodium.a
-OBJS=sha256.o hkdf.o hmac.o sha512.o
+OBJS=sha256.o hkdf.o hmac.o sha512.o lnsocket.o
DEPS=$(OBJS) config.h
all: lnsocket
@@ -37,7 +37,7 @@ deps/libsodium/src/libsodium/.libs/libsodium.a: deps/libsodium/configure
cd deps/libsodium/src/libsodium; \
make -j2 libsodium.la
-lnsocket: lnsocket.c $(DEPS) $(ARS)
+lnsocket: $(DEPS) $(ARS)
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
tags: fake
diff --git a/handshake.h b/handshake.h
@@ -9,6 +9,12 @@
#include <secp256k1_extrakeys.h>
#define PUBKEY_CMPR_LEN 33
+#define ACT_ONE_SIZE 50
+#define ACT_TWO_SIZE 50
+
+struct node_id {
+ u8 k[PUBKEY_CMPR_LEN];
+};
enum bolt8_side {
INITIATOR,
diff --git a/lnsocket.c b/lnsocket.c
@@ -8,6 +8,7 @@
#include <string.h>
#include <assert.h>
#include <errno.h>
+#include <unistd.h>
#include <secp256k1.h>
#include <secp256k1_ecdh.h>
@@ -26,10 +27,59 @@ struct lnsocket {
const char *errors[8];
int socket;
int num_errors;
- secp256k1_context *secp_ctx;
+ secp256k1_context *secp;
};
+static bool char_to_hex(unsigned char *val, char c)
+{
+ if (c >= '0' && c <= '9') {
+ *val = c - '0';
+ return true;
+ }
+ if (c >= 'a' && c <= 'f') {
+ *val = c - 'a' + 10;
+ return true;
+ }
+ if (c >= 'A' && c <= 'F') {
+ *val = c - 'A' + 10;
+ return true;
+ }
+ return false;
+}
+
+static bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize)
+{
+ unsigned char v1, v2;
+ unsigned char *p = buf;
+
+ while (slen > 1) {
+ if (!char_to_hex(&v1, str[0]) || !char_to_hex(&v2, str[1]))
+ return false;
+ if (!bufsize)
+ return false;
+ *(p++) = (v1 << 4) | v2;
+ str += 2;
+ slen -= 2;
+ bufsize--;
+ }
+ return slen == 0 && bufsize == 0;
+}
+
+int parse_node_id(const char *str, struct node_id *dest)
+{
+ return hex_decode(str, strlen(str), dest->k, sizeof(*dest));
+}
+
+int pubkey_from_node_id(secp256k1_context *secp, struct pubkey *key,
+ const struct node_id *id)
+{
+ return secp256k1_ec_pubkey_parse(secp, &key->pubkey,
+ memcheck(id->k, sizeof(id->k)),
+ sizeof(id->k));
+}
+
+
static struct keypair generate_key(secp256k1_context *ctx)
{
struct keypair k;
@@ -56,7 +106,7 @@ static void lnsocket_init(struct lnsocket *lnsocket)
{
memset(lnsocket, 0, sizeof(*lnsocket));
- lnsocket->secp_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY |
+ lnsocket->secp = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY |
SECP256K1_CONTEXT_SIGN);
}
@@ -157,10 +207,105 @@ static void encrypt_ad(const struct secret *k, u64 nonce,
assert(clen == plaintext_len + crypto_aead_chacha20poly1305_ietf_ABYTES);
}
+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);
+}
+
+static void print_hex(u8 *bytes, int len) {
+ int i;
+ for (i = 0; i < len; ++i) {
+ printf("%02x", bytes[i]);
+ }
+}
+
+static void new_handshake(secp256k1_context *secp, struct handshake *handshake,
+ const struct pubkey *responder_id)
+{
+ /* BOLT #8:
+ *
+ * Before the start of Act One, 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));
+
+ /* 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 compressed format:
+ * * `h = SHA-256(h || rs.pub.serializeCompressed())`
+ *
+ * * The responding node mixes in their local static public key
+ * serialized in Bitcoin's compressed format:
+ * * `h = SHA-256(h || ls.pub.serializeCompressed())`
+ */
+ sha_mix_in_key(secp, &handshake->h, responder_id);
+}
+
+static void print_act_two(struct act_two *two)
+{
+ printf("ACT2 v %d pubkey ", two->v);
+ print_hex(two->pubkey, sizeof(two->pubkey));
+ printf("tag ");
+ print_hex(two->tag, sizeof(two->tag));
+ printf("\n");
+}
+
+static int act_two_initiator(struct lnsocket *ln, struct handshake *h)
+{
+ /* BOLT #8:
+ *
+ * 1. Read _exactly_ 50 bytes from the network buffer.
+ *
+ * 2. Parse the read message (`m`) into `v`, `re`, and `c`:
+ * * where `v` is the _first_ byte of `m`, `re` is the next 33
+ * bytes of `m`, and `c` is the last 16 bytes of `m`.
+ */
+ ssize_t size;
+
+ if ((size = read(ln->socket, &h->act2, ACT_TWO_SIZE)) != ACT_TWO_SIZE) {
+ printf("read %ld bytes, expected %d\n", size, ACT_TWO_SIZE);
+ push_error(ln, strerror(errno));
+ return 0;
+ }
+
+ print_act_two(&h->act2);
+ return 0;
+}
int act_one_initiator(struct lnsocket *ln, struct handshake *h)
{
- h->e = generate_key(ln->secp_ctx);
+ h->e = generate_key(ln->secp);
/* BOLT #8:
*
@@ -168,7 +313,7 @@ int act_one_initiator(struct lnsocket *ln, struct handshake *h)
* * The newly generated ephemeral key is accumulated into the
* running handshake digest.
*/
- sha_mix_in_key(ln->secp_ctx, &h->h, &h->e.pub);
+ sha_mix_in_key(ln->secp, &h->h, &h->e.pub);
/* BOLT #8:
*
@@ -176,7 +321,7 @@ int act_one_initiator(struct lnsocket *ln, struct handshake *h)
* * The initiator performs an ECDH between its newly generated ephemeral
* key and the remote node's static public key.
*/
- if (!secp256k1_ecdh(ln->secp_ctx, h->ss.data,
+ if (!secp256k1_ecdh(ln->secp, h->ss.data,
&h->their_id.pubkey, h->e.priv.secret.data,
NULL, NULL))
return handshake_failed(ln, h);
@@ -196,16 +341,49 @@ int act_one_initiator(struct lnsocket *ln, struct handshake *h)
encrypt_ad(&h->temp_k, 0, &h->h, sizeof(h->h), NULL, 0,
h->act1.tag, sizeof(h->act1.tag));
- return 1;
+ /* BOLT #8:
+ * 6. `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));
+
+ /* BOLT #8:
+ *
+ * 7. Send `m = 0 || e.pub.serializeCompressed() || c` to the responder over the network buffer.
+ */
+ h->act1.v = 0;
+ size_t len = sizeof(h->act1.pubkey);
+ secp256k1_ec_pubkey_serialize(ln->secp, h->act1.pubkey, &len,
+ &h->e.pub.pubkey,
+ SECP256K1_EC_COMPRESSED);
+
+ check_act_one(&h->act1);
+
+ if (write(ln->socket, &h->act1, ACT_ONE_SIZE) != ACT_ONE_SIZE)
+ return handshake_failed(ln, h);
+
+ return act_two_initiator(ln, h);
}
-int connect_node(struct lnsocket *ln, const struct pubkey pubkey, const char *host)
+int connect_node(struct lnsocket *ln, const char *node_id, const char *host)
{
int ret;
struct addrinfo *addrs = NULL;
struct handshake h;
-
- h.their_id = pubkey;
+ struct keypair my_id;
+ struct pubkey their_id;
+ struct node_id their_node_id;
+
+ if (!parse_node_id(node_id, &their_node_id)) {
+ push_error(ln, "failed to parse node id");
+ return 0;
+ }
+
+ if (!pubkey_from_node_id(ln->secp, &their_id, &their_node_id)) {
+ push_error(ln, "failed to convert node_id to pubkey");
+ return 0;
+ }
if ((ret = getaddrinfo(host, "9735", NULL, &addrs)) || !addrs) {
push_error(ln, gai_strerror(ret));
@@ -222,22 +400,23 @@ int connect_node(struct lnsocket *ln, const struct pubkey pubkey, const char *ho
return 0;
}
+ my_id = generate_key(ln->secp);
+ new_handshake(ln->secp, &h, &their_id);
+
+ h.side = INITIATOR;
+ h.my_id = my_id.pub;
+ h.their_id = their_id;
+
return act_one_initiator(ln, &h);
}
-static struct pubkey nodeid = {
- .pubkey = {
- .data = {
- 0x03, 0xf3, 0xc1, 0x08, 0xcc, 0xd5, 0x36, 0xb8, 0x52, 0x68, 0x41, 0xf0,
- 0xa5, 0xc5, 0x82, 0x12, 0xbb, 0x9e, 0x65, 0x84, 0xa1, 0xeb, 0x49, 0x30,
- 0x80, 0xe7, 0xc1, 0xcc, 0x34, 0xf8, 0x2d, 0xad, 0x71 } }
-};
-
int main(int argc, const char *argv[])
{
struct lnsocket ln;
lnsocket_init(&ln);
+ const char *nodeid = "03f3c108ccd536b8526841f0a5c58212bb9e6584a1eb493080e7c1cc34f82dad71";
+
if (!connect_node(&ln, nodeid, "24.84.152.187")) {
printf("connection failed: %s\n", ln.errors[0]);
return 1;