diff --git a/lightningd/Makefile b/lightningd/Makefile index 1bbe7b086..986819c06 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -107,7 +107,7 @@ check-makefile: check-lightningd-makefile check-lightningd-makefile: @for f in lightningd/*.h lightningd/*/*.h; do if ! echo $(LIGHTNINGD_HEADERS_NOGEN) $(LIGHTNINGD_HEADERS_GEN) "" | grep -q "$$f "; then echo $$f not mentioned in LIGHTNINGD_HEADERS_NOGEN or LIGHTNINGD_HEADERS_GEN >&2; exit 1; fi; done -lightningd/lightningd: $(LIGHTNINGD_OBJS) $(LIGHTNINGD_OLD_OBJS) $(LIGHTNINGD_OLD_LIB_OBJS) $(LIGHTNINGD_LIB_OBJS) $(LIGHTNINGD_JSMN_OBJS) $(CORE_OBJS) $(CORE_TX_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(CCAN_OBJS) $(LIGHTNINGD_HSM_CONTROL_OBJS) $(LIGHTNINGD_HANDSHAKE_CONTROL_OBJS) $(LIGHTNINGD_GOSSIP_CONTROL_OBJS) $(LIBBASE58_OBJS) libsecp256k1.a libsodium.a +lightningd/lightningd: $(LIGHTNINGD_OBJS) $(LIGHTNINGD_OLD_OBJS) $(LIGHTNINGD_OLD_LIB_OBJS) $(LIGHTNINGD_LIB_OBJS) $(LIGHTNINGD_JSMN_OBJS) $(CORE_OBJS) $(CORE_TX_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(CCAN_OBJS) $(LIGHTNINGD_HSM_CONTROL_OBJS) $(LIGHTNINGD_HANDSHAKE_CONTROL_OBJS) $(LIGHTNINGD_GOSSIP_CONTROL_OBJS) $(LIBBASE58_OBJS) libsecp256k1.a libsodium.a libwallycore.a clean: lightningd-clean diff --git a/lightningd/hsm/Makefile b/lightningd/hsm/Makefile index d0a8b4d78..e0584b581 100644 --- a/lightningd/hsm/Makefile +++ b/lightningd/hsm/Makefile @@ -37,9 +37,11 @@ $(LIGHTNINGD_HSM_OBJS) $(LIGHTNINGD_HSM_CLIENT_OBJS): $(LIGHTNINGD_HEADERS) $(LIGHTNINGD_HSM_CONTROL_OBJS) : $(LIGHTNINGD_HSM_CONTROL_HEADERS) +$(LIGHTNINGD_HSM_OBJS): $(CORE_TX_HEADERS) + lightningd/hsm-all: lightningd/lightningd_hsm $(LIGHTNINGD_HSM_CLIENT_OBJS) -lightningd/lightningd_hsm: $(LIGHTNINGD_HSM_OBJS) $(CORE_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(CCAN_OBJS) $(LIBBASE58_OBJS) libsecp256k1.a libsodium.a libwallycore.a +lightningd/lightningd_hsm: $(LIGHTNINGD_HSM_OBJS) $(CORE_OBJS) $(CORE_TX_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(CCAN_OBJS) $(LIBBASE58_OBJS) lightningd/utxo.o libsecp256k1.a libsodium.a libwallycore.a $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) lightningd/hsm/gen_hsm_client_wire.h: $(WIRE_GEN) lightningd/hsm/hsm_client_wire_csv diff --git a/lightningd/hsm/hsm.c b/lightningd/hsm/hsm.c index 11fc8b594..67d803581 100644 --- a/lightningd/hsm/hsm.c +++ b/lightningd/hsm/hsm.c @@ -135,14 +135,25 @@ static u8 *handle_ecdh(struct client *c, const void *data) static u8 *init_response(struct conn_info *control) { struct pubkey node_id; + u8 *serialized_extkey = tal_arr(control, u8, BIP32_SERIALIZED_LEN), *msg; + node_key(NULL, &node_id); - return towire_hsmctl_init_response(control, &node_id); + if (bip32_key_serialize(&secretstuff.bip32, BIP32_FLAG_KEY_PUBLIC, + serialized_extkey, tal_len(serialized_extkey)) + != WALLY_OK) + status_failed(WIRE_HSMSTATUS_KEY_FAILED, + "Can't serialize bip32 public key"); + + msg = towire_hsmctl_init_response(control, &node_id, serialized_extkey); + tal_free(serialized_extkey); + return msg; } static void populate_secretstuff(void) { u8 bip32_seed[BIP32_ENTROPY_LEN_256]; u32 salt = 0; + struct ext_key master_extkey, child_extkey; /* Fill in the BIP32 tree for bitcoin addresses. */ do { @@ -154,7 +165,79 @@ static void populate_secretstuff(void) salt++; } while (bip32_key_from_seed(bip32_seed, sizeof(bip32_seed), BIP32_VER_TEST_PRIVATE, - 0, &secretstuff.bip32) != WALLY_OK); + 0, &master_extkey) != WALLY_OK); + + /* BIP 32: + * + * The default wallet layout + * + * An HDW is organized as several 'accounts'. Accounts are numbered, + * the default account ("") being number 0. Clients are not required + * to support more than one account - if not, they only use the + * default account. + * + * Each account is composed of two keypair chains: an internal and an + * external one. The external keychain is used to generate new public + * addresses, while the internal keychain is used for all other + * operations (change addresses, generation addresses, ..., anything + * that doesn't need to be communicated). Clients that do not support + * separate keychains for these should use the external one for + * everything. + * + * - m/iH/0/k corresponds to the k'th keypair of the external chain of account number i of the HDW derived from master m. + */ + /* Hence child 0, then child 0 again to get extkey to derive from. */ + if (bip32_key_from_parent(&master_extkey, 0, BIP32_FLAG_KEY_PRIVATE, + &child_extkey) != WALLY_OK) + status_failed(WIRE_HSMSTATUS_KEY_FAILED, + "Can't derive child bip32 key"); + + if (bip32_key_from_parent(&child_extkey, 0, BIP32_FLAG_KEY_PRIVATE, + &secretstuff.bip32) != WALLY_OK) + status_failed(WIRE_HSMSTATUS_KEY_FAILED, + "Can't derive private bip32 key"); +} + +static inline void bitcoin_pubkey(struct pubkey *pubkey, u32 index) +{ + struct ext_key ext; + + if (index >= BIP32_INITIAL_HARDENED_CHILD) + status_failed(WIRE_HSMSTATUS_KEY_FAILED, + "Index %u too great", index); + + if (bip32_key_from_parent(&secretstuff.bip32, index, + BIP32_FLAG_KEY_PUBLIC, &ext) != WALLY_OK) + status_failed(WIRE_HSMSTATUS_KEY_FAILED, + "BIP32 of %u failed", index); + + if (!secp256k1_ec_pubkey_parse(secp256k1_ctx, &pubkey->pubkey, + ext.pub_key, sizeof(ext.pub_key))) + status_failed(WIRE_HSMSTATUS_KEY_FAILED, + "Parse of BIP32 child %u pubkey failed", index); +} + +static inline void bitcoin_keypair(struct privkey *privkey, + struct pubkey *pubkey, + u32 index) +{ + struct ext_key ext; + + if (index >= BIP32_INITIAL_HARDENED_CHILD) + status_failed(WIRE_HSMSTATUS_KEY_FAILED, + "Index %u too great", index); + + if (bip32_key_from_parent(&secretstuff.bip32, index, + BIP32_FLAG_KEY_PRIVATE, &ext) != WALLY_OK) + status_failed(WIRE_HSMSTATUS_KEY_FAILED, + "BIP32 of %u failed", index); + + /* libwally says: The private key with prefix byte 0 */ + memcpy(privkey->secret, ext.priv_key+1, 32); + if (!secp256k1_ec_pubkey_create(secp256k1_ctx, &pubkey->pubkey, + privkey->secret)) + status_failed(WIRE_HSMSTATUS_KEY_FAILED, + "BIP32 pubkey %u create failed", index); } static u8 *create_new_hsm(struct conn_info *control) diff --git a/lightningd/hsm/hsm_control_wire_csv b/lightningd/hsm/hsm_control_wire_csv index e51d6b3de..33137e2c8 100644 --- a/lightningd/hsm/hsm_control_wire_csv +++ b/lightningd/hsm/hsm_control_wire_csv @@ -4,6 +4,8 @@ hsmctl_init_load,2 hsmctl_init_response,100 hsmctl_init_response,0,node_id,33 +hsmctl_init_response,33,bip32_len,2 +hsmctl_init_response,35,bip32_seed,bip32_len*1,u8 # ECDH returns an fd. hsmctl_hsmfd_ecdh,3 diff --git a/lightningd/hsm/hsm_status_wire_csv b/lightningd/hsm/hsm_status_wire_csv index 807b5bb75..9636af65f 100644 --- a/lightningd/hsm/hsm_status_wire_csv +++ b/lightningd/hsm/hsm_status_wire_csv @@ -3,6 +3,7 @@ hsmstatus_init_failed,0x8000 hsmstatus_writemsg_failed,0x8001 hsmstatus_bad_request,0x8002 hsmstatus_fd_failed,0x8003 +hsmstatus_key_failed,0x8004 # Clients should not give a bad request but not the HSM's decision to crash. hsmstatus_client_bad_request,1 diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index da756fbca..f18b2ab48 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -9,14 +9,23 @@ #include #include #include +#include static void hsm_init_done(struct subdaemon *hsm, const u8 *msg, struct lightningd *ld) { - if (!fromwire_hsmctl_init_response(msg, NULL, &ld->dstate.id)) + u8 *serialized_extkey; + + if (!fromwire_hsmctl_init_response(hsm, msg, NULL, &ld->dstate.id, + &serialized_extkey)) errx(1, "HSM did not give init response"); log_info_struct(ld->log, "Our ID: %s", struct pubkey, &ld->dstate.id); + ld->bip32_base = tal(ld, struct ext_key); + if (bip32_key_unserialize(serialized_extkey, tal_len(serialized_extkey), + ld->bip32_base) != WALLY_OK) + errx(1, "HSM did not give unserializable BIP32 extkey"); + io_break(ld->hsm); } @@ -57,6 +66,7 @@ static enum subdaemon_status hsm_status(struct subdaemon *hsm, const u8 *msg, case WIRE_HSMSTATUS_WRITEMSG_FAILED: case WIRE_HSMSTATUS_BAD_REQUEST: case WIRE_HSMSTATUS_FD_FAILED: + case WIRE_HSMSTATUS_KEY_FAILED: break; } return STATUS_COMPLETE; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 410d36dab..a2ea7b031 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -30,6 +30,9 @@ struct lightningd { /* All peers we're tracking. */ struct list_head peers; + + /* Public base for bip32 keys. */ + struct ext_key *bip32_base; }; /* FIXME */