diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 56f22836e..6c47c94ad 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -461,6 +461,7 @@ static struct io_plan *init_hsm(struct io_conn *conn, struct bip32_key_version bip32_key_version; u32 minversion, maxversion; const u32 our_minversion = 4, our_maxversion = 6; + struct tlv_hsmd_init_tlvs *tlvs; /* This must be lightningd. */ assert(is_lightningd(c)); @@ -475,7 +476,7 @@ static struct io_plan *init_hsm(struct io_conn *conn, &dev_force_bip32_seed, &dev_force_channel_secrets, &dev_force_channel_secrets_shaseed, - &minversion, &maxversion)) + &minversion, &maxversion, &tlvs)) return bad_req(conn, c, msg_in); /*~ Usually we don't worry about API breakage between internal daemons, @@ -487,6 +488,25 @@ static struct io_plan *init_hsm(struct io_conn *conn, minversion, maxversion, our_minversion, our_maxversion); + /*~ We used to have lightningd hand us the encryption key derived from + * the passphrase which was used to encrypt the `hsm_secret` file. Then + * Rusty gave me the thankless task of introducing BIP-39 mnemonics. I + * think this is some kind of obscure CLN hazing ritual? Anyway, the + * passphrase needs to be *appended* to the mnemonic, so the HSM needs + * the raw passphrase. To avoid a compatibility break, I put it inside + * the TLV, and left the old "hsm_encryption_key" field in place, even + * though we override it here for the old-style non-BIP39 hsm_secret. */ + if (tlvs->hsm_passphrase) { + const char *hsm_passphrase = (const char *)tlvs->hsm_passphrase; + const char *err_msg; + + hsm_encryption_key = tal(NULL, struct secret); + if (hsm_secret_encryption_key_with_exitcode(hsm_passphrase, hsm_encryption_key, &err_msg) != 0) + return bad_req_fmt(conn, c, msg_in, + "Bad passphrase: %s", err_msg); + } + tal_free(tlvs); + /*~ The memory is actually copied in towire(), so lock the `hsm_secret` * encryption key (new) memory again here. */ if (hsm_encryption_key && sodium_mlock(hsm_encryption_key, diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index 4f4a40d1e..4746ac57f 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -29,6 +29,9 @@ msgdata,hsmd_init,dev_force_channel_secrets,?secrets, msgdata,hsmd_init,dev_force_channel_secrets_shaseed,?sha256, msgdata,hsmd_init,hsm_wire_min_version,u32, msgdata,hsmd_init,hsm_wire_max_version,u32, +msgdata,hsmd_init,tlvs,hsmd_init_tlvs, +tlvtype,hsmd_init_tlvs,hsm_passphrase,1 +tlvdata,hsmd_init_tlvs,hsm_passphrase,passphrase,wirestring, #include # Sorry: I should have put version in v2 :( diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index 599ef4081..61eedee48 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -98,7 +98,7 @@ struct ext_key *hsm_init(struct lightningd *ld) /* If hsm_secret is encrypted and the --encrypted-hsm startup option is * not passed, don't let hsmd use the first 32 bytes of the cypher as the * actual secret. */ - if (!ld->config.keypass) { + if (!ld->hsm_passphrase) { if (is_hsm_secret_encrypted("hsm_secret") == 1) errx(EXITCODE_HSM_ERROR_IS_ENCRYPT, "hsm_secret is encrypted, you need to pass the " "--encrypted-hsm startup option."); @@ -122,16 +122,24 @@ struct ext_key *hsm_init(struct lightningd *ld) err(EXITCODE_HSM_GENERIC_ERROR, "Writing preinit msg to hsm"); } + /* Create TLV for passphrase if needed */ + struct tlv_hsmd_init_tlvs *tlv = NULL; + if (ld->hsm_passphrase) { + tlv = tlv_hsmd_init_tlvs_new(tmpctx); + tlv->hsm_passphrase = ld->hsm_passphrase; + } + if (!wire_sync_write(ld->hsm_fd, towire_hsmd_init(tmpctx, &chainparams->bip32_key_version, chainparams, - ld->config.keypass, + NULL, ld->dev_force_privkey, ld->dev_force_bip32_seed, ld->dev_force_channel_secrets, ld->dev_force_channel_secrets_shaseed, HSM_MIN_VERSION, - HSM_MAX_VERSION))) + HSM_MAX_VERSION, + tlv))) err(EXITCODE_HSM_GENERIC_ERROR, "Writing init msg to hsm"); bip32_base = tal(ld, struct ext_key); @@ -143,7 +151,7 @@ struct ext_key *hsm_init(struct lightningd *ld) &unused)) { /* nothing to do. */ } else { - if (ld->config.keypass) + if (ld->hsm_passphrase) errx(EXITCODE_HSM_BAD_PASSWORD, "Wrong password for encrypted hsm_secret."); errx(EXITCODE_HSM_GENERIC_ERROR, "HSM did not give init reply"); } diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 2ce30d3f1..5de7a12ca 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -309,10 +309,10 @@ static struct lightningd *new_lightningd(const tal_t *ctx) /*~ This is set when a JSON RPC command comes in to shut us down. */ ld->stop_conn = NULL; - /*~ This is used to signal that `hsm_secret` is encrypted, and will - * be set to `true` if the `--encrypted-hsm` option is passed at startup. + /*~ This is used to store the passphrase for hsm_secret if needed. + * It will be set if the `--hsm-passphrase` option is passed at startup. */ - ld->encrypted_hsm = false; + ld->hsm_passphrase = NULL; /* This is used to override subdaemons */ strmap_init(&ld->alt_subdaemons); @@ -1305,11 +1305,6 @@ int main(int argc, char *argv[]) /*~ This is the ccan/io central poll override from above. */ io_poll_override(io_poll_lightningd); - /*~ If hsm_secret is encrypted, we don't need its encryption key - * anymore. Note that sodium_munlock() also zeroes the memory.*/ - if (ld->config.keypass) - discard_key(take(ld->config.keypass)); - /*~ Our default color and alias are derived from our node id, so we * can only set those now (if not set by config options). */ setup_color_and_alias(ld); diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index ce8b62b01..b87c973c3 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -69,8 +69,6 @@ struct config { /* Minimal amount of effective funding_satoshis for accepting channels */ u64 min_capacity_sat; - /* This is the key we use to encrypt `hsm_secret`. */ - struct secret *keypass; /* How long before we give up waiting for INIT msg */ u32 connection_timeout_secs; @@ -387,7 +385,9 @@ struct lightningd { char *old_bookkeeper_dir; char *old_bookkeeper_db; - bool encrypted_hsm; + /* HSM passphrase for any format that needs it */ + char *hsm_passphrase; + /* What (additional) messages the HSM accepts */ u32 *hsm_capabilities; diff --git a/lightningd/options.c b/lightningd/options.c index 1ef5d1fa2..f2364de9e 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -597,13 +597,7 @@ static char *opt_set_hsm_password(struct lightningd *ld) } prompt(ld, ""); - ld->config.keypass = tal(NULL, struct secret); - - opt_exitcode = hsm_secret_encryption_key_with_exitcode(passwd, ld->config.keypass, &err_msg); - if (opt_exitcode > 0) - return cast_const(char *, err_msg); - - ld->encrypted_hsm = true; + ld->hsm_passphrase = tal_strdup(ld, passwd); free(passwd); return NULL; diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index c616710a8..2fac66644 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -641,6 +641,9 @@ void tell_connectd_peer_importance(struct peer *peer UNNEEDED, /* Generated stub for tlv_hsmd_dev_preinit_tlvs_new */ struct tlv_hsmd_dev_preinit_tlvs *tlv_hsmd_dev_preinit_tlvs_new(const tal_t *ctx UNNEEDED) { fprintf(stderr, "tlv_hsmd_dev_preinit_tlvs_new called!\n"); abort(); } +/* Generated stub for tlv_hsmd_init_tlvs_new */ +struct tlv_hsmd_init_tlvs *tlv_hsmd_init_tlvs_new(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "tlv_hsmd_init_tlvs_new called!\n"); abort(); } /* Generated stub for topology_add_sync_waiter_ */ void topology_add_sync_waiter_(const tal_t *ctx UNNEEDED, struct chain_topology *topo UNNEEDED, @@ -712,7 +715,7 @@ u8 *towire_hsmd_forget_channel(const tal_t *ctx UNNEEDED, const struct node_id * u8 *towire_hsmd_get_output_scriptpubkey(const tal_t *ctx UNNEEDED, u64 channel_id UNNEEDED, const struct node_id *peer_id UNNEEDED, const struct pubkey *commitment_point UNNEEDED) { fprintf(stderr, "towire_hsmd_get_output_scriptpubkey called!\n"); abort(); } /* Generated stub for towire_hsmd_init */ -u8 *towire_hsmd_init(const tal_t *ctx UNNEEDED, const struct bip32_key_version *bip32_key_version UNNEEDED, const struct chainparams *chainparams UNNEEDED, const struct secret *hsm_encryption_key UNNEEDED, const struct privkey *dev_force_privkey UNNEEDED, const struct secret *dev_force_bip32_seed UNNEEDED, const struct secrets *dev_force_channel_secrets UNNEEDED, const struct sha256 *dev_force_channel_secrets_shaseed UNNEEDED, u32 hsm_wire_min_version UNNEEDED, u32 hsm_wire_max_version UNNEEDED) +u8 *towire_hsmd_init(const tal_t *ctx UNNEEDED, const struct bip32_key_version *bip32_key_version UNNEEDED, const struct chainparams *chainparams UNNEEDED, const struct secret *hsm_encryption_key UNNEEDED, const struct privkey *dev_force_privkey UNNEEDED, const struct secret *dev_force_bip32_seed UNNEEDED, const struct secrets *dev_force_channel_secrets UNNEEDED, const struct sha256 *dev_force_channel_secrets_shaseed UNNEEDED, u32 hsm_wire_min_version UNNEEDED, u32 hsm_wire_max_version UNNEEDED, const struct tlv_hsmd_init_tlvs *tlvs UNNEEDED) { fprintf(stderr, "towire_hsmd_init called!\n"); abort(); } /* Generated stub for towire_hsmd_new_channel */ u8 *towire_hsmd_new_channel(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 dbid UNNEEDED)