@ -1,12 +1,17 @@
# include <bitcoin/privkey.h>
# include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
# include <ccan/err/err.h>
# include <ccan/noerr/noerr.h>
# include <ccan/read_write_all/read_write_all.h>
# include <ccan/tal/path/path.h>
# include <ccan/str/str.h>
# include <common/derive_basepoints.h>
# include <common/node_id.h>
# include <common/type_to_string.h>
# include <common/utils.h>
# include <errno.h>
# include <fcntl.h>
# include <inttypes.h>
# include <sodium.h>
# include <sys/stat.h>
# include <unistd.h>
@ -14,6 +19,8 @@
# define ERROR_HSM_FILE errno
# define ERROR_USAGE 2
# define ERROR_LIBSODIUM 3
# define ERROR_LIBWALLY 4
# define ERROR_KEYDERIV 5
static void show_usage ( void )
{
@ -21,6 +28,8 @@ static void show_usage(void)
printf ( " methods: \n " ) ;
printf ( " - decrypt <path/to/hsm_secret> <password> \n " ) ;
printf ( " - encrypt <path/to/hsm_secret> <password> \n " ) ;
printf ( " - dumpcommitments <node id> <channel dbid> <depth> "
" <path/to/hsm_secret> [hsm_secret password] \n " ) ;
exit ( 0 ) ;
}
@ -47,25 +56,33 @@ static bool ensure_hsm_secret_exists(int fd, const char *path)
return true ;
}
static int decrypt_hsm ( const char * hsm_secret_path , const char * passwd )
static void get_hsm_secret ( struct secret * hsm_secret ,
const char * hsm_secret_path )
{
int fd ;
struct stat st ;
struct secret key , hsm_secret ;
fd = open ( hsm_secret_path , O_RDONLY ) ;
if ( fd < 0 )
err ( ERROR_HSM_FILE , " Could not open hsm_secret " ) ;
if ( ! read_all ( fd , hsm_secret , sizeof ( * hsm_secret ) ) )
err ( ERROR_HSM_FILE , " Could not read hsm_secret " ) ;
close ( fd ) ;
}
/* Derive the encryption key from the password provided, and try to decrypt
* the cipher . */
static void get_encrypted_hsm_secret ( struct secret * hsm_secret ,
const char * hsm_secret_path ,
const char * passwd )
{
int fd ;
struct secret key ;
u8 salt [ 16 ] = " c-lightning \0 \0 \0 \0 \0 " ;
crypto_secretstream_xchacha20poly1305_state crypto_state ;
u8 header [ crypto_secretstream_xchacha20poly1305_HEADERBYTES ] ;
/* The cipher size is static with xchacha20poly1305. */
u8 cipher [ sizeof ( struct secret ) + crypto_secretstream_xchacha20poly1305_ABYTES ] ;
if ( sodium_init ( ) = = - 1 )
err ( ERROR_LIBSODIUM ,
" Could not initialize libsodium. Not enough entropy ? " ) ;
if ( stat ( hsm_secret_path , & st ) ! = 0 )
err ( ERROR_HSM_FILE , " Could not stat hsm_secret " ) ;
if ( st . st_size < = 32 )
err ( ERROR_USAGE , " hsm_secret is not encrypted " ) ;
fd = open ( hsm_secret_path , O_RDONLY ) ;
if ( fd < 0 )
err ( ERROR_HSM_FILE , " Could not open hsm_secret " ) ;
@ -75,8 +92,6 @@ static int decrypt_hsm(const char *hsm_secret_path, const char *passwd)
if ( ! read_all ( fd , cipher , sizeof ( cipher ) ) )
err ( ERROR_HSM_FILE , " Could not read cipher body " ) ;
/* Derive the encryption key from the password provided, and try to decrypt
* the cipher . */
if ( crypto_pwhash ( key . data , sizeof ( key . data ) , passwd , strlen ( passwd ) , salt ,
crypto_pwhash_argon2id_OPSLIMIT_MODERATE ,
crypto_pwhash_argon2id_MEMLIMIT_MODERATE ,
@ -85,11 +100,65 @@ static int decrypt_hsm(const char *hsm_secret_path, const char *passwd)
if ( crypto_secretstream_xchacha20poly1305_init_pull ( & crypto_state , header ,
key . data ) ! = 0 )
err ( ERROR_LIBSODIUM , " Could not initialize the crypto state " ) ;
if ( crypto_secretstream_xchacha20poly1305_pull ( & crypto_state , hsm_secret . data ,
if ( crypto_secretstream_xchacha20poly1305_pull ( & crypto_state , hsm_secret - > data ,
NULL , 0 , cipher , sizeof ( cipher ) ,
NULL , 0 ) ! = 0 )
err ( ERROR_LIBSODIUM , " Could not retrieve the seed. Wrong password ? " ) ;
close ( fd ) ;
}
/* Taken from hsmd. */
static void get_channel_seed ( struct secret * channel_seed , struct node_id * peer_id ,
u64 dbid , struct secret * hsm_secret )
{
struct secret channel_base ;
u8 input [ sizeof ( peer_id - > k ) + sizeof ( dbid ) ] ;
/*~ Again, "per-peer" should be "per-channel", but Hysterical Raisins */
const char * info = " per-peer seed " ;
/*~ We use the DER encoding of the pubkey, because it's platform
* independent . Since the dbid is unique , however , it ' s completely
* unnecessary , but again , existing users can ' t be broken . */
/* FIXME: lnd has a nicer BIP32 method for deriving secrets which we
* should migrate to . */
hkdf_sha256 ( & channel_base , sizeof ( struct secret ) , NULL , 0 ,
hsm_secret , sizeof ( * hsm_secret ) ,
/*~ Initially, we didn't support multiple channels per
* peer at all : a channel had to be completely forgotten
* before another could exist . That was slightly relaxed ,
* but the phrase " peer seed " is wired into the seed
* generation here , so we need to keep it that way for
* existing clients , rather than using " channel seed " . */
" peer seed " , strlen ( " peer seed " ) ) ;
memcpy ( input , peer_id - > k , sizeof ( peer_id - > k ) ) ;
BUILD_ASSERT ( sizeof ( peer_id - > k ) = = PUBKEY_CMPR_LEN ) ;
/*~ For all that talk about platform-independence, note that this
* field is endian - dependent ! But let ' s face it , little - endian won .
* In related news , we don ' t support EBCDIC or middle - endian . */
memcpy ( input + PUBKEY_CMPR_LEN , & dbid , sizeof ( dbid ) ) ;
hkdf_sha256 ( channel_seed , sizeof ( * channel_seed ) ,
input , sizeof ( input ) ,
& channel_base , sizeof ( channel_base ) ,
info , strlen ( info ) ) ;
}
static int decrypt_hsm ( const char * hsm_secret_path , const char * passwd )
{
int fd ;
struct stat st ;
struct secret hsm_secret ;
if ( sodium_init ( ) = = - 1 )
err ( ERROR_LIBSODIUM ,
" Could not initialize libsodium. Not enough entropy ? " ) ;
if ( stat ( hsm_secret_path , & st ) ! = 0 )
err ( ERROR_HSM_FILE , " Could not stat hsm_secret " ) ;
if ( st . st_size < = 32 )
err ( ERROR_HSM_FILE , " hsm_secret is not encrypted " ) ;
get_encrypted_hsm_secret ( & hsm_secret , hsm_secret_path , passwd ) ;
/* Create a backup file, "just in case". */
rename ( hsm_secret_path , " hsm_secret.backup " ) ;
@ -137,11 +206,7 @@ static int encrypt_hsm(const char *hsm_secret_path, const char *passwd)
err ( ERROR_HSM_FILE , " Could not stat hsm_secret " ) ;
if ( st . st_size > 32 )
err ( ERROR_USAGE , " hsm_secret is already encrypted " ) ;
fd = open ( hsm_secret_path , O_RDONLY ) ;
if ( fd < 0 )
err ( ERROR_HSM_FILE , " Could not open hsm_secret " ) ;
if ( ! read_all ( fd , & hsm_secret , sizeof ( hsm_secret ) ) )
err ( ERROR_HSM_FILE , " Could not read hsm_secret " ) ;
get_hsm_secret ( & hsm_secret , hsm_secret_path ) ;
/* Derive the encryption key from the password provided, and try to encrypt
* the seed . */
@ -187,6 +252,39 @@ static int encrypt_hsm(const char *hsm_secret_path, const char *passwd)
return 0 ;
}
static int dump_commitments_infos ( struct node_id * node_id , u64 channel_id ,
u64 depth , char * hsm_secret_path , char * passwd )
{
struct sha256 shaseed ;
struct secret hsm_secret , channel_seed , per_commitment_secret ;
struct pubkey per_commitment_point ;
secp256k1_ctx = secp256k1_context_create ( SECP256K1_CONTEXT_VERIFY
| SECP256K1_CONTEXT_SIGN ) ;
if ( passwd )
get_encrypted_hsm_secret ( & hsm_secret , hsm_secret_path , passwd ) ;
else
get_hsm_secret ( & hsm_secret , hsm_secret_path ) ;
get_channel_seed ( & channel_seed , node_id , channel_id , & hsm_secret ) ;
derive_shaseed ( & channel_seed , & shaseed ) ;
printf ( " shaseed: %s \n " , type_to_string ( tmpctx , struct sha256 , & shaseed ) ) ;
for ( u64 i = 0 ; i < depth ; i + + ) {
if ( ! per_commit_secret ( & shaseed , & per_commitment_secret , i ) )
err ( ERROR_KEYDERIV , " Could not derive secret #% " PRIu64 , i ) ;
printf ( " commit secret #% " PRIu64 " : %s \n " ,
i , tal_hexstr ( tmpctx , per_commitment_secret . data ,
sizeof ( per_commitment_secret . data ) ) ) ;
if ( ! per_commit_point ( & shaseed , & per_commitment_point , i ) )
err ( ERROR_KEYDERIV , " Could not derive point #% " PRIu64 , i ) ;
printf ( " commit point #% " PRIu64 " : %s \n " ,
i , type_to_string ( tmpctx , struct pubkey , & per_commitment_point ) ) ;
}
return 0 ;
}
int main ( int argc , char * argv [ ] )
{
const char * method ;
@ -210,5 +308,16 @@ int main(int argc, char *argv[])
return encrypt_hsm ( argv [ 2 ] , argv [ 3 ] ) ;
}
if ( streq ( method , " dumpcommitments " ) ) {
/* node_id channel_id depth hsm_secret ?password? */
if ( ! ( argv [ 2 ] & & argv [ 3 ] & & argv [ 4 ] & & argv [ 5 ] ) )
show_usage ( ) ;
struct node_id node_id ;
if ( ! node_id_from_hexstr ( argv [ 2 ] , strlen ( argv [ 2 ] ) , & node_id ) )
err ( ERROR_USAGE , " Bad node id " ) ;
return dump_commitments_infos ( & node_id , atol ( argv [ 3 ] ) , atol ( argv [ 4 ] ) ,
argv [ 5 ] , argv [ 6 ] ) ;
}
show_usage ( ) ;
}