11#include "config.h"
2+ #include <assert.h>
23#include <ccan/crypto/sha256/sha256.h>
34#include <ccan/mem/mem.h>
45#include <ccan/tal/str/str.h>
2223/* Total length of an encrypted hsm_secret */
2324#define ENCRYPTED_HSM_SECRET_LEN (HS_HEADER_LEN + HS_CIPHERTEXT_LEN)
2425
25- static void destroy_secret (struct secret * secret )
26+ void destroy_secret (struct secret * secret )
2627{
2728 sodium_munlock (secret -> data , sizeof (secret -> data ));
2829}
2930
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+
3048struct secret * get_encryption_key (const tal_t * ctx , const char * passphrase )
3149{
3250 struct secret * secret = tal (ctx , struct secret );
@@ -113,27 +131,6 @@ bool derive_seed_hash(const char *mnemonic, const char *passphrase, struct sha25
113131 return true;
114132}
115133
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-
137134static bool decrypt_hsm_secret (const struct secret * encryption_key ,
138135 const u8 * cipher ,
139136 struct secret * output )
@@ -171,8 +168,6 @@ const char *hsm_secret_error_str(enum hsm_secret_error err)
171168 return "Invalid mnemonic" ;
172169 case HSM_SECRET_ERR_ENCRYPTION_FAILED :
173170 return "Encryption failed" ;
174- case HSM_SECRET_ERR_WORDLIST_FAILED :
175- return "Could not load wordlist" ;
176171 case HSM_SECRET_ERR_SEED_DERIVATION_FAILED :
177172 return "Could not derive seed from mnemonic" ;
178173 case HSM_SECRET_ERR_INVALID_FORMAT :
@@ -227,7 +222,7 @@ static struct hsm_secret *extract_encrypted_secret(const tal_t *ctx,
227222 decrypt_success = decrypt_hsm_secret (encryption_key , hsm_secret , & hsms -> secret );
228223
229224 /* Clear encryption key immediately after use */
230- discard_key (encryption_key );
225+ destroy_secret (encryption_key );
231226
232227 if (!decrypt_success ) {
233228 /* Clear any partial decryption data */
@@ -247,15 +242,14 @@ static struct hsm_secret *extract_mnemonic_secret(const tal_t *ctx,
247242 const u8 * hsm_secret ,
248243 size_t len ,
249244 const char * passphrase ,
245+ enum hsm_secret_type type ,
250246 enum hsm_secret_error * err )
251247{
252248 struct hsm_secret * hsms = tal (ctx , struct hsm_secret );
253- struct words * words ;
254249 const u8 * mnemonic_start ;
255250 size_t mnemonic_len ;
256- enum hsm_secret_type type ;
257251
258- type = detect_hsm_secret_type ( hsm_secret , len );
252+ assert ( type == HSM_SECRET_MNEMONIC_NO_PASS || type == HSM_SECRET_MNEMONIC_WITH_PASS );
259253 hsms -> type = type ;
260254
261255 /* 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,
269263 return tal_free (hsms );
270264 }
271265
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 )) {
273274 * err = HSM_SECRET_ERR_WRONG_PASSPHRASE ;
274275 return tal_free (hsms );
275276 }
@@ -283,23 +284,16 @@ static struct hsm_secret *extract_mnemonic_secret(const tal_t *ctx,
283284 /* Copy and validate mnemonic */
284285 hsms -> mnemonic = tal_strndup (hsms , (const char * )mnemonic_start , mnemonic_len );
285286
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 )) {
294289 return tal_free (hsms );
295290 }
296291
297292 /* Derive the seed from the mnemonic */
298293 u8 bip32_seed [BIP39_SEED_LEN_512 ];
299294 size_t bip32_seed_len ;
300- const char * seed_passphrase = (type == HSM_SECRET_MNEMONIC_WITH_PASS ) ? passphrase : NULL ;
301295
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 ) {
303297 * err = HSM_SECRET_ERR_SEED_DERIVATION_FAILED ;
304298 return tal_free (hsms );
305299 }
@@ -326,13 +320,13 @@ struct hsm_secret *extract_hsm_secret(const tal_t *ctx,
326320 case HSM_SECRET_ENCRYPTED :
327321 return extract_encrypted_secret (ctx , hsm_secret , len , passphrase , err );
328322 case HSM_SECRET_MNEMONIC_NO_PASS :
329- return extract_mnemonic_secret (ctx , hsm_secret , len , NULL , err );
330323 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 );
332325 case HSM_SECRET_INVALID :
333326 * err = HSM_SECRET_ERR_INVALID_FORMAT ;
334327 return NULL ;
335328 }
329+ abort ();
336330}
337331
338332bool encrypt_legacy_hsm_secret (const struct secret * encryption_key ,
@@ -355,28 +349,8 @@ bool encrypt_legacy_hsm_secret(const struct secret *encryption_key,
355349 return true;
356350}
357351
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-
377352static void destroy_passphrase (char * passphrase )
378353{
379- sodium_memzero (passphrase , tal_bytelen (passphrase ));
380354 sodium_munlock (passphrase , tal_bytelen (passphrase ));
381355}
382356
@@ -404,8 +378,8 @@ static void restore_echo(const struct termios *saved_term)
404378 tcsetattr (fileno (stdin ), TCSANOW , saved_term );
405379}
406380
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 )
409383{
410384 char * line = NULL ;
411385 size_t size = 0 ;
@@ -420,7 +394,10 @@ static char *read_line(void)
420394 if (len > 0 && line [len - 1 ] == '\n' )
421395 line [len - 1 ] = '\0' ;
422396
423- return line ;
397+ /* Convert to tal string */
398+ char * result = tal_strndup (ctx , line , len );
399+ free (line );
400+ return result ;
424401}
425402
426403const 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)
434411 return NULL ;
435412 }
436413
437- char * input = read_line ();
414+ char * input = read_line (ctx );
438415 if (!input ) {
439416 if (echo_disabled )
440417 restore_echo (& saved_term );
441418 * err = HSM_SECRET_ERR_INVALID_FORMAT ;
442419 return NULL ;
443420 }
444421
445- size_t len = strlen (input );
446- char * passphrase = tal_arr (ctx , char , len + 1 );
447- strcpy (passphrase , input );
448- free (input );
449-
450422 /* 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 )
452424 abort ();
453425
454- tal_add_destructor (passphrase , destroy_passphrase );
426+ tal_add_destructor (input , destroy_passphrase );
455427
456428 if (echo_disabled )
457429 restore_echo (& saved_term );
458430
459- return passphrase ;
431+ return input ;
460432}
461433
462- /* Add this function to hsm_secret.c */
463434const char * read_stdin_mnemonic (const tal_t * ctx , enum hsm_secret_error * err )
464435{
465436 * err = HSM_SECRET_OK ;
466437
467438 printf ("Introduce your BIP39 word list separated by space (at least 12 words):\n" );
468439 fflush (stdout );
469440
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 ) {
474443 * err = HSM_SECRET_ERR_INVALID_FORMAT ;
475444 return NULL ;
476445 }
477446
478- /* Strip newline */
479- size_t len = strlen (line );
480- if (len > 0 && line [len - 1 ] == '\n' )
481- line [len - 1 ] = '\0' ;
482-
483447 /* 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 )) {
494449 return NULL ;
495450 }
496451
497- /* Convert to tal string */
498- char * mnemonic = tal_strdup (ctx , line );
499- free (line );
500-
501- return mnemonic ;
452+ return line ;
502453}
503454
455+ int is_legacy_hsm_secret_encrypted (const char * path )
456+ {
457+ struct stat st ;
504458
459+ if (stat (path , & st ) != 0 )
460+ return -1 ;
505461
506-
507-
508-
462+ return st .st_size == ENCRYPTED_HSM_SECRET_LEN ;
463+ }
0 commit comments