#include "config.h"
#include <ccan/array_size/array_size.h>
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
#include <ccan/tal/grab_file/grab_file.h>
#include <ccan/tal/str/str.h>
#include <common/bech32.h>
#include <common/codex32.h>
#include <common/hsm_secret.h>
#include <common/json_param.h>
#include <common/json_stream.h>
#include <common/setup.h>
#include <common/utils.h>
#include <errno.h>
#include <plugins/libplugin.h>
struct exposesecret {
char *exposure_passphrase;
struct pubkey our_node_id;
const char *our_node_alias;
};
static struct exposesecret *exposesecret_data(struct plugin *plugin)
{
return plugin_get_data(plugin, struct exposesecret);
}
static bool compare_passphrases(const char *a, const char *b)
{
struct sha256 a_sha, b_sha;
sha256(&a_sha, a, strlen(a));
sha256(&b_sha, b, strlen(b));
return sha256_eq(&a_sha, &b_sha);
}
static struct command_result *json_exposesecret(struct command *cmd,
const char *buffer,
const jsmntok_t *params)
{
const struct exposesecret *exposesecret = exposesecret_data(cmd->plugin);
struct json_stream *js;
u8 *contents;
const char *id, *passphrase;
enum hsm_secret_error err;
struct hsm_secret *hsms;
struct privkey node_privkey;
struct pubkey node_id;
char *bip93;
u32 salt = 0;
if (!param_check(cmd, buffer, params,
p_req("passphrase", param_string, &passphrase),
p_opt("identifier", param_string, &id),
NULL))
return command_param_failed();
if (!exposesecret->exposure_passphrase)
return command_fail(cmd, LIGHTNINGD, "exposesecrets-passphrase is not set");
if (!compare_passphrases(exposesecret->exposure_passphrase, passphrase))
return command_fail(cmd, LIGHTNINGD, "passphrase does not match exposesecrets-passphrase");
contents = grab_file_raw(tmpctx, "hsm_secret");
if (!contents)
return command_fail(cmd, LIGHTNINGD, "Could not open hsm_secret: %s", strerror(errno));
if (hsm_secret_needs_passphrase(contents, tal_bytelen(contents))) {
return command_fail(cmd, LIGHTNINGD, "Secret with passphrase is not supported");
}
hsms = extract_hsm_secret(tmpctx, contents, tal_bytelen(contents), NULL, &err);
if (!hsms)
return command_fail(cmd, LIGHTNINGD, "Could not parse hsm_secret: %s", hsm_secret_error_str(err));
hkdf_sha256(&node_privkey, sizeof(node_privkey),
&salt, sizeof(salt),
hsms->secret_data,
32,
"nodeid", 6);
if (!pubkey_from_privkey(&node_privkey, &node_id))
return command_fail(cmd, LIGHTNINGD, "Invalid private key?");
if (!pubkey_eq(&node_id, &exposesecret->our_node_id))
return command_fail(cmd, LIGHTNINGD, "This hsm_secret is not for the current node");
if (!id) {
size_t off = 0;
char idstr[] = "xxxx";
for (size_t i = 0; idstr[off]; i++) {
unsigned char c = exposesecret->our_node_alias[i];
if (c == 0)
break;
if (c >= sizeof(bech32_charset_rev))
continue;
c = tolower(c);
if (bech32_charset_rev[c] == -1)
continue;
idstr[off++] = c;
}
id = tal_strdup(cmd, idstr);
}
const char *encode_err = codex32_secret_encode(tmpctx, "cl", id, 0, hsms->secret_data, 32, &bip93);
if (encode_err)
return command_fail(cmd, LIGHTNINGD, "Unexpected failure encoding hsm_secret: %s", encode_err);
if (command_check_only(cmd))
return command_check_done(cmd);
js = jsonrpc_stream_success(cmd);
json_add_string(js, "identifier", id);
json_add_string(js, "codex32", bip93);
if (hsms->mnemonic)
json_add_string(js, "mnemonic", hsms->mnemonic);
return command_finished(cmd, js);
}
static const char *init(struct command *init_cmd,
const char *buf UNUSED, const jsmntok_t *config UNUSED)
{
struct exposesecret *exposesecret = exposesecret_data(init_cmd->plugin);
rpc_scan(init_cmd, "getinfo",
take(json_out_obj(NULL, NULL, NULL)),
"{id:%,alias:%}",
JSON_SCAN(json_to_pubkey, &exposesecret->our_node_id),
JSON_SCAN_TAL(exposesecret, json_strdup, &exposesecret->our_node_alias));
return NULL;
}
static const struct plugin_command commands[] = {
{
"exposesecret",
json_exposesecret,
}
};
int main(int argc, char *argv[])
{
setup_locale();
struct exposesecret *exposesecret = talz(NULL, struct exposesecret);
plugin_main(argv, init, take(exposesecret),
PLUGIN_RESTARTABLE, true, NULL, commands, ARRAY_SIZE(commands),
NULL, 0, NULL, 0, NULL, 0,
plugin_option("exposesecret-passphrase", "string-conceal",
"Enable exposesecret command to allow HSM Secret backup, with this passphrase",
charp_option, NULL, &exposesecret->exposure_passphrase),
NULL);
}