Skip to content

Commit a6d70de

Browse files
committed
TRY to migrate hsmd to use mnemonics
1 parent 1ca8bfa commit a6d70de

File tree

5 files changed

+182
-157
lines changed

5 files changed

+182
-157
lines changed

common/hsm_secret.c

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
#include "config.h"
2+
#include <ccan/crypto/sha256/sha256.h>
3+
#include <ccan/mem/mem.h>
24
#include <ccan/tal/str/str.h>
35
#include <ccan/tal/tal.h>
46
#include <common/errcode.h>
5-
#include <common/utils.h>
67
#include <common/hsm_secret.h>
8+
#include <common/utils.h>
79
#include <errno.h>
10+
#include <sodium.h>
11+
#include <sys/stat.h>
812
#include <termios.h>
913
#include <unistd.h>
10-
#include <ccan/crypto/sha256/sha256.h>
11-
#include <ccan/mem/mem.h>
12-
#include <sodium.h>
13-
#include <wally_bip39.h>
14-
#include <sys/stat.h>
14+
#include <wally_bip39.h>
1515

1616
/* Length of the encrypted hsm secret header. */
1717
#define HS_HEADER_LEN crypto_secretstream_xchacha20poly1305_HEADERBYTES
@@ -70,11 +70,15 @@ bool hsm_secret_needs_passphrase(const u8 *hsm_secret, size_t len)
7070
case HSM_SECRET_INVALID:
7171
return false;
7272
}
73-
return false;
73+
abort();
7474
}
7575

7676
enum hsm_secret_type detect_hsm_secret_type(const u8 *hsm_secret, size_t len)
7777
{
78+
/* Check for invalid cases first and return early */
79+
if (len < HSM_SECRET_PLAIN_SIZE)
80+
return HSM_SECRET_INVALID;
81+
7882
/* Legacy 32-byte plain format */
7983
if (len == HSM_SECRET_PLAIN_SIZE)
8084
return HSM_SECRET_PLAIN;
@@ -84,20 +88,29 @@ enum hsm_secret_type detect_hsm_secret_type(const u8 *hsm_secret, size_t len)
8488
return HSM_SECRET_ENCRYPTED;
8589

8690
/* Check if it starts with our type bytes (mnemonic formats) */
87-
if (len > 32) {
88-
if (memeqzero(hsm_secret, 32))
89-
return HSM_SECRET_MNEMONIC_NO_PASS;
90-
else
91-
return HSM_SECRET_MNEMONIC_WITH_PASS;
92-
}
93-
return HSM_SECRET_INVALID;
91+
if (memeqzero(hsm_secret, 32))
92+
return HSM_SECRET_MNEMONIC_NO_PASS;
93+
else
94+
return HSM_SECRET_MNEMONIC_WITH_PASS;
9495
}
9596

96-
static void hash_passphrase(const char *passphrase, u8 hash[PASSPHRASE_HASH_LEN])
97+
/* Helper function to derive seed hash from mnemonic + passphrase */
98+
bool derive_seed_hash(const char *mnemonic, const char *passphrase, struct sha256 *seed_hash)
9799
{
98-
struct sha256 sha;
99-
sha256(&sha, passphrase, strlen(passphrase));
100-
memcpy(hash, sha.u.u8, PASSPHRASE_HASH_LEN);
100+
if (!passphrase) {
101+
/* No passphrase - return zero hash */
102+
memset(seed_hash, 0, sizeof(*seed_hash));
103+
return true;
104+
}
105+
106+
u8 bip32_seed[BIP39_SEED_LEN_512];
107+
size_t bip32_seed_len;
108+
109+
if (bip39_mnemonic_to_seed(mnemonic, passphrase, bip32_seed, sizeof(bip32_seed), &bip32_seed_len) != WALLY_OK)
110+
return false;
111+
112+
sha256(seed_hash, bip32_seed, sizeof(bip32_seed));
113+
return true;
101114
}
102115

103116
/* Validate the passphrase for a mnemonic secret */
@@ -108,28 +121,34 @@ bool validate_mnemonic_passphrase(const u8 *hsm_secret, size_t len, const char *
108121
if (type != HSM_SECRET_MNEMONIC_WITH_PASS)
109122
return true; /* No validation needed */
110123

111-
/* First 32 bytes are the stored passphrase hash */
112-
const u8 *stored_hash = hsm_secret;
113-
u8 computed_hash[32];
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+
size_t mnemonic_len = len - sizeof(struct sha256);
114131

115-
hash_passphrase(passphrase, computed_hash);
116-
return memcmp(stored_hash, computed_hash, 32) == 0;
132+
if (!derive_seed_hash(mnemonic_start, passphrase, &computed_hash))
133+
return false;
134+
135+
return sha256_eq(stored_hash, &computed_hash);
117136
}
118137

119138
static bool decrypt_hsm_secret(const struct secret *encryption_key,
120-
const struct encrypted_hsm_secret *cipher,
139+
const u8 *cipher,
121140
struct secret *output)
122141
{
123142
crypto_secretstream_xchacha20poly1305_state crypto_state;
124143

125144
/* The header part */
126-
if (crypto_secretstream_xchacha20poly1305_init_pull(&crypto_state, cipher->data,
145+
if (crypto_secretstream_xchacha20poly1305_init_pull(&crypto_state, cipher,
127146
encryption_key->data) != 0)
128147
return false;
129148
/* The ciphertext part */
130149
if (crypto_secretstream_xchacha20poly1305_pull(&crypto_state, output->data,
131150
NULL, 0,
132-
cipher->data + HS_HEADER_LEN,
151+
cipher + HS_HEADER_LEN,
133152
HS_CIPHERTEXT_LEN,
134153
NULL, 0) != 0)
135154
return false;
@@ -166,6 +185,7 @@ const char *hsm_secret_error_str(enum hsm_secret_error err)
166185
}
167186
return "Unknown error";
168187
}
188+
169189
static struct hsm_secret *extract_plain_secret(const tal_t *ctx,
170190
const u8 *hsm_secret,
171191
size_t len,
@@ -180,6 +200,7 @@ static struct hsm_secret *extract_plain_secret(const tal_t *ctx,
180200
*err = HSM_SECRET_OK;
181201
return hsms;
182202
}
203+
183204
static struct hsm_secret *extract_encrypted_secret(const tal_t *ctx,
184205
const u8 *hsm_secret,
185206
size_t len,
@@ -204,7 +225,7 @@ static struct hsm_secret *extract_encrypted_secret(const tal_t *ctx,
204225
memset(&hsms->secret, 0, sizeof(hsms->secret));
205226

206227
/* Attempt decryption */
207-
decrypt_success = decrypt_hsm_secret(encryption_key, (const struct encrypted_hsm_secret *)hsm_secret, &hsms->secret);
228+
decrypt_success = decrypt_hsm_secret(encryption_key, hsm_secret, &hsms->secret);
208229

209230
/* Clear encryption key immediately after use */
210231
discard_key(encryption_key);
@@ -317,15 +338,15 @@ struct hsm_secret *extract_hsm_secret(const tal_t *ctx,
317338

318339
bool encrypt_legacy_hsm_secret(const struct secret *encryption_key,
319340
const struct secret *hsm_secret,
320-
struct encrypted_hsm_secret *output)
341+
u8 *output)
321342
{
322343
crypto_secretstream_xchacha20poly1305_state crypto_state;
323344

324-
if (crypto_secretstream_xchacha20poly1305_init_push(&crypto_state, output->data,
345+
if (crypto_secretstream_xchacha20poly1305_init_push(&crypto_state, output,
325346
encryption_key->data) != 0)
326347
return false;
327348
if (crypto_secretstream_xchacha20poly1305_push(&crypto_state,
328-
output->data + HS_HEADER_LEN,
349+
output + HS_HEADER_LEN,
329350
NULL, hsm_secret->data,
330351
sizeof(hsm_secret->data),
331352
/* Additional data and tag */

common/hsm_secret.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,6 @@ struct hsm_secret {
4545
enum hsm_secret_type type;
4646
};
4747

48-
struct encrypted_hsm_secret {
49-
u8 data[ENCRYPTED_HSM_SECRET_LEN];
50-
};
51-
5248
/**
5349
* Checks whether the hsm_secret data requires a passphrase to decrypt.
5450
* Handles legacy, encrypted, and mnemonic-based formats.
@@ -75,13 +71,13 @@ struct hsm_secret *extract_hsm_secret(const tal_t *ctx,
7571
* Encrypt a given hsm_secret using a provided encryption key.
7672
* @encryption_key - derived from passphrase (via Argon2)
7773
* @hsm_secret - plaintext secret to encrypt
78-
* @output - output struct containing encrypted data
74+
* @output - output buffer for encrypted data (must be ENCRYPTED_HSM_SECRET_LEN bytes)
7975
*
8076
* Returns true on success.
8177
*/
8278
bool encrypt_legacy_hsm_secret(const struct secret *encryption_key,
8379
const struct secret *hsm_secret,
84-
struct encrypted_hsm_secret *output);
80+
u8 *output);
8581

8682
/**
8783
* Securely discard an encryption key from memory.
@@ -153,4 +149,14 @@ bool validate_mnemonic_passphrase(const u8 *hsm_secret, size_t len, const char *
153149
*/
154150
const char *read_stdin_mnemonic(const tal_t *ctx, enum hsm_secret_error *err);
155151

152+
/**
153+
* Derive seed hash from mnemonic + passphrase.
154+
* @mnemonic - the BIP39 mnemonic
155+
* @passphrase - the passphrase (can be NULL)
156+
* @seed_hash - output parameter for the derived seed hash
157+
*
158+
* Returns true on success, false on failure.
159+
*/
160+
bool derive_seed_hash(const char *mnemonic, const char *passphrase, struct sha256 *seed_hash);
161+
156162
#endif /* LIGHTNING_COMMON_HSM_SECRET_H */

0 commit comments

Comments
 (0)