From 74f294e36c3ae9526a0d59d37325afc1d64f390a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 22 Jan 2016 06:41:48 +1030 Subject: [PATCH] daemon: encrypted communication (version 3) After useful feedback from Anthony Towns and Mats Jerratsch (of thunder.network fame), this is the third version of inter-node crypto. 1) First, each side sends a 33-byte session pubkey. This is a bitcoin-style compressed EC key, unique for each session. 2) ECDH is used to derive a shared secret. From this we generate the following transmission encoding parameters for each side: Session AES-128 key: SHA256(shared-secret || my-sessionpubkey || 0) Session HMAC key: SHA256(shared-secret || my-sessionpubkey || 1) IV for AES: SHA256(shared-secret || my-sessionpubkey || 2) 3) All packets from then on are encrypted of form: /* HMAC, covering totlen and data */ struct sha256 hmac; /* Total data transmitted (including this). */ le64 totlen; /* Encrypted contents, rounded up to 16 byte boundary. */ u8 data[]; 4) The first packet is an Authenticate protobuf, containing this node's pubkey, and a bitcoin-style EC signature of the other side's session pubkey. 5) Unknown protobuf fields are handled in the protocol as follows (including in the initial Authenticate packet): 1) Odd numbered fields are optional, and backwards compatible. 2) Even numbered fields are required; abort if you get one. Currently both sides just send an error packet "hello" after the handshake, and make sure they receive the same. Signed-off-by: Rusty Russell --- daemon/Makefile | 2 + daemon/cryptopkt.c | 509 +++++++++++++++++++++++++++++++++++++++++++++ daemon/cryptopkt.h | 26 +++ daemon/peer.c | 36 +++- daemon/peer.h | 7 + lightning.pb-c.c | 118 ++++++++++- lightning.pb-c.h | 49 +++++ lightning.proto | 10 + 8 files changed, 749 insertions(+), 8 deletions(-) create mode 100644 daemon/cryptopkt.c create mode 100644 daemon/cryptopkt.h diff --git a/daemon/Makefile b/daemon/Makefile index 699cdc9a7..dfdcc0f19 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -14,6 +14,7 @@ DAEMON_LIB_SRC := \ DAEMON_LIB_OBJS := $(DAEMON_LIB_SRC:.c=.o) DAEMON_SRC := \ + daemon/cryptopkt.c \ daemon/dns.c \ daemon/jsonrpc.c \ daemon/lightningd.c \ @@ -31,6 +32,7 @@ DAEMON_JSMN_HEADERS := daemon/jsmn/jsmn.h DAEMON_HEADERS := \ daemon/configdir.h \ + daemon/cryptopkt.h \ daemon/dns.h \ daemon/json.h \ daemon/jsonrpc.h \ diff --git a/daemon/cryptopkt.c b/daemon/cryptopkt.c new file mode 100644 index 000000000..ac2c1f4f6 --- /dev/null +++ b/daemon/cryptopkt.c @@ -0,0 +1,509 @@ +#include "bitcoin/shadouble.h" +#include "bitcoin/signature.h" +#include "cryptopkt.h" +#include "lightning.pb-c.h" +#include "lightningd.h" +#include "log.h" +#include "peer.h" +#include "protobuf_convert.h" +#include "secrets.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_PKT_LEN (1024 * 1024) + +#define ROUNDUP(x,a) (((x) + ((a)-1)) & ~((a)-1)) + +struct crypto_pkt { + /* HMAC */ + struct sha256 hmac; + /* Total length transmitted. */ + le64 totlen; + /* ... contents... */ + u8 data[]; +}; + +/* Temporary structure for negotiation (peer->io_data->neg) */ +struct key_negotiate { + /* Our session secret key. */ + u8 seckey[32]; + + /* Our pubkey, their pubkey. */ + u8 our_sessionpubkey[33], their_sessionpubkey[33]; + + /* Callback once it's all done. */ + struct io_plan *(*cb)(struct io_conn *, struct peer *); +}; + +#define ENCKEY_SEED 0 +#define HMACKEY_SEED 1 +#define IV_SEED 2 + +struct enckey { + struct sha256 k; +}; + +struct hmackey { + struct sha256 k; +}; + +struct iv { + unsigned char iv[AES_BLOCK_SIZE]; +}; + +static void sha_with_seed(const unsigned char secret[32], + const unsigned char serial_pubkey[33], + unsigned char seed, + struct sha256 *res) +{ + struct sha256_ctx ctx; + + sha256_init(&ctx); + sha256_update(&ctx, memcheck(secret, 32), 32); + sha256_update(&ctx, memcheck(serial_pubkey, 33), 33); + sha256_u8(&ctx, seed); + sha256_done(&ctx, res); +} + +static struct enckey enckey_from_secret(const unsigned char secret[32], + const unsigned char serial_pubkey[33]) +{ + struct enckey enckey; + sha_with_seed(secret, serial_pubkey, ENCKEY_SEED, &enckey.k); + return enckey; +} + +static struct hmackey hmackey_from_secret(const unsigned char secret[32], + const unsigned char serial_pubkey[33]) +{ + struct hmackey hmackey; + sha_with_seed(secret, serial_pubkey, HMACKEY_SEED, &hmackey.k); + return hmackey; +} + +static struct iv iv_from_secret(const unsigned char secret[32], + const unsigned char serial_pubkey[33]) +{ + struct sha256 sha; + struct iv iv; + + sha_with_seed(secret, serial_pubkey, IV_SEED, &sha); + memcpy(iv.iv, sha.u.u8, sizeof(iv.iv)); + return iv; +} + +struct dir_state { + u64 totlen; + struct hmackey hmackey; + EVP_CIPHER_CTX evpctx; + + /* Current packet. */ + struct crypto_pkt *cpkt; +}; + +static bool setup_crypto(struct dir_state *dir, + u8 shared_secret[32], u8 serial_pubkey[33]) +{ + struct iv iv; + struct enckey enckey; + + dir->totlen = 0; + dir->hmackey = hmackey_from_secret(shared_secret, serial_pubkey); + dir->cpkt = NULL; + + iv = iv_from_secret(shared_secret, serial_pubkey); + enckey = enckey_from_secret(shared_secret, serial_pubkey); + + return EVP_EncryptInit(&dir->evpctx, EVP_aes_128_ctr(), + memcheck(enckey.k.u.u8, sizeof(enckey.k)), + memcheck(iv.iv, sizeof(iv.iv))) == 1; +} + +struct io_data { + /* Stuff we need to keep around to talk to peer. */ + struct dir_state in, out; + + /* Header we're currently reading. */ + size_t len_in; + struct crypto_pkt hdr_in; + + /* For negotiation phase. */ + struct key_negotiate *neg; +}; + +static void *proto_tal_alloc(void *allocator_data, size_t size) +{ + return tal_arr(allocator_data, char, size); +} + +static void proto_tal_free(void *allocator_data, void *pointer) +{ + tal_free(pointer); +} + +static Pkt *decrypt_pkt(struct peer *peer, struct crypto_pkt *cpkt, + size_t data_len) +{ + size_t full_len; + struct sha256 hmac; + int outlen; + struct io_data *iod = peer->io_data; + struct ProtobufCAllocator prototal; + Pkt *ret; + + full_len = ROUNDUP(data_len, AES_BLOCK_SIZE); + + HMAC(EVP_sha256(), iod->in.hmackey.k.u.u8, sizeof(iod->in.hmackey), + (unsigned char *)&cpkt->totlen, sizeof(cpkt->totlen) + full_len, + hmac.u.u8, NULL); + + if (CRYPTO_memcmp(&hmac, &cpkt->hmac, sizeof(hmac)) != 0) { + log_unusual(peer->log, "Packet has bad HMAC"); + return NULL; + } + + /* FIXME: Assumes we can decrypt in place! */ + EVP_DecryptUpdate(&iod->in.evpctx, cpkt->data, &outlen, + memcheck(cpkt->data, full_len), full_len); + assert(outlen == full_len); + + /* De-protobuf it. */ + prototal.alloc = proto_tal_alloc; + prototal.free = proto_tal_free; + prototal.allocator_data = tal(iod, char); + + ret = pkt__unpack(&prototal, data_len, cpkt->data); + if (!ret) + tal_free(prototal.allocator_data); + else + /* Make sure packet owns contents */ + tal_steal(ret, prototal.allocator_data); + return ret; +} + +static struct crypto_pkt *encrypt_pkt(struct peer *peer, + const Pkt *pkt, + size_t *total_len) +{ + static unsigned char zeroes[AES_BLOCK_SIZE-1]; + struct crypto_pkt *cpkt; + unsigned char *dout; + size_t len, full_len; + int outlen; + struct io_data *iod = peer->io_data; + + len = pkt__get_packed_size(pkt); + full_len = ROUNDUP(len, AES_BLOCK_SIZE); + *total_len = sizeof(*cpkt) + full_len; + + cpkt = (struct crypto_pkt *)tal_arr(peer, char, *total_len); + iod->out.totlen += len; + cpkt->totlen = cpu_to_le64(iod->out.totlen); + + dout = cpkt->data; + /* FIXME: Assumes we can encrypt in place! */ + pkt__pack(pkt, dout); + EVP_EncryptUpdate(&iod->out.evpctx, dout, &outlen, + memcheck(dout, len), len); + dout += outlen; + + /* Now encrypt tail, padding with zeroes if necessary. */ + EVP_EncryptUpdate(&iod->out.evpctx, dout, &outlen, zeroes, + full_len - len); + assert(dout + outlen == cpkt->data + full_len); + + HMAC(EVP_sha256(), iod->out.hmackey.k.u.u8, sizeof(iod->out.hmackey), + (unsigned char *)&cpkt->totlen, sizeof(cpkt->totlen) + full_len, + cpkt->hmac.u.u8, NULL); + + return cpkt; +} + +static int do_read_packet(int fd, struct io_plan_arg *arg) +{ + struct peer *peer = arg->u1.vp; + struct io_data *iod = peer->io_data; + u64 max; + size_t data_off, data_len; + int ret; + + /* Still reading header? */ + if (iod->len_in < sizeof(iod->hdr_in)) { + ret = read(fd, (char *)&iod->hdr_in + iod->len_in, + sizeof(iod->hdr_in) - iod->len_in); + if (ret <= 0) + return -1; + iod->len_in += ret; + /* We don't ever send empty packets, so don't check for + * that here. */ + return 0; + } + + max = ROUNDUP(le64_to_cpu(iod->hdr_in.totlen) - iod->in.totlen, + AES_BLOCK_SIZE); + + if (iod->len_in == sizeof(iod->hdr_in)) { + /* FIXME: Handle re-xmit. */ + if (le64_to_cpu(iod->hdr_in.totlen) < iod->in.totlen) { + log_unusual(peer->log, + "Packet went backwards: %"PRIu64 + " -> %"PRIu64, + iod->in.totlen, + le64_to_cpu(iod->hdr_in.totlen)); + return -1; + } + if (le64_to_cpu(iod->hdr_in.totlen) + > iod->in.totlen + MAX_PKT_LEN) { + log_unusual(peer->log, + "Packet overlength: %"PRIu64" -> %"PRIu64, + iod->in.totlen, + le64_to_cpu(iod->hdr_in.totlen)); + return -1; + } + iod->in.cpkt = (struct crypto_pkt *) + tal_arr(iod, u8, sizeof(struct crypto_pkt) + max); + memcpy(iod->in.cpkt, &iod->hdr_in, sizeof(iod->hdr_in)); + } + + data_off = iod->len_in - sizeof(struct crypto_pkt); + ret = read(fd, iod->in.cpkt->data + data_off, max - data_off); + if (ret <= 0) + return -1; + + iod->len_in += ret; + if (iod->len_in <= max) + return 0; + + /* Can't overflow len arg: packet can't be more than MAX_PKT_LEN */ + data_len = le64_to_cpu(iod->hdr_in.totlen) - iod->in.totlen; + peer->inpkt = decrypt_pkt(peer, iod->in.cpkt, data_len); + iod->in.cpkt = tal_free(iod->in.cpkt); + + if (!peer->inpkt) + return -1; + iod->in.totlen += data_len; + return 1; +} + +struct io_plan *peer_read_packet(struct io_conn *conn, + struct peer *peer, + struct io_plan *(*cb)(struct io_conn *, + struct peer *)) +{ + struct io_plan_arg *arg = io_plan_arg(conn, IO_IN); + + peer->io_data->len_in = 0; + arg->u1.vp = peer; + return io_set_plan(conn, IO_IN, do_read_packet, + (struct io_plan *(*)(struct io_conn *, void *))cb, + peer); +} + +/* Caller must free data! */ +struct io_plan *peer_write_packet(struct io_conn *conn, + struct peer *peer, + const Pkt *pkt, + struct io_plan *(*next)(struct io_conn *, + struct peer *)) +{ + struct io_data *iod = peer->io_data; + size_t totlen; + + /* We free previous packet here, rather than doing indirection + * via io_write */ + tal_free(iod->out.cpkt); + iod->out.cpkt = encrypt_pkt(peer, pkt, &totlen); + return io_write(conn, iod->out.cpkt, totlen, next, peer); +} + +static void *pkt_unwrap(struct peer *peer, Pkt__PktCase which) +{ + size_t i; + const ProtobufCMessage *base; + + if (peer->inpkt->pkt_case != which) { + log_unusual(peer->log, "Expected %u, got %u", + which, peer->inpkt->pkt_case); + return NULL; + } + + /* It's a union, and each member starts with base. Pick one */ + base = &peer->inpkt->error->base; + + /* Look for unknown fields. Remember, "It's OK to be odd!" */ + for (i = 0; i < base->n_unknown_fields; i++) { + log_debug(peer->log, "Unknown field in %u: %u", + which, base->unknown_fields[i].tag); + /* Odd is OK */ + if (base->unknown_fields[i].tag & 1) + continue; + log_unusual(peer->log, "Unknown field %u in %u", + base->unknown_fields[i].tag, which); + return NULL; + } + return peer->inpkt->error; +} + +static struct io_plan *check_proof(struct io_conn *conn, struct peer *peer) +{ + struct key_negotiate *neg = peer->io_data->neg; + struct sha256_double sha; + struct signature sig; + struct io_plan *(*cb)(struct io_conn *, struct peer *); + struct pubkey id; + Authenticate *auth; + + auth = pkt_unwrap(peer, PKT__PKT_AUTH); + if (!auth) + return io_close(conn); + + if (!proto_to_signature(auth->session_sig, &sig)) { + log_unusual(peer->log, "Invalid auth signature"); + return io_close(conn); + } + + if (!proto_to_pubkey(peer->state->secpctx, auth->node_id, &id)) { + log_unusual(peer->log, "Invalid auth id"); + return io_close(conn); + } + + /* Signature covers *our* session key. */ + sha256_double(&sha, + neg->our_sessionpubkey, sizeof(neg->our_sessionpubkey)); + + if (!check_signed_hash(peer->state->secpctx, &sha, &sig, &id)) { + log_unusual(peer->log, "Bad auth signature"); + return io_close(conn); + } + + tal_free(auth); + + /* All complete, return to caller. */ + cb = neg->cb; + peer->io_data->neg = tal_free(neg); + return cb(conn, peer); +} + +static struct io_plan *receive_proof(struct io_conn *conn, struct peer *peer) +{ + return peer_read_packet(conn, peer, check_proof); +} + +/* Steals w onto the returned Pkt */ +static Pkt *pkt_wrap(const tal_t *ctx, void *w, Pkt__PktCase pkt_case) +{ + Pkt *pkt = tal(ctx, Pkt); + pkt__init(pkt); + pkt->pkt_case = pkt_case; + /* Union, so any will do */ + pkt->error = tal_steal(pkt, w); + return pkt; +} + +static Pkt *authenticate_pkt(const tal_t *ctx, + const struct pubkey *node_id, + const struct signature *sig) +{ + Authenticate *auth = tal(ctx, Authenticate); + authenticate__init(auth); + auth->node_id = pubkey_to_proto(auth, node_id); + auth->session_sig = signature_to_proto(auth, sig); + return pkt_wrap(ctx, auth, PKT__PKT_AUTH); +} + +static struct io_plan *keys_exchanged(struct io_conn *conn, struct peer *peer) +{ + u8 shared_secret[32]; + struct pubkey sessionkey; + struct signature sig; + struct key_negotiate *neg = peer->io_data->neg; + Pkt *auth; + + if (!pubkey_from_der(peer->state->secpctx, + neg->their_sessionpubkey, + sizeof(neg->their_sessionpubkey), + &sessionkey)) { + /* FIXME: Dump key in this case. */ + log_unusual(peer->log, "Bad sessionkey"); + return io_close(conn); + } + + /* Derive shared secret. */ + if (!secp256k1_ecdh(peer->state->secpctx, shared_secret, + &sessionkey.pubkey, neg->seckey)) { + log_unusual(peer->log, "Bad ECDH"); + return io_close(conn); + } + + /* Each side combines with their OWN session key to SENDING crypto. */ + if (!setup_crypto(&peer->io_data->in, shared_secret, + neg->their_sessionpubkey) + || !setup_crypto(&peer->io_data->out, shared_secret, + neg->our_sessionpubkey)) { + log_unusual(peer->log, "Failed setup_crypto()"); + return io_close(conn); + } + + /* Now sign their session key to prove who we are. */ + privkey_sign(peer, neg->their_sessionpubkey, + sizeof(neg->their_sessionpubkey), &sig); + + /* FIXME: Free auth afterwards. */ + auth = authenticate_pkt(peer, &peer->state->id, &sig); + return peer_write_packet(conn, peer, auth, receive_proof); +} + +static struct io_plan *session_key_receive(struct io_conn *conn, + struct peer *peer) +{ + struct key_negotiate *neg = peer->io_data->neg; + /* Now read their key. */ + return io_read(conn, neg->their_sessionpubkey, + sizeof(neg->their_sessionpubkey), keys_exchanged, peer); +} + +static void gen_sessionkey(secp256k1_context *ctx, + u8 seckey[32], + secp256k1_pubkey *pubkey) +{ + do { + if (RAND_bytes(seckey, 32) != 1) + fatal("Could not get random bytes for sessionkey"); + } while (!secp256k1_ec_pubkey_create(ctx, pubkey, seckey)); +} + +struct io_plan *peer_crypto_setup(struct io_conn *conn, struct peer *peer, + struct io_plan *(*cb)(struct io_conn *, + struct peer *)) +{ + size_t outputlen; + secp256k1_pubkey sessionkey; + struct key_negotiate *neg; + + peer->io_data = tal(peer, struct io_data); + + /* We store negotiation state here. */ + neg = peer->io_data->neg = tal(peer->io_data, struct key_negotiate); + neg->cb = cb; + + gen_sessionkey(peer->state->secpctx, neg->seckey, &sessionkey); + + secp256k1_ec_pubkey_serialize(peer->state->secpctx, + neg->our_sessionpubkey, &outputlen, + &sessionkey, + SECP256K1_EC_COMPRESSED); + assert(outputlen == sizeof(neg->our_sessionpubkey)); + return io_write(conn, neg->our_sessionpubkey, outputlen, + session_key_receive, peer); +} diff --git a/daemon/cryptopkt.h b/daemon/cryptopkt.h new file mode 100644 index 000000000..06c61672d --- /dev/null +++ b/daemon/cryptopkt.h @@ -0,0 +1,26 @@ +#ifndef LIGHTNING_DAEMON_CRYPTOPKT_H +#define LIGHTNING_DAEMON_CRYPTOPKT_H +#include "config.h" +#include "lightning.pb-c.h" +#include + +struct peer; + +struct io_plan *peer_crypto_setup(struct io_conn *conn, + struct peer *peer, + struct io_plan *(*cb)(struct io_conn *, + struct peer *)); + +/* Reads packet into peer->inpkt/peer->inpkt_len */ +struct io_plan *peer_read_packet(struct io_conn *conn, + struct peer *peer, + struct io_plan *(*cb)(struct io_conn *, + struct peer *)); + +struct io_plan *peer_write_packet(struct io_conn *conn, + struct peer *peer, + const Pkt *pkt, + struct io_plan *(*next)(struct io_conn *, + struct peer *)); + +#endif /* LIGHTNING_DAEMON_CRYPTOPKT_H */ diff --git a/daemon/peer.c b/daemon/peer.c index 5f578eecd..ed4fcda8d 100644 --- a/daemon/peer.c +++ b/daemon/peer.c @@ -1,3 +1,4 @@ +#include "cryptopkt.h" #include "dns.h" #include "jsonrpc.h" #include "lightningd.h" @@ -14,6 +15,33 @@ #include #include +/* Send and receive (encrypted) hello message. */ +static struct io_plan *peer_test_check(struct io_conn *conn, struct peer *peer) +{ + if (peer->inpkt->pkt_case != PKT__PKT_ERROR) + fatal("Bad packet type %u", peer->inpkt->pkt_case); + if (!peer->inpkt->error->problem + || strcmp(peer->inpkt->error->problem, "hello") != 0) + fatal("Bad packet '%.6s'", peer->inpkt->error->problem); + log_info(peer->log, "Successful hello!"); + return io_close(conn); +} + +static struct io_plan *peer_test_read(struct io_conn *conn, struct peer *peer) +{ + return peer_read_packet(conn, peer, peer_test_check); +} + +static struct io_plan *peer_test(struct io_conn *conn, struct peer *peer) +{ + Error err = ERROR__INIT; + Pkt pkt = PKT__INIT; + pkt.pkt_case = PKT__PKT_ERROR; + pkt.error = &err; + err.problem = "hello"; + return peer_write_packet(conn, peer, &pkt, peer_test_read); +} + static void destroy_peer(struct peer *peer) { list_del_from(&peer->state->peers, &peer->list); @@ -32,6 +60,7 @@ static struct peer *new_peer(struct lightningd_state *state, peer->state = state; peer->addr.type = addr_type; peer->addr.protocol = addr_protocol; + peer->io_data = NULL; /* FIXME: Attach IO logging for this peer. */ tal_add_destructor(peer, destroy_peer); @@ -63,7 +92,7 @@ struct io_plan *peer_connected_out(struct io_conn *conn, return io_close(conn); } log_info(peer->log, "Connected out to %s:%s", name, port); - return io_write(conn, "Hello!", 6, io_close_cb, NULL); + return peer_crypto_setup(conn, peer, peer_test); } static struct io_plan *peer_connected_in(struct io_conn *conn, @@ -73,8 +102,9 @@ static struct io_plan *peer_connected_in(struct io_conn *conn, "in"); if (!peer) return io_close(conn); - - return io_write(conn, "Hello!", 6, io_close_cb, NULL); + + log_info(peer->log, "Peer connected in"); + return peer_crypto_setup(conn, peer, peer_test); } static int make_listen_fd(struct lightningd_state *state, diff --git a/daemon/peer.h b/daemon/peer.h index 50cea69a1..c39f943af 100644 --- a/daemon/peer.h +++ b/daemon/peer.h @@ -1,6 +1,7 @@ #ifndef LIGHTNING_DAEMON_PEER_H #define LIGHTNING_DAEMON_PEER_H #include "config.h" +#include "lightning.pb-c.h" #include "netaddr.h" #include @@ -14,6 +15,12 @@ struct peer { /* The other end's address. */ struct netaddr addr; + /* Current received packet. */ + Pkt *inpkt; + + /* Current ongoing packetflow */ + struct io_data *io_data; + /* What happened. */ struct log *log; }; diff --git a/lightning.pb-c.c b/lightning.pb-c.c index 5dde94f91..62777aa9f 100644 --- a/lightning.pb-c.c +++ b/lightning.pb-c.c @@ -222,6 +222,49 @@ void funding__free_unpacked assert(message->base.descriptor == &funding__descriptor); protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); } +void authenticate__init + (Authenticate *message) +{ + static Authenticate init_value = AUTHENTICATE__INIT; + *message = init_value; +} +size_t authenticate__get_packed_size + (const Authenticate *message) +{ + assert(message->base.descriptor == &authenticate__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t authenticate__pack + (const Authenticate *message, + uint8_t *out) +{ + assert(message->base.descriptor == &authenticate__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t authenticate__pack_to_buffer + (const Authenticate *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &authenticate__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Authenticate * + authenticate__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Authenticate *) + protobuf_c_message_unpack (&authenticate__descriptor, + allocator, len, data); +} +void authenticate__free_unpacked + (Authenticate *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &authenticate__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} void open_channel__init (OpenChannel *message) { @@ -1344,6 +1387,57 @@ const ProtobufCMessageDescriptor funding__descriptor = (ProtobufCMessageInit) funding__init, NULL,NULL,NULL /* reserved[123] */ }; +static const ProtobufCFieldDescriptor authenticate__field_descriptors[2] = +{ + { + "node_id", + 1, + PROTOBUF_C_LABEL_REQUIRED, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Authenticate, node_id), + &bitcoin_pubkey__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "session_sig", + 2, + PROTOBUF_C_LABEL_REQUIRED, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Authenticate, session_sig), + &signature__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned authenticate__field_indices_by_name[] = { + 0, /* field[0] = node_id */ + 1, /* field[1] = session_sig */ +}; +static const ProtobufCIntRange authenticate__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor authenticate__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "authenticate", + "Authenticate", + "Authenticate", + "", + sizeof(Authenticate), + 2, + authenticate__field_descriptors, + authenticate__field_indices_by_name, + 1, authenticate__number_ranges, + (ProtobufCMessageInit) authenticate__init, + NULL,NULL,NULL /* reserved[123] */ +}; static const ProtobufCEnumValue open_channel__anchor_offer__enum_values_by_number[2] = { { "WILL_CREATE_ANCHOR", "OPEN_CHANNEL__ANCHOR_OFFER__WILL_CREATE_ANCHOR", 1 }, @@ -2259,7 +2353,7 @@ const ProtobufCMessageDescriptor error__descriptor = (ProtobufCMessageInit) error__init, NULL,NULL,NULL /* reserved[123] */ }; -static const ProtobufCFieldDescriptor pkt__field_descriptors[17] = +static const ProtobufCFieldDescriptor pkt__field_descriptors[18] = { { "update", @@ -2465,8 +2559,21 @@ static const ProtobufCFieldDescriptor pkt__field_descriptors[17] = 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 0,NULL,NULL /* reserved1,reserved2, etc */ }, + { + "auth", + 50, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(Pkt, pkt_case), + offsetof(Pkt, auth), + &authenticate__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, }; static const unsigned pkt__field_indices_by_name[] = { + 17, /* field[17] = auth */ 13, /* field[13] = close */ 15, /* field[15] = close_ack */ 14, /* field[14] = close_complete */ @@ -2485,13 +2592,14 @@ static const unsigned pkt__field_indices_by_name[] = { 3, /* field[3] = update_signature */ 7, /* field[7] = update_timedout_htlc */ }; -static const ProtobufCIntRange pkt__number_ranges[4 + 1] = +static const ProtobufCIntRange pkt__number_ranges[5 + 1] = { { 1, 0 }, { 20, 9 }, { 30, 13 }, { 40, 16 }, - { 0, 17 } + { 50, 17 }, + { 0, 18 } }; const ProtobufCMessageDescriptor pkt__descriptor = { @@ -2501,10 +2609,10 @@ const ProtobufCMessageDescriptor pkt__descriptor = "Pkt", "", sizeof(Pkt), - 17, + 18, pkt__field_descriptors, pkt__field_indices_by_name, - 4, pkt__number_ranges, + 5, pkt__number_ranges, (ProtobufCMessageInit) pkt__init, NULL,NULL,NULL /* reserved[123] */ }; diff --git a/lightning.pb-c.h b/lightning.pb-c.h index 26219ccc3..4178a1e59 100644 --- a/lightning.pb-c.h +++ b/lightning.pb-c.h @@ -20,6 +20,7 @@ typedef struct _Signature Signature; typedef struct _Locktime Locktime; typedef struct _BitcoinPubkey BitcoinPubkey; typedef struct _Funding Funding; +typedef struct _Authenticate Authenticate; typedef struct _OpenChannel OpenChannel; typedef struct _OpenAnchor OpenAnchor; typedef struct _OpenCommitSig OpenCommitSig; @@ -147,6 +148,26 @@ struct _Funding , 0,0ll, 0,0 } +/* + * Set channel params. + */ +struct _Authenticate +{ + ProtobufCMessage base; + /* + * Which node this is. + */ + BitcoinPubkey *node_id; + /* + * Signature of your session key. * + */ + Signature *session_sig; +}; +#define AUTHENTICATE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&authenticate__descriptor) \ + , NULL, NULL } + + /* * Set channel params. */ @@ -500,6 +521,7 @@ struct _Error typedef enum { PKT__PKT__NOT_SET = 0, + PKT__PKT_AUTH = 50, PKT__PKT_OPEN = 20, PKT__PKT_OPEN_ANCHOR = 21, PKT__PKT_OPEN_COMMIT_SIG = 22, @@ -527,6 +549,10 @@ struct _Pkt ProtobufCMessage base; Pkt__PktCase pkt_case; union { + /* + * Start of connection + */ + Authenticate *auth; /* * Opening */ @@ -658,6 +684,25 @@ Funding * void funding__free_unpacked (Funding *message, ProtobufCAllocator *allocator); +/* Authenticate methods */ +void authenticate__init + (Authenticate *message); +size_t authenticate__get_packed_size + (const Authenticate *message); +size_t authenticate__pack + (const Authenticate *message, + uint8_t *out); +size_t authenticate__pack_to_buffer + (const Authenticate *message, + ProtobufCBuffer *buffer); +Authenticate * + authenticate__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void authenticate__free_unpacked + (Authenticate *message, + ProtobufCAllocator *allocator); /* OpenChannel methods */ void open_channel__init (OpenChannel *message); @@ -1017,6 +1062,9 @@ typedef void (*BitcoinPubkey_Closure) typedef void (*Funding_Closure) (const Funding *message, void *closure_data); +typedef void (*Authenticate_Closure) + (const Authenticate *message, + void *closure_data); typedef void (*OpenChannel_Closure) (const OpenChannel *message, void *closure_data); @@ -1082,6 +1130,7 @@ extern const ProtobufCMessageDescriptor signature__descriptor; extern const ProtobufCMessageDescriptor locktime__descriptor; extern const ProtobufCMessageDescriptor bitcoin_pubkey__descriptor; extern const ProtobufCMessageDescriptor funding__descriptor; +extern const ProtobufCMessageDescriptor authenticate__descriptor; extern const ProtobufCMessageDescriptor open_channel__descriptor; extern const ProtobufCEnumDescriptor open_channel__anchor_offer__descriptor; extern const ProtobufCMessageDescriptor open_anchor__descriptor; diff --git a/lightning.proto b/lightning.proto index 5f0eb03bc..0c1fcddf6 100644 --- a/lightning.proto +++ b/lightning.proto @@ -51,6 +51,14 @@ message funding { // Packet Types // +// Set channel params. +message authenticate { + // Which node this is. + required bitcoin_pubkey node_id = 1; + // Signature of your session key. */ + required signature session_sig = 2; +}; + // Set channel params. message open_channel { // Relative locktime for outputs going to us. @@ -205,6 +213,8 @@ message error { // This is the union which defines all of them message pkt { oneof pkt { + // Start of connection + authenticate auth = 50; // Opening open_channel open = 20; open_anchor open_anchor = 21;