1212#include <ccan/io/fdpass/fdpass.h>
1313#include <ccan/noerr/noerr.h>
1414#include <ccan/read_write_all/read_write_all.h>
15+ #include <ccan/tal/grab_file/grab_file.h>
1516#include <ccan/tal/str/str.h>
1617#include <common/daemon_conn.h>
17- #include <common/hsm_encryption .h>
18+ #include <common/hsm_secret .h>
1819#include <common/memleak.h>
1920#include <common/status.h>
2021#include <common/status_wiregen.h>
2122#include <common/subdaemon.h>
2223#include <errno.h>
2324#include <fcntl.h>
25+ #include <stdarg.h>
2426/*~ _wiregen files are autogenerated by tools/generate-wire.py */
2527#include <hsmd/libhsmd.h>
2628#include <hsmd/permissions.h>
2729#include <sys/socket.h>
2830#include <sys/stat.h>
31+ #include <wally_bip39.h>
2932#include <wire/wire_io.h>
3033
3134/*~ Each subdaemon is started with stdin connected to lightningd (for status
3538#define REQ_FD 3
3639
3740/* Temporary storage for the secret until we pass it to `hsmd_init` */
38- struct secret hsm_secret ;
41+ struct hsm_secret hsm_secret ;
3942
4043/*~ We keep track of clients, but there's not much to keep. */
4144struct client {
@@ -270,66 +273,138 @@ static struct io_plan *req_reply(struct io_conn *conn,
270273 return io_write_wire (conn , msg_out , client_read_next , c );
271274}
272275
273- /*~ This encrypts the content of the `struct secret hsm_secret` and
274- * stores it in hsm_secret, this is called instead of create_hsm() if
275- * `lightningd` is started with --encrypted-hsm.
276- */
277- static void create_encrypted_hsm (int fd , const struct secret * encryption_key )
276+ /* Send an init reply failure message to lightningd and then call status_failed */
277+ static void hsmd_send_init_reply_failure (enum hsm_secret_error error_code , enum status_failreason reason , const char * error_msg , ...)
278278{
279- struct encrypted_hsm_secret cipher ;
280-
281- if (!encrypt_hsm_secret (encryption_key , & hsm_secret ,
282- & cipher ))
283- status_failed (STATUS_FAIL_INTERNAL_ERROR ,
284- "Encrypting hsm_secret" );
285- if (!write_all (fd , cipher .data , ENCRYPTED_HSM_SECRET_LEN )) {
286- unlink_noerr ("hsm_secret" );
287- status_failed (STATUS_FAIL_INTERNAL_ERROR ,
288- "Writing encrypted hsm_secret: %s" , strerror (errno ));
279+ u8 * msg ;
280+ va_list ap ;
281+ char * formatted_msg ;
282+
283+ va_start (ap , error_msg );
284+ formatted_msg = tal_vfmt (tmpctx , error_msg , ap );
285+ va_end (ap );
286+
287+ /* Send the init reply failure first */
288+ msg = towire_hsmd_init_reply_failure (NULL , error_code , formatted_msg );
289+ if (msg ) {
290+ /* Send directly to lightningd via REQ_FD */
291+ write_all (REQ_FD , msg , tal_bytelen (msg ));
292+ tal_free (msg );
289293 }
294+
295+ /* Then call status_failed with the error message */
296+ status_failed (reason , "%s" , formatted_msg );
290297}
291298
292- static void create_hsm (int fd )
299+ static void create_hsm (int fd , const char * passphrase )
293300{
294- /*~ ccan/read_write_all has a more convenient return than write() where
295- * we'd have to check the return value == the length we gave: write()
296- * can return short on normal files if we run out of disk space. */
297- if (!write_all (fd , & hsm_secret , sizeof (hsm_secret ))) {
298- /* ccan/noerr contains useful routines like this, which don't
299- * clobber errno, so we can use it in our error report. */
301+ u8 * hsm_secret_data ;
302+ size_t hsm_secret_len ;
303+ int ret ;
304+ /* Always create a mnemonic-based hsm_secret */
305+ u8 entropy [BIP39_ENTROPY_LEN_128 ];
306+ char * mnemonic = NULL ;
307+ struct sha256 seed_hash ;
308+
309+ status_debug ("HSM: Starting create_hsm with passphrase=%s" , passphrase ? "provided" : "none" );
310+
311+ /* Initialize wally tal context for libwally operations */
312+
313+ status_debug ("HSM: Initialized wally tal context" );
314+
315+ /* Generate random entropy for new mnemonic */
316+ randombytes_buf (entropy , sizeof (entropy ));
317+ status_debug ("HSM: Generated random entropy" );
318+
319+
320+ /* Generate mnemonic from entropy */
321+ tal_wally_start ();
322+ ret = bip39_mnemonic_from_bytes (NULL , entropy , sizeof (entropy ), & mnemonic );
323+ tal_wally_end (tmpctx );
324+
325+ if (ret != WALLY_OK ) {
326+ unlink_noerr ("hsm_secret" );
327+ hsmd_send_init_reply_failure (HSM_SECRET_ERR_SEED_DERIVATION_FAILED , STATUS_FAIL_INTERNAL_ERROR ,
328+ "Failed to generate mnemonic from entropy" );
329+ }
330+ status_debug ("HSM: Generated mnemonic from entropy" );
331+
332+ if (!mnemonic ) {
333+ unlink_noerr ("hsm_secret" );
334+ hsmd_send_init_reply_failure (HSM_SECRET_ERR_SEED_DERIVATION_FAILED , STATUS_FAIL_INTERNAL_ERROR ,
335+ "Failed to get generated mnemonic" );
336+ }
337+
338+ /* Derive seed hash from mnemonic + passphrase (or zero if no passphrase) */
339+ if (!derive_seed_hash (mnemonic , passphrase , & seed_hash )) {
340+ unlink_noerr ("hsm_secret" );
341+ hsmd_send_init_reply_failure (HSM_SECRET_ERR_SEED_DERIVATION_FAILED , STATUS_FAIL_INTERNAL_ERROR ,
342+ "Failed to derive seed hash from mnemonic" );
343+ }
344+ status_debug ("HSM: Derived seed hash from mnemonic" );
345+
346+ /* Create hsm_secret format: seed_hash (32 bytes) + mnemonic */
347+ hsm_secret_len = PASSPHRASE_HASH_LEN + strlen (mnemonic );
348+ hsm_secret_data = tal_arr (tmpctx , u8 , hsm_secret_len );
349+
350+ /* Copy seed hash first */
351+ memcpy (hsm_secret_data , & seed_hash , PASSPHRASE_HASH_LEN );
352+ /* Copy mnemonic after seed hash */
353+ memcpy (hsm_secret_data + PASSPHRASE_HASH_LEN , mnemonic , strlen (mnemonic ));
354+ status_debug ("HSM: Created hsm_secret data structure" );
355+
356+ /* Derive the actual secret from mnemonic + passphrase for our global hsm_secret */
357+ u8 bip32_seed [BIP39_SEED_LEN_512 ];
358+ size_t bip32_seed_len ;
359+
360+ tal_wally_start ();
361+ ret = bip39_mnemonic_to_seed (mnemonic , passphrase , bip32_seed , sizeof (bip32_seed ), & bip32_seed_len );
362+ tal_wally_end (tmpctx );
363+ if (ret != WALLY_OK ) {
364+ unlink_noerr ("hsm_secret" );
365+ hsmd_send_init_reply_failure (HSM_SECRET_ERR_SEED_DERIVATION_FAILED , STATUS_FAIL_INTERNAL_ERROR ,
366+ "Failed to derive seed from mnemonic" );
367+ }
368+ status_debug ("HSM: Derived BIP32 seed from mnemonic" );
369+
370+ /* Use first 32 bytes for hsm_secret */
371+ memcpy (& hsm_secret .secret , bip32_seed , sizeof (hsm_secret .secret ));
372+
373+ /* Write the hsm_secret data to file */
374+ if (!write_all (fd , hsm_secret_data , hsm_secret_len )) {
300375 unlink_noerr ("hsm_secret" );
301376 status_failed (STATUS_FAIL_INTERNAL_ERROR ,
302377 "writing: %s" , strerror (errno ));
303378 }
379+ status_debug ("HSM: Successfully wrote hsm_secret to file" );
304380}
305381
306382/*~ We store our root secret in a "hsm_secret" file (like all of Core Lightning,
307- * we run in the user's .lightning directory). */
308- static void maybe_create_new_hsm (const struct secret * encryption_key ,
309- bool random_hsm )
383+ * we run in the user's .lightning directory).
384+ *
385+ * NOTE: This function no longer creates encrypted 32-byte secrets. New hsm_secret
386+ * files will use mnemonic format with passphrases.
387+ */
388+ static void maybe_create_new_hsm (const char * passphrase )
310389{
311390 /*~ Note that this is opened for write-only, even though the permissions
312391 * are set to read-only. That's perfectly valid! */
313392 int fd = open ("hsm_secret" , O_CREAT |O_EXCL |O_WRONLY , 0400 );
314393 if (fd < 0 ) {
315394 /* If this is not the first time we've run, it will exist. */
316- if (errno == EEXIST )
395+ if (errno == EEXIST ) {
396+ status_debug ("HSM: hsm_secret file already exists, skipping creation" );
317397 return ;
398+ }
318399 status_failed (STATUS_FAIL_INTERNAL_ERROR ,
319400 "creating: %s" , strerror (errno ));
320401 }
321402
322- /*~ This is libsodium's cryptographic randomness routine: we assume
323- * it's doing a good job. */
324- if (random_hsm )
325- randombytes_buf (& hsm_secret , sizeof (hsm_secret ));
326-
327- /*~ If an encryption_key was provided, store an encrypted seed. */
328- if (encryption_key )
329- create_encrypted_hsm (fd , encryption_key );
330- /*~ Otherwise store the seed in clear.. */
331- else
332- create_hsm (fd );
403+ status_debug ("HSM: Creating new hsm_secret file" );
404+
405+ /*~ Store the seed in clear. New hsm_secret files will use mnemonic format
406+ * with passphrases, not encrypted 32-byte secrets. */
407+ create_hsm (fd , passphrase );
333408 /*~ fsync (mostly!) ensures that the file has reached the disk. */
334409 if (fsync (fd ) != 0 ) {
335410 unlink_noerr ("hsm_secret" );
@@ -367,62 +442,36 @@ static void maybe_create_new_hsm(const struct secret *encryption_key,
367442/*~ We always load the HSM file, even if we just created it above. This
368443 * both unifies the code paths, and provides a nice sanity check that the
369444 * file contents are as they will be for future invocations. */
370- static void load_hsm (const struct secret * encryption_key )
445+ static void load_hsm (const char * passphrase )
371446{
372- struct stat st ;
373- int fd = open ("hsm_secret" , O_RDONLY );
374- if (fd < 0 )
375- status_failed (STATUS_FAIL_INTERNAL_ERROR ,
376- "opening: %s" , strerror (errno ));
377- if (stat ("hsm_secret" , & st ) != 0 )
378- status_failed (STATUS_FAIL_INTERNAL_ERROR ,
379- "stating: %s" , strerror (errno ));
380-
381- /* If the seed is stored in clear. */
382- if (st .st_size == 32 ) {
383- if (!read_all (fd , & hsm_secret , sizeof (hsm_secret )))
384- status_failed (STATUS_FAIL_INTERNAL_ERROR ,
385- "reading: %s" , strerror (errno ));
386- /* If an encryption key was passed with a not yet encrypted hsm_secret,
387- * remove the old one and create an encrypted one. */
388- if (encryption_key ) {
389- if (close (fd ) != 0 )
390- status_failed (STATUS_FAIL_INTERNAL_ERROR ,
391- "closing: %s" , strerror (errno ));
392- if (remove ("hsm_secret" ) != 0 )
393- status_failed (STATUS_FAIL_INTERNAL_ERROR ,
394- "removing clear hsm_secret: %s" , strerror (errno ));
395- maybe_create_new_hsm (encryption_key , false);
396- fd = open ("hsm_secret" , O_RDONLY );
397- if (fd < 0 )
398- status_failed (STATUS_FAIL_INTERNAL_ERROR ,
399- "opening: %s" , strerror (errno ));
400- }
447+ u8 * hsm_secret_contents ;
448+ struct hsm_secret * hsms ;
449+ enum hsm_secret_error err ;
450+
451+ /* Read the hsm_secret file */
452+ hsm_secret_contents = grab_file (tmpctx , "hsm_secret" );
453+ if (!hsm_secret_contents ) {
454+ hsmd_send_init_reply_failure (HSM_SECRET_ERR_INVALID_FORMAT , STATUS_FAIL_INTERNAL_ERROR ,
455+ "Could not read hsm_secret: %s" , strerror (errno ));
401456 }
402- /* If an encryption key was passed and the `hsm_secret` is stored
403- * encrypted, recover the seed from the cipher. */
404- else if (st .st_size == ENCRYPTED_HSM_SECRET_LEN ) {
405- struct encrypted_hsm_secret encrypted_secret ;
406457
407- /* hsm_control must have checked it! */
408- assert ( encryption_key );
409-
410- if (! read_all ( fd , encrypted_secret . data , ENCRYPTED_HSM_SECRET_LEN ))
411- status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
412- "Reading encrypted hsm_secret: %s" , strerror ( errno ));
413- if (! decrypt_hsm_secret ( encryption_key , & encrypted_secret ,
414- & hsm_secret )) {
415- /* Exit but don't throw a backtrace when the user made a mistake in typing
416- * its password. Instead exit and `lightningd` will be able to give
417- * an error message. */
418- exit ( 1 );
419- }
458+ /* Remove the NUL terminator that grab_file adds */
459+ tal_resize ( & hsm_secret_contents , tal_bytelen ( hsm_secret_contents ) - 1 );
460+
461+ /* Extract the secret using the new hsm_secret module */
462+ tal_wally_start ();
463+ hsms = extract_hsm_secret ( tmpctx , hsm_secret_contents ,
464+ tal_bytelen ( hsm_secret_contents ) ,
465+ passphrase , & err );
466+ tal_wally_end ( tmpctx );
467+ if (! hsms ) {
468+ status_debug ( "HSM: Failed to load hsm_secret: %s" , hsm_secret_error_str ( err ));
469+ hsmd_send_init_reply_failure ( err , STATUS_FAIL_INTERNAL_ERROR ,
470+ "Failed to load hsm_secret: %s" , hsm_secret_error_str ( err ));
420471 }
421- else
422- status_failed (STATUS_FAIL_INTERNAL_ERROR , "Invalid hsm_secret, "
423- "no plaintext nor encrypted"
424- " seed." );
425- close (fd );
472+
473+ /* Copy the extracted secret to our global hsm_secret */
474+ memcpy (& hsm_secret , & hsms -> secret , sizeof (hsm_secret ));
426475}
427476
428477/*~ We have a pre-init call in developer mode, to set dev flags */
@@ -458,6 +507,7 @@ static struct io_plan *init_hsm(struct io_conn *conn,
458507 const u8 * msg_in )
459508{
460509 struct secret * hsm_encryption_key ;
510+ const char * hsm_passphrase ;
461511 struct bip32_key_version bip32_key_version ;
462512 u32 minversion , maxversion ;
463513 const u32 our_minversion = 4 , our_maxversion = 6 ;
@@ -494,27 +544,13 @@ static struct io_plan *init_hsm(struct io_conn *conn,
494544 * think this is some kind of obscure CLN hazing ritual? Anyway, the
495545 * passphrase needs to be *appended* to the mnemonic, so the HSM needs
496546 * the raw passphrase. To avoid a compatibility break, I put it inside
497- * the TLV, and left the old "hsm_encryption_key" field in place, even
498- * though we override it here for the old-style non-BIP39 hsm_secret. */
499- if (tlvs -> hsm_passphrase ) {
500- const char * hsm_passphrase = (const char * )tlvs -> hsm_passphrase ;
501- const char * err_msg ;
502-
503- hsm_encryption_key = tal (NULL , struct secret );
504- if (hsm_secret_encryption_key_with_exitcode (hsm_passphrase , hsm_encryption_key , & err_msg ) != 0 )
505- return bad_req_fmt (conn , c , msg_in ,
506- "Bad passphrase: %s" , err_msg );
507- }
508- tal_free (tlvs );
547+ * the TLV, and left the old "hsm_encryption_key" field in place (and lightningd
548+ * never sets that anymore), and we use the TLV instead. */
549+ if (tlvs -> hsm_passphrase )
550+ hsm_passphrase = (const char * )tlvs -> hsm_passphrase ;
509551
510- /*~ The memory is actually copied in towire(), so lock the `hsm_secret`
511- * encryption key (new) memory again here. */
512- if (hsm_encryption_key && sodium_mlock (hsm_encryption_key ,
513- sizeof (hsm_encryption_key )) != 0 )
514- status_failed (STATUS_FAIL_INTERNAL_ERROR ,
515- "Could not lock memory for hsm_secret encryption key." );
516552 /*~ Don't swap this. */
517- sodium_mlock (hsm_secret .data , sizeof (hsm_secret .data ));
553+ sodium_mlock (hsm_secret .secret . data , sizeof (hsm_secret . secret .data ));
518554
519555 if (!developer ) {
520556 assert (!dev_force_privkey );
@@ -526,16 +562,15 @@ static struct io_plan *init_hsm(struct io_conn *conn,
526562 /* Once we have read the init message we know which params the master
527563 * will use */
528564 c -> chainparams = chainparams ;
529- maybe_create_new_hsm (hsm_encryption_key , true);
530- load_hsm (hsm_encryption_key );
531-
532- /*~ We don't need the hsm_secret encryption key anymore. */
533- if (hsm_encryption_key )
534- discard_key (take (hsm_encryption_key ));
565+ maybe_create_new_hsm (hsm_passphrase );
566+ load_hsm (hsm_passphrase );
535567
536568 /* Define the minimum common max version for the hsmd one */
537569 hsmd_mutual_version = maxversion < our_maxversion ? maxversion : our_maxversion ;
538- return req_reply (conn , c , hsmd_init (hsm_secret , hsmd_mutual_version ,
570+
571+ /* This was tallocated off NULL, and memleak complains if we don't free it */
572+ tal_free (tlvs );
573+ return req_reply (conn , c , hsmd_init (hsm_secret .secret , hsmd_mutual_version ,
539574 bip32_key_version ));
540575}
541576
@@ -756,6 +791,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c)
756791 case WIRE_HSMD_SIGN_WITHDRAWAL_REPLY :
757792 case WIRE_HSMD_SIGN_INVOICE_REPLY :
758793 case WIRE_HSMD_INIT_REPLY_V4 :
794+ case WIRE_HSMD_INIT_REPLY_FAILURE :
759795 case WIRE_HSMD_DERIVE_SECRET_REPLY :
760796 case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST :
761797 case WIRE_HSMD_SIGN_COMMITMENT_TX_REPLY :
0 commit comments