Basically, `devtools/reduce-includes.sh */*.c`. Build time from make clean (RUST=0) (includes building external libs): Before: real 0m38.944000-40.416000(40.1131+/-0.4)s user 3m6.790000-17.159000(15.0571+/-2.8)s sys 0m35.304000-37.336000(36.8942+/-0.57)s After: real 0m37.872000-39.974000(39.5466+/-0.59)s user 3m1.211000-14.968000(12.4556+/-3.9)s sys 0m35.008000-36.830000(36.4143+/-0.5)s Build time after touch config.vars (RUST=0): Before: real 0m19.831000-21.862000(21.5528+/-0.58)s user 2m15.361000-30.731000(28.4798+/-4.4)s sys 0m21.056000-22.339000(22.0346+/-0.35)s After: real 0m18.384000-21.307000(20.8605+/-0.92)s user 2m5.585000-26.843000(23.6017+/-6.7)s sys 0m19.650000-22.003000(21.4943+/-0.69)s Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
162 lines
4.9 KiB
C
162 lines
4.9 KiB
C
#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/json_param.h>
|
|
#include <common/json_stream.h>
|
|
#include <errno.h>
|
|
#include <plugins/libplugin.h>
|
|
|
|
/* Information this plugin wants to keep. */
|
|
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);
|
|
}
|
|
|
|
/* Don't let compiler do clever things, which would allow the caller
|
|
* to measure time, and figure out how much of the passphrase matched! */
|
|
static bool compare_passphrases(const char *a, const char *b)
|
|
{
|
|
struct sha256 a_sha, b_sha;
|
|
|
|
/* Technically, this gives information about passphrase length, but
|
|
* they can also just brute force it if it is small, so this doesn't
|
|
* add much! Also, hashing messes with timing quite a bit. */
|
|
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, *err;
|
|
struct secret hsm_secret;
|
|
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");
|
|
|
|
/* Technically, this could become a timing oracle. */
|
|
if (!compare_passphrases(exposesecret->exposure_passphrase, passphrase))
|
|
return command_fail(cmd, LIGHTNINGD, "passphrase does not match exposesecrets-passphrase");
|
|
|
|
contents = grab_file(tmpctx, "hsm_secret");
|
|
if (!contents)
|
|
return command_fail(cmd, LIGHTNINGD, "Could not open hsm_secret: %s", strerror(errno));
|
|
|
|
/* grab_file adds a \0 byte at the end for convenience */
|
|
if (tal_bytelen(contents) == sizeof(hsm_secret) + 1) {
|
|
memcpy(&hsm_secret, contents, sizeof(hsm_secret));
|
|
} else {
|
|
return command_fail(cmd, LIGHTNINGD, "Not a valid hsm_secret file? Bad length (maybe encrypted?)");
|
|
}
|
|
|
|
/* Before we expose it, check it's correct! */
|
|
hkdf_sha256(&node_privkey, sizeof(node_privkey),
|
|
&salt, sizeof(salt),
|
|
&hsm_secret,
|
|
sizeof(hsm_secret),
|
|
"nodeid", 6);
|
|
|
|
/* Should not happen! */
|
|
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 they didn't give an identifier, we make an appropriate one! */
|
|
if (!id) {
|
|
size_t off = 0;
|
|
/* If we run out of alias, use x. */
|
|
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;
|
|
/* Convert to lower case */
|
|
c = tolower(c);
|
|
/* Must be a valid bech32 char now */
|
|
if (bech32_charset_rev[c] == -1)
|
|
continue;
|
|
idstr[off++] = c;
|
|
}
|
|
|
|
id = tal_strdup(cmd, idstr);
|
|
}
|
|
|
|
/* This also cannot fail! */
|
|
err = codex32_secret_encode(tmpctx, "cl", id, 0, hsm_secret.data, 32, &bip93);
|
|
if (err)
|
|
return command_fail(cmd, LIGHTNINGD, "Unexpected failure encoding hsm_secret: %s", err);
|
|
|
|
/* If we're just checking, stop */
|
|
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);
|
|
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);
|
|
}
|