#ifndef LIGHTNING_COMMON_HSM_SECRET_H #define LIGHTNING_COMMON_HSM_SECRET_H #include "config.h" #include #include #include #include #include #include /* Length constants for encrypted HSM secret files */ #define HS_HEADER_LEN crypto_secretstream_xchacha20poly1305_HEADERBYTES #define HS_CIPHERTEXT_LEN \ (sizeof(struct secret) + crypto_secretstream_xchacha20poly1305_ABYTES) #define ENCRYPTED_HSM_SECRET_LEN (HS_HEADER_LEN + HS_CIPHERTEXT_LEN) enum hsm_secret_type { HSM_SECRET_PLAIN = 0, /* Legacy 32-byte format */ HSM_SECRET_ENCRYPTED = 1, /* Legacy 73-byte encrypted format */ HSM_SECRET_MNEMONIC_NO_PASS = 2, /* Mnemonic without passphrase */ HSM_SECRET_MNEMONIC_WITH_PASS = 3, /* Mnemonic with passphrase */ HSM_SECRET_INVALID = 4, /* When all else fails, blame the user */ }; enum hsm_secret_error { HSM_SECRET_OK = 0, HSM_SECRET_ERR_PASSPHRASE_REQUIRED, HSM_SECRET_ERR_PASSPHRASE_NOT_NEEDED, HSM_SECRET_ERR_WRONG_PASSPHRASE, HSM_SECRET_ERR_INVALID_MNEMONIC, HSM_SECRET_ERR_ENCRYPTION_FAILED, HSM_SECRET_ERR_SEED_DERIVATION_FAILED, HSM_SECRET_ERR_INVALID_FORMAT, HSM_SECRET_ERR_TERMINAL, }; /** * Represents the content of the hsm_secret file, either a raw seed or a mnemonic. */ struct hsm_secret { enum hsm_secret_type type; const u8 *secret_data; /* Variable length: 32 bytes (legacy) or 64 bytes (mnemonic) */ const char *mnemonic; /* NULL if not derived from mnemonic */ }; /** * Check if this HSM secret is mnemonic-based (64-byte seed). * Returns true for mnemonic-derived secrets, false for legacy 32-byte secrets. */ bool is_mnemonic_secret(size_t secret_len); /** * Check if we should use BIP86 derivation for this HSM secret. * BIP86 was introduced alongside mnemonic support, so they're available together. * Returns true if mnemonic-based secret is available, false otherwise. */ bool use_bip86_derivation(size_t secret_len); /** * Checks whether the hsm_secret data requires a passphrase to decrypt. * Handles legacy, encrypted, and mnemonic-based formats. */ bool hsm_secret_needs_passphrase(const u8 *hsm_secret, size_t len); /** * Parse and decrypt an hsm_secret file. * * @ctx - a tal context * @hsm_secret - raw file contents * @len - length of file * @passphrase - passphrase, or NULL if not needed * @err - pointer to set error code on failure * * Returns parsed `struct hsm_secret` or NULL on error. */ struct hsm_secret *extract_hsm_secret(const tal_t *ctx, const u8 *hsm_secret, size_t len, const char *passphrase, enum hsm_secret_error *err); /** * get_encryption_key - Derive encryption key from passphrase using Argon2. * @ctx - tal context for allocation * @passphrase - the passphrase to derive from * * Returns derived encryption key, or NULL on error. * The returned key is memory-locked and has a destructor to clear it. */ struct secret *get_encryption_key(const tal_t *ctx, const char *passphrase); /** * Encrypt a given hsm_secret using a provided encryption key. * @encryption_key - derived from passphrase (via Argon2) * @hsm_secret - plaintext secret to encrypt * @output - output buffer for encrypted data (must be ENCRYPTED_HSM_SECRET_LEN bytes) * * Returns true on success. */ bool encrypt_legacy_hsm_secret(const struct secret *encryption_key, const struct secret *hsm_secret, u8 *output); /** * Reads a passphrase from stdin, disabling terminal echo. * Returns a newly allocated string on success, NULL on error. * @ctx - tal context for allocation * @err - on failure, this will be set to the error code * * Returns allocated passphrase or NULL on error. */ const char *read_stdin_pass(const tal_t *ctx, enum hsm_secret_error *err); /** * Convert error code to human-readable string. * @err - the error code to convert * * Returns a string describing the error. */ const char *hsm_secret_error_str(enum hsm_secret_error err); /** * Detect the type of hsm_secret based on its content and length. * @hsm_secret - raw file contents * @len - length of file * * Returns the detected type. */ enum hsm_secret_type detect_hsm_secret_type(const u8 *hsm_secret, size_t len); /** * Check a BIP39 mnemonic is valid. * @mnemonic - 12 words, single-space separated, nul terminate. * * Returns HSM_SECRET_ERR_INVALID_MNEMONIC or HSM_SECRET_OK. */ enum hsm_secret_error validate_mnemonic(const char *mnemonic); /** * Reads a BIP39 mnemonic from stdin with validation. * Returns a newly allocated string on success, NULL on error. * @ctx - tal context for allocation * @err - optional pointer to set error code on failure * * Returns tal-allocated mnemonic string or NULL on error. */ const char *read_stdin_mnemonic(const tal_t *ctx, enum hsm_secret_error *err); /** * Derive seed hash from mnemonic + passphrase. * @mnemonic - the BIP39 mnemonic * @passphrase - the passphrase (can be NULL) * @seed_hash - output parameter for the derived seed hash * * Returns true on success, false on failure. */ bool derive_seed_hash(const char *mnemonic, const char *passphrase, struct sha256 *seed_hash); /** * Check if hsm_secret file is encrypted (legacy format only). * @path - path to the hsm_secret file * * Returns 1 if encrypted, 0 if not encrypted, -1 on error. */ int is_legacy_hsm_secret_encrypted(const char *path); /** * Convert hsm_secret_type enum to human-readable string. * @type - the hsm_secret_type to convert * * Returns a string describing the type. */ const char *format_type_name(enum hsm_secret_type type); /** * Derive encryption key from passphrase using Argon2. * @ctx - tal context for allocation * @passphrase - the passphrase to derive from * * Returns derived encryption key, or NULL on error. * The returned key is memory-locked and has a destructor to clear it. */ struct secret *get_encryption_key(const tal_t *ctx, const char *passphrase); #endif /* LIGHTNING_COMMON_HSM_SECRET_H */