1
1
#include "config.h"
2
+ #include <assert.h>
2
3
#include <ccan/crypto/sha256/sha256.h>
3
4
#include <ccan/mem/mem.h>
4
5
#include <ccan/tal/str/str.h>
22
23
/* Total length of an encrypted hsm_secret */
23
24
#define ENCRYPTED_HSM_SECRET_LEN (HS_HEADER_LEN + HS_CIPHERTEXT_LEN)
24
25
25
- static void destroy_secret (struct secret * secret )
26
+ void destroy_secret (struct secret * secret )
26
27
{
27
28
sodium_munlock (secret -> data , sizeof (secret -> data ));
28
29
}
29
30
31
+ /* Helper function to validate a mnemonic string */
32
+ static bool validate_mnemonic (const char * mnemonic , enum hsm_secret_error * err )
33
+ {
34
+ struct words * words ;
35
+
36
+ if (bip39_get_wordlist ("en" , & words ) != WALLY_OK ) {
37
+ abort ();
38
+ }
39
+
40
+ if (bip39_mnemonic_validate (words , mnemonic ) != WALLY_OK ) {
41
+ * err = HSM_SECRET_ERR_INVALID_MNEMONIC ;
42
+ return false;
43
+ }
44
+
45
+ return true;
46
+ }
47
+
30
48
struct secret * get_encryption_key (const tal_t * ctx , const char * passphrase )
31
49
{
32
50
struct secret * secret = tal (ctx , struct secret );
@@ -113,27 +131,6 @@ bool derive_seed_hash(const char *mnemonic, const char *passphrase, struct sha25
113
131
return true;
114
132
}
115
133
116
- /* Validate the passphrase for a mnemonic secret */
117
- bool validate_mnemonic_passphrase (const u8 * hsm_secret , size_t len , const char * passphrase )
118
- {
119
- enum hsm_secret_type type = detect_hsm_secret_type (hsm_secret , len );
120
-
121
- if (type != HSM_SECRET_MNEMONIC_WITH_PASS )
122
- return true; /* No validation needed */
123
-
124
- /* First 32 bytes are the stored seed hash */
125
- const struct sha256 * stored_hash = (const struct sha256 * )hsm_secret ;
126
- struct sha256 computed_hash ;
127
-
128
- /* Extract mnemonic portion (skip first 32 bytes which are seed hash) */
129
- const char * mnemonic_start = (const char * )(hsm_secret + sizeof (struct sha256 ));
130
-
131
- if (!derive_seed_hash (mnemonic_start , passphrase , & computed_hash ))
132
- return false;
133
-
134
- return sha256_eq (stored_hash , & computed_hash );
135
- }
136
-
137
134
static bool decrypt_hsm_secret (const struct secret * encryption_key ,
138
135
const u8 * cipher ,
139
136
struct secret * output )
@@ -171,8 +168,6 @@ const char *hsm_secret_error_str(enum hsm_secret_error err)
171
168
return "Invalid mnemonic" ;
172
169
case HSM_SECRET_ERR_ENCRYPTION_FAILED :
173
170
return "Encryption failed" ;
174
- case HSM_SECRET_ERR_WORDLIST_FAILED :
175
- return "Could not load wordlist" ;
176
171
case HSM_SECRET_ERR_SEED_DERIVATION_FAILED :
177
172
return "Could not derive seed from mnemonic" ;
178
173
case HSM_SECRET_ERR_INVALID_FORMAT :
@@ -227,7 +222,7 @@ static struct hsm_secret *extract_encrypted_secret(const tal_t *ctx,
227
222
decrypt_success = decrypt_hsm_secret (encryption_key , hsm_secret , & hsms -> secret );
228
223
229
224
/* Clear encryption key immediately after use */
230
- discard_key (encryption_key );
225
+ destroy_secret (encryption_key );
231
226
232
227
if (!decrypt_success ) {
233
228
/* Clear any partial decryption data */
@@ -247,15 +242,14 @@ static struct hsm_secret *extract_mnemonic_secret(const tal_t *ctx,
247
242
const u8 * hsm_secret ,
248
243
size_t len ,
249
244
const char * passphrase ,
245
+ enum hsm_secret_type type ,
250
246
enum hsm_secret_error * err )
251
247
{
252
248
struct hsm_secret * hsms = tal (ctx , struct hsm_secret );
253
- struct words * words ;
254
249
const u8 * mnemonic_start ;
255
250
size_t mnemonic_len ;
256
- enum hsm_secret_type type ;
257
251
258
- type = detect_hsm_secret_type ( hsm_secret , len );
252
+ assert ( type == HSM_SECRET_MNEMONIC_NO_PASS || type == HSM_SECRET_MNEMONIC_WITH_PASS );
259
253
hsms -> type = type ;
260
254
261
255
/* Extract mnemonic portion (skip first 32 bytes which are passphrase hash) */
@@ -269,7 +263,14 @@ static struct hsm_secret *extract_mnemonic_secret(const tal_t *ctx,
269
263
return tal_free (hsms );
270
264
}
271
265
272
- if (!validate_mnemonic_passphrase (hsm_secret , len , passphrase )) {
266
+ /* Validate passphrase by comparing stored hash with computed hash */
267
+ struct sha256 stored_hash , computed_hash ;
268
+ memcpy (& stored_hash , hsm_secret , sizeof (stored_hash ));
269
+ if (!derive_seed_hash ((const char * )mnemonic_start , passphrase , & computed_hash )) {
270
+ * err = HSM_SECRET_ERR_SEED_DERIVATION_FAILED ;
271
+ return tal_free (hsms );
272
+ }
273
+ if (!sha256_eq (& stored_hash , & computed_hash )) {
273
274
* err = HSM_SECRET_ERR_WRONG_PASSPHRASE ;
274
275
return tal_free (hsms );
275
276
}
@@ -283,23 +284,16 @@ static struct hsm_secret *extract_mnemonic_secret(const tal_t *ctx,
283
284
/* Copy and validate mnemonic */
284
285
hsms -> mnemonic = tal_strndup (hsms , (const char * )mnemonic_start , mnemonic_len );
285
286
286
- /* Load wordlist and validate mnemonic */
287
- if (bip39_get_wordlist ("en" , & words ) != WALLY_OK ) {
288
- * err = HSM_SECRET_ERR_WORDLIST_FAILED ;
289
- return tal_free (hsms );
290
- }
291
-
292
- if (bip39_mnemonic_validate (words , hsms -> mnemonic ) != WALLY_OK ) {
293
- * err = HSM_SECRET_ERR_INVALID_MNEMONIC ;
287
+ /* Validate mnemonic */
288
+ if (!validate_mnemonic (hsms -> mnemonic , err )) {
294
289
return tal_free (hsms );
295
290
}
296
291
297
292
/* Derive the seed from the mnemonic */
298
293
u8 bip32_seed [BIP39_SEED_LEN_512 ];
299
294
size_t bip32_seed_len ;
300
- const char * seed_passphrase = (type == HSM_SECRET_MNEMONIC_WITH_PASS ) ? passphrase : NULL ;
301
295
302
- if (bip39_mnemonic_to_seed (hsms -> mnemonic , seed_passphrase , bip32_seed , sizeof (bip32_seed ), & bip32_seed_len ) != WALLY_OK ) {
296
+ if (bip39_mnemonic_to_seed (hsms -> mnemonic , passphrase , bip32_seed , sizeof (bip32_seed ), & bip32_seed_len ) != WALLY_OK ) {
303
297
* err = HSM_SECRET_ERR_SEED_DERIVATION_FAILED ;
304
298
return tal_free (hsms );
305
299
}
@@ -326,13 +320,13 @@ struct hsm_secret *extract_hsm_secret(const tal_t *ctx,
326
320
case HSM_SECRET_ENCRYPTED :
327
321
return extract_encrypted_secret (ctx , hsm_secret , len , passphrase , err );
328
322
case HSM_SECRET_MNEMONIC_NO_PASS :
329
- return extract_mnemonic_secret (ctx , hsm_secret , len , NULL , err );
330
323
case HSM_SECRET_MNEMONIC_WITH_PASS :
331
- return extract_mnemonic_secret (ctx , hsm_secret , len , passphrase , err );
324
+ return extract_mnemonic_secret (ctx , hsm_secret , len , passphrase , type , err );
332
325
case HSM_SECRET_INVALID :
333
326
* err = HSM_SECRET_ERR_INVALID_FORMAT ;
334
327
return NULL ;
335
328
}
329
+ abort ();
336
330
}
337
331
338
332
bool encrypt_legacy_hsm_secret (const struct secret * encryption_key ,
@@ -355,28 +349,8 @@ bool encrypt_legacy_hsm_secret(const struct secret *encryption_key,
355
349
return true;
356
350
}
357
351
358
- /* Returns -1 on error (and sets errno), 0 if not encrypted, 1 if it is */
359
- int is_legacy_hsm_secret_encrypted (const char * path )
360
- {
361
- struct stat st ;
362
-
363
- if (stat (path , & st ) != 0 )
364
- return -1 ;
365
-
366
- return st .st_size == ENCRYPTED_HSM_SECRET_LEN ;
367
- }
368
-
369
- void discard_key (struct secret * key TAKES )
370
- {
371
- /* sodium_munlock() also zeroes the memory. */
372
- sodium_munlock (key -> data , sizeof (key -> data ));
373
- if (taken (key ))
374
- tal_free (key );
375
- }
376
-
377
352
static void destroy_passphrase (char * passphrase )
378
353
{
379
- sodium_memzero (passphrase , tal_bytelen (passphrase ));
380
354
sodium_munlock (passphrase , tal_bytelen (passphrase ));
381
355
}
382
356
@@ -404,8 +378,8 @@ static void restore_echo(const struct termios *saved_term)
404
378
tcsetattr (fileno (stdin ), TCSANOW , saved_term );
405
379
}
406
380
407
- /* Read line from stdin (uses malloc internally ) */
408
- static char * read_line (void )
381
+ /* Read line from stdin (uses tal allocation ) */
382
+ static char * read_line (const tal_t * ctx )
409
383
{
410
384
char * line = NULL ;
411
385
size_t size = 0 ;
@@ -420,7 +394,10 @@ static char *read_line(void)
420
394
if (len > 0 && line [len - 1 ] == '\n' )
421
395
line [len - 1 ] = '\0' ;
422
396
423
- return line ;
397
+ /* Convert to tal string */
398
+ char * result = tal_strndup (ctx , line , len );
399
+ free (line );
400
+ return result ;
424
401
}
425
402
426
403
const char * read_stdin_pass (const tal_t * ctx , enum hsm_secret_error * err )
@@ -434,75 +411,53 @@ const char *read_stdin_pass(const tal_t *ctx, enum hsm_secret_error *err)
434
411
return NULL ;
435
412
}
436
413
437
- char * input = read_line ();
414
+ char * input = read_line (ctx );
438
415
if (!input ) {
439
416
if (echo_disabled )
440
417
restore_echo (& saved_term );
441
418
* err = HSM_SECRET_ERR_INVALID_FORMAT ;
442
419
return NULL ;
443
420
}
444
421
445
- size_t len = strlen (input );
446
- char * passphrase = tal_arr (ctx , char , len + 1 );
447
- strcpy (passphrase , input );
448
- free (input );
449
-
450
422
/* Memory locking is mandatory: failure means we're on an insecure system */
451
- if (sodium_mlock (passphrase , len + 1 ) != 0 )
423
+ if (sodium_mlock (input , tal_bytelen ( input ) ) != 0 )
452
424
abort ();
453
425
454
- tal_add_destructor (passphrase , destroy_passphrase );
426
+ tal_add_destructor (input , destroy_passphrase );
455
427
456
428
if (echo_disabled )
457
429
restore_echo (& saved_term );
458
430
459
- return passphrase ;
431
+ return input ;
460
432
}
461
433
462
- /* Add this function to hsm_secret.c */
463
434
const char * read_stdin_mnemonic (const tal_t * ctx , enum hsm_secret_error * err )
464
435
{
465
436
* err = HSM_SECRET_OK ;
466
437
467
438
printf ("Introduce your BIP39 word list separated by space (at least 12 words):\n" );
468
439
fflush (stdout );
469
440
470
- char * line = NULL ;
471
- size_t size = 0 ;
472
- if (getline (& line , & size , stdin ) < 0 ) {
473
- free (line );
441
+ char * line = read_line (ctx );
442
+ if (!line ) {
474
443
* err = HSM_SECRET_ERR_INVALID_FORMAT ;
475
444
return NULL ;
476
445
}
477
446
478
- /* Strip newline */
479
- size_t len = strlen (line );
480
- if (len > 0 && line [len - 1 ] == '\n' )
481
- line [len - 1 ] = '\0' ;
482
-
483
447
/* Validate mnemonic */
484
- struct words * words ;
485
- if (bip39_get_wordlist ("en" , & words ) != WALLY_OK ) {
486
- free (line );
487
- * err = HSM_SECRET_ERR_WORDLIST_FAILED ;
488
- return NULL ;
489
- }
490
-
491
- if (bip39_mnemonic_validate (words , line ) != WALLY_OK ) {
492
- free (line );
493
- * err = HSM_SECRET_ERR_INVALID_MNEMONIC ;
448
+ if (!validate_mnemonic (line , err )) {
494
449
return NULL ;
495
450
}
496
451
497
- /* Convert to tal string */
498
- char * mnemonic = tal_strdup (ctx , line );
499
- free (line );
500
-
501
- return mnemonic ;
452
+ return line ;
502
453
}
503
454
455
+ int is_legacy_hsm_secret_encrypted (const char * path )
456
+ {
457
+ struct stat st ;
504
458
459
+ if (stat (path , & st ) != 0 )
460
+ return -1 ;
505
461
506
-
507
-
508
-
462
+ return st .st_size == ENCRYPTED_HSM_SECRET_LEN ;
463
+ }
0 commit comments