12
12
#include <ccan/io/fdpass/fdpass.h>
13
13
#include <ccan/noerr/noerr.h>
14
14
#include <ccan/read_write_all/read_write_all.h>
15
+ #include <ccan/tal/grab_file/grab_file.h>
15
16
#include <ccan/tal/str/str.h>
16
17
#include <common/daemon_conn.h>
17
- #include <common/hsm_encryption .h>
18
+ #include <common/hsm_secret .h>
18
19
#include <common/memleak.h>
19
20
#include <common/status.h>
20
21
#include <common/status_wiregen.h>
26
27
#include <hsmd/permissions.h>
27
28
#include <sys/socket.h>
28
29
#include <sys/stat.h>
30
+ #include <wally_bip39.h>
29
31
#include <wire/wire_io.h>
30
32
31
33
/*~ Each subdaemon is started with stdin connected to lightningd (for status
35
37
#define REQ_FD 3
36
38
37
39
/* Temporary storage for the secret until we pass it to `hsmd_init` */
38
- struct secret hsm_secret ;
40
+ struct hsm_secret hsm_secret ;
39
41
40
42
/*~ We keep track of clients, but there's not much to keep. */
41
43
struct client {
@@ -270,66 +272,113 @@ static struct io_plan *req_reply(struct io_conn *conn,
270
272
return io_write_wire (conn , msg_out , client_read_next , c );
271
273
}
272
274
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 )
275
+ static void create_hsm (int fd , const char * passphrase )
278
276
{
279
- struct encrypted_hsm_secret cipher ;
280
-
281
- if (!encrypt_hsm_secret (encryption_key , & hsm_secret ,
282
- & cipher ))
277
+ u8 * hsm_secret_data ;
278
+ size_t hsm_secret_len ;
279
+ int ret ;
280
+ /* Always create a mnemonic-based hsm_secret */
281
+ u8 entropy [BIP39_ENTROPY_LEN_128 ];
282
+ char * mnemonic = NULL ;
283
+ struct sha256 seed_hash ;
284
+
285
+ status_debug ("HSM: Starting create_hsm with passphrase=%s" , passphrase ? "provided" : "none" );
286
+
287
+ /* Initialize wally tal context for libwally operations */
288
+
289
+ status_debug ("HSM: Initialized wally tal context" );
290
+
291
+ /* Generate random entropy for new mnemonic */
292
+ randombytes_buf (entropy , sizeof (entropy ));
293
+ status_debug ("HSM: Generated random entropy" );
294
+
295
+
296
+ /* Generate mnemonic from entropy */
297
+ tal_wally_start ();
298
+ ret = bip39_mnemonic_from_bytes (NULL , entropy , sizeof (entropy ), & mnemonic );
299
+ tal_wally_end (tmpctx );
300
+
301
+ if (ret != WALLY_OK ) {
302
+ unlink_noerr ("hsm_secret" );
283
303
status_failed (STATUS_FAIL_INTERNAL_ERROR ,
284
- "Encrypting hsm_secret" );
285
- if (!write_all (fd , cipher .data , ENCRYPTED_HSM_SECRET_LEN )) {
304
+ "Failed to generate mnemonic from entropy" );
305
+ }
306
+ status_debug ("HSM: Generated mnemonic from entropy" );
307
+
308
+ if (!mnemonic ) {
286
309
unlink_noerr ("hsm_secret" );
310
+ //TODO: Add passphrase error message, add new codes
287
311
status_failed (STATUS_FAIL_INTERNAL_ERROR ,
288
- "Writing encrypted hsm_secret: %s" , strerror ( errno ) );
312
+ "Failed to get generated mnemonic" );
289
313
}
290
- }
291
-
292
- static void create_hsm (int fd )
293
- {
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. */
314
+
315
+ /* Derive seed hash from mnemonic + passphrase (or zero if no passphrase) */
316
+ if (!derive_seed_hash (mnemonic , passphrase , & seed_hash )) {
317
+ unlink_noerr ("hsm_secret" );
318
+ status_failed (STATUS_FAIL_INTERNAL_ERROR ,
319
+ "Failed to derive seed hash from mnemonic" );
320
+ }
321
+ status_debug ("HSM: Derived seed hash from mnemonic" );
322
+
323
+ /* Create hsm_secret format: seed_hash (32 bytes) + mnemonic */
324
+ hsm_secret_len = PASSPHRASE_HASH_LEN + strlen (mnemonic );
325
+ hsm_secret_data = tal_arr (tmpctx , u8 , hsm_secret_len );
326
+
327
+ /* Copy seed hash first */
328
+ memcpy (hsm_secret_data , & seed_hash , PASSPHRASE_HASH_LEN );
329
+ /* Copy mnemonic after seed hash */
330
+ memcpy (hsm_secret_data + PASSPHRASE_HASH_LEN , mnemonic , strlen (mnemonic ));
331
+ status_debug ("HSM: Created hsm_secret data structure" );
332
+
333
+ /* Derive the actual secret from mnemonic + passphrase for our global hsm_secret */
334
+ u8 bip32_seed [BIP39_SEED_LEN_512 ];
335
+ size_t bip32_seed_len ;
336
+
337
+ if (bip39_mnemonic_to_seed (mnemonic , passphrase , bip32_seed , sizeof (bip32_seed ), & bip32_seed_len ) != WALLY_OK ) {
338
+ unlink_noerr ("hsm_secret" );
339
+ status_failed (STATUS_FAIL_INTERNAL_ERROR ,
340
+ "Failed to derive seed from mnemonic" );
341
+ }
342
+ status_debug ("HSM: Derived BIP32 seed from mnemonic" );
343
+
344
+ /* Use first 32 bytes for hsm_secret */
345
+ memcpy (& hsm_secret .secret , bip32_seed , sizeof (hsm_secret .secret ));
346
+
347
+ /* Write the hsm_secret data to file */
348
+ if (!write_all (fd , hsm_secret_data , hsm_secret_len )) {
300
349
unlink_noerr ("hsm_secret" );
301
350
status_failed (STATUS_FAIL_INTERNAL_ERROR ,
302
351
"writing: %s" , strerror (errno ));
303
352
}
353
+ status_debug ("HSM: Successfully wrote hsm_secret to file" );
304
354
}
305
355
306
356
/*~ 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 )
357
+ * we run in the user's .lightning directory).
358
+ *
359
+ * NOTE: This function no longer creates encrypted 32-byte secrets. New hsm_secret
360
+ * files will use mnemonic format with passphrases.
361
+ */
362
+ static void maybe_create_new_hsm (const char * passphrase )
310
363
{
311
364
/*~ Note that this is opened for write-only, even though the permissions
312
365
* are set to read-only. That's perfectly valid! */
313
366
int fd = open ("hsm_secret" , O_CREAT |O_EXCL |O_WRONLY , 0400 );
314
367
if (fd < 0 ) {
315
368
/* If this is not the first time we've run, it will exist. */
316
- if (errno == EEXIST )
369
+ if (errno == EEXIST ) {
370
+ status_debug ("HSM: hsm_secret file already exists, skipping creation" );
317
371
return ;
372
+ }
318
373
status_failed (STATUS_FAIL_INTERNAL_ERROR ,
319
374
"creating: %s" , strerror (errno ));
320
375
}
321
376
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 );
377
+ status_debug ("HSM: Creating new hsm_secret file" );
378
+
379
+ /*~ Store the seed in clear. New hsm_secret files will use mnemonic format
380
+ * with passphrases, not encrypted 32-byte secrets. */
381
+ create_hsm (fd , passphrase );
333
382
/*~ fsync (mostly!) ensures that the file has reached the disk. */
334
383
if (fsync (fd ) != 0 ) {
335
384
unlink_noerr ("hsm_secret" );
@@ -367,62 +416,35 @@ static void maybe_create_new_hsm(const struct secret *encryption_key,
367
416
/*~ We always load the HSM file, even if we just created it above. This
368
417
* both unifies the code paths, and provides a nice sanity check that the
369
418
* file contents are as they will be for future invocations. */
370
- static void load_hsm (const struct secret * encryption_key )
419
+ static void load_hsm (const char * passphrase )
371
420
{
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 ));
421
+ u8 * hsm_secret_contents ;
422
+ struct hsm_secret * hsms ;
423
+ enum hsm_secret_error err ;
380
424
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
- }
425
+ /* Read the hsm_secret file */
426
+ hsm_secret_contents = grab_file (tmpctx , "hsm_secret" );
427
+ if (!hsm_secret_contents ) {
428
+ status_failed (STATUS_FAIL_INTERNAL_ERROR ,
429
+ "Could not read hsm_secret: %s" , strerror (errno ));
401
430
}
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 ;
406
431
407
- /* hsm_control must have checked it! */
408
- assert ( encryption_key );
432
+ /* Remove the NUL terminator that grab_file adds */
433
+ tal_resize ( & hsm_secret_contents , tal_bytelen ( hsm_secret_contents ) - 1 );
409
434
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
- }
435
+ /* Extract the secret using the new hsm_secret module */
436
+ tal_wally_start ();
437
+ hsms = extract_hsm_secret (tmpctx , hsm_secret_contents ,
438
+ tal_bytelen (hsm_secret_contents ),
439
+ passphrase , & err );
440
+ tal_wally_end (tmpctx );
441
+ if (!hsms ) {
442
+ status_failed (STATUS_FAIL_INTERNAL_ERROR ,
443
+ "Failed to load hsm_secret: %s" , hsm_secret_error_str (err ));
420
444
}
421
- else
422
- status_failed (STATUS_FAIL_INTERNAL_ERROR , "Invalid hsm_secret, "
423
- "no plaintext nor encrypted"
424
- " seed." );
425
- close (fd );
445
+
446
+ /* Copy the extracted secret to our global hsm_secret */
447
+ memcpy (& hsm_secret , & hsms -> secret , sizeof (hsm_secret ));
426
448
}
427
449
428
450
/*~ We have a pre-init call in developer mode, to set dev flags */
@@ -458,6 +480,7 @@ static struct io_plan *init_hsm(struct io_conn *conn,
458
480
const u8 * msg_in )
459
481
{
460
482
struct secret * hsm_encryption_key ;
483
+ const char * hsm_passphrase ;
461
484
struct bip32_key_version bip32_key_version ;
462
485
u32 minversion , maxversion ;
463
486
const u32 our_minversion = 4 , our_maxversion = 6 ;
@@ -494,27 +517,13 @@ static struct io_plan *init_hsm(struct io_conn *conn,
494
517
* think this is some kind of obscure CLN hazing ritual? Anyway, the
495
518
* passphrase needs to be *appended* to the mnemonic, so the HSM needs
496
519
* 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 );
520
+ * the TLV, and left the old "hsm_encryption_key" field in place (and lightningd
521
+ * never sets that anymore), and we use the TLV instead. */
522
+ if (tlvs -> hsm_passphrase )
523
+ hsm_passphrase = (const char * )tlvs -> hsm_passphrase ;
509
524
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." );
516
525
/*~ Don't swap this. */
517
- sodium_mlock (hsm_secret .data , sizeof (hsm_secret .data ));
526
+ sodium_mlock (hsm_secret .secret . data , sizeof (hsm_secret . secret .data ));
518
527
519
528
if (!developer ) {
520
529
assert (!dev_force_privkey );
@@ -526,16 +535,15 @@ static struct io_plan *init_hsm(struct io_conn *conn,
526
535
/* Once we have read the init message we know which params the master
527
536
* will use */
528
537
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 ));
538
+ maybe_create_new_hsm (hsm_passphrase );
539
+ load_hsm (hsm_passphrase );
535
540
536
541
/* Define the minimum common max version for the hsmd one */
537
542
hsmd_mutual_version = maxversion < our_maxversion ? maxversion : our_maxversion ;
538
- return req_reply (conn , c , hsmd_init (hsm_secret , hsmd_mutual_version ,
543
+
544
+ /* This was tallocated off NULL, and memleak complains if we don't free it */
545
+ tal_free (tlvs );
546
+ return req_reply (conn , c , hsmd_init (hsm_secret .secret , hsmd_mutual_version ,
539
547
bip32_key_version ));
540
548
}
541
549
0 commit comments