codex32: use "cl" instead of "ms" as our HRP.

This was strongly recommended by Russell O'Connor: the "ms" implies that
it's a BIP-32 master secret, and this is CLN specific.

If we changed the hrp to "cln" it would be better, but apparently that
means we no longer fit in a "standard billfold metal wallet" (and
our code assumes a 2-byte prefix anyway).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2023-08-02 12:27:43 +09:30
parent 67e43ea868
commit 0f6687ec7b
7 changed files with 58 additions and 28 deletions

View File

@ -313,6 +313,7 @@ fail:
/* Return NULL if the codex32 is invalid */
struct codex32 *codex32_decode(const tal_t *ctx,
const char *hrp,
const char *codex32str,
char **fail)
{
@ -335,8 +336,8 @@ struct codex32 *codex32_decode(const tal_t *ctx,
}
parts->hrp = tal_strndup(parts, codex32str, sep - codex32str);
if (!streq(parts->hrp, "ms")) {
*fail = tal_fmt(ctx, "Invalid HRP!");
if (hrp && !streq(parts->hrp, hrp)) {
*fail = tal_fmt(ctx, "Invalid hrp %s!", parts->hrp);
return tal_free(parts);
}
@ -397,6 +398,7 @@ struct codex32 *codex32_decode(const tal_t *ctx,
/* Returns Codex32 encoded secret of the seed provided. */
const char *codex32_secret_encode(const tal_t *ctx,
const char *hrp,
const char *id,
const u32 threshold,
const u8 *seed,
@ -404,7 +406,11 @@ const char *codex32_secret_encode(const tal_t *ctx,
char **bip93)
{
const struct checksum_engine *csum_engine;
const char *hrp = "ms";
/* FIXME: Our code assumes a two-letter HRP! Larger won't allow a
* 128-bit secret in a "standard billfold metal wallet" acording to
* Russell O'Connor */
assert(strlen(hrp) == 2);
if (threshold > 9 || threshold < 0 || threshold == 1)
return tal_fmt(ctx, "Invalid threshold %u", threshold);

View File

@ -32,17 +32,22 @@ struct codex32 {
* updated to contain the details extracted from the codex32 string.
* fail: Pointer to a char *, that would be updated with the reason
* of failure in case this function returns a NULL.
* In: input: Pointer to a null-terminated codex32 string.
* In: ctx: Allocation context for *fail or return.
* hrp: If non-NULL, a hrp which must match.
* codex32str: Pointer to a nul-terminated codex32 string.
*
* Returns Parts to indicate decoding was successful. NULL is returned if decoding failed,
* with appropriate reason in the fail param
*/
struct codex32 *codex32_decode(const tal_t *ctx,
const char *hrp,
const char *codex32str,
char **fail);
/** Encode a seed into codex32 secret format.
*
* In: input: id: Valid 4 char string identifying the secret
* In: input: hrp: 2 character human-readable-prefix
* id: Valid 4 char string identifying the secret
* threshold: Threshold according to the bip93
* seed: The secret in u8*
* seedlen: Length of the seed provided.
@ -51,6 +56,7 @@ struct codex32 *codex32_decode(const tal_t *ctx,
* Returns an error string, or returns NULL and sets @bip93.
*/
const char *codex32_secret_encode(const tal_t *ctx,
const char *hrp,
const char *id,
const u32 threshold,
const u8 *seed,

View File

@ -119,6 +119,19 @@ void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNE
{ fprintf(stderr, "towire_u8_array called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
/* Print the "cl" variant of the vector */
static void print_cl_vec(const char *desc, const struct codex32 *parts)
{
const char *err;
char *bip93;
err = codex32_secret_encode(tmpctx, "cl", parts->id, parts->threshold,
parts->payload, tal_bytelen(parts->payload),
&bip93);
assert(!err);
printf("%s: %s\n", desc, bip93);
}
int main(int argc, char *argv[])
{
common_setup(argv[0]);
@ -133,7 +146,7 @@ int main(int argc, char *argv[])
0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00,
};
assert(codex32_secret_encode(tmpctx, "leet", 0, seed_b, ARRAY_SIZE(seed_b), &c) == NULL);
assert(codex32_secret_encode(tmpctx, "ms", "leet", 0, seed_b, ARRAY_SIZE(seed_b), &c) == NULL);
assert(streq(c,
"ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqtum9pgv99ycma"));
@ -156,7 +169,7 @@ int main(int argc, char *argv[])
* * master node xprv: xprv9s21ZrQH143K3taPNekMd9oV5K6szJ8ND7vVh6fxicRUMDcChr3bFFzuxY8qP3xFFBL6DWc2uEYCfBFZ2nFWbAqKPhtCLRjgv78EZJDEfpL
*/
parts = codex32_decode(tmpctx, "ms10testsxxxxxxxxxxxxxxxxxxxxxxxxxx4nzvca9cmczlw", &fail);
parts = codex32_decode(tmpctx, NULL, "ms10testsxxxxxxxxxxxxxxxxxxxxxxxxxx4nzvca9cmczlw", &fail);
if (parts) {
assert(streq(parts->hrp, "ms"));
assert(parts->threshold == 0);
@ -164,6 +177,7 @@ int main(int argc, char *argv[])
assert(parts->share_idx == 's');
assert(streq(tal_hexstr(tmpctx, parts->payload, tal_bytelen(parts->payload)),
"318c6318c6318c6318c6318c6318c631"));
print_cl_vec("Test vector 1", parts);
} else {
abort();
}
@ -183,10 +197,11 @@ int main(int argc, char *argv[])
* * master node xprv: xprv9s21ZrQH143K2NkobdHxXeyFDqE44nJYvzLFtsriatJNWMNKznGoGgW5UMTL4fyWtajnMYb5gEc2CgaKhmsKeskoi9eTimpRv2N11THhPTU
*/
parts = codex32_decode(tmpctx, "MS12NAMES6XQGUZTTXKEQNJSJZV4JV3NZ5K3KWGSPHUH6EVW", &fail);
parts = codex32_decode(tmpctx, "ms", "MS12NAMES6XQGUZTTXKEQNJSJZV4JV3NZ5K3KWGSPHUH6EVW", &fail);
if(parts) {
assert(streq(tal_hexstr(tmpctx, parts->payload, tal_bytelen(parts->payload)), "d1808e096b35b209ca12132b264662a5"));
print_cl_vec("Test vector 2", parts);
} else {
abort();
}
@ -226,10 +241,11 @@ int main(int argc, char *argv[])
};
for (size_t i = 0; i < ARRAY_SIZE(addr_vec3); i++) {
parts = codex32_decode(tmpctx, addr_vec3[i], &fail);
parts = codex32_decode(tmpctx, NULL, addr_vec3[i], &fail);
if(parts) {
assert(streq(tal_hexstr(tmpctx, parts->payload, tal_bytelen(parts->payload)),
"ffeeddccbbaa99887766554433221100"));
print_cl_vec("Test vector 3", parts);
} else {
abort();
}
@ -283,10 +299,11 @@ int main(int argc, char *argv[])
};
for (size_t i = 0; i < ARRAY_SIZE(addr_vec4); i++) {
parts = codex32_decode(tmpctx, addr_vec4[i], &fail);
parts = codex32_decode(tmpctx, NULL, addr_vec4[i], &fail);
if (parts) {
assert(streq(tal_hexstr(tmpctx, parts->payload, tal_bytelen(parts->payload)),
"ffeeddccbbaa99887766554433221100ffeeddccbbaa99887766554433221100"));
print_cl_vec("Test vector 4", parts);
} else {
abort();
}
@ -305,10 +322,11 @@ int main(int argc, char *argv[])
*
*/
parts = codex32_decode(tmpctx, "MS100C8VSM32ZXFGUHPCHTLUPZRY9X8GF2TVDW0S3JN54KHCE6MUA7LQPZYGSFJD6AN074RXVCEMLH8WU3TK925ACDEFGHJKLMNPQRSTUVWXY06FHPV80UNDVARHRAK", &fail);
parts = codex32_decode(tmpctx, NULL, "MS100C8VSM32ZXFGUHPCHTLUPZRY9X8GF2TVDW0S3JN54KHCE6MUA7LQPZYGSFJD6AN074RXVCEMLH8WU3TK925ACDEFGHJKLMNPQRSTUVWXY06FHPV80UNDVARHRAK", &fail);
if (parts) {
assert(streq(tal_hexstr(tmpctx, parts->payload, tal_bytelen(parts->payload)),
"dc5423251cb87175ff8110c8531d0952d8d73e1194e95b5f19d6f9df7c01111104c9baecdfea8cccc677fb9ddc8aec5553b86e528bcadfdcc201c17c638c47e9"));
print_cl_vec("Test vector 5", parts);
} else {
abort();
}
@ -368,7 +386,7 @@ int main(int argc, char *argv[])
};
for (size_t i = 0; i < ARRAY_SIZE(addr_invalid); i++) {
parts = codex32_decode(tmpctx, addr_invalid[i], &fail);
parts = codex32_decode(tmpctx, NULL, addr_invalid[i], &fail);
if (parts) {
abort();
} else {
@ -405,7 +423,7 @@ int main(int argc, char *argv[])
};
for (size_t i = 0; i < ARRAY_SIZE(addr_invalid1); i++) {
parts = codex32_decode(tmpctx, addr_invalid1[i], &fail);
parts = codex32_decode(tmpctx, NULL, addr_invalid1[i], &fail);
if (parts) {
printf("payload == %ld\n", tal_bytelen(parts->payload));
abort();
@ -453,7 +471,7 @@ int main(int argc, char *argv[])
};
for (size_t i = 0; i < ARRAY_SIZE(addr_invalid2); i++) {
parts = codex32_decode(tmpctx, addr_invalid2[i], &fail);
parts = codex32_decode(tmpctx, NULL, addr_invalid2[i], &fail);
if (parts) {
printf("payload %ld\n", tal_bytelen(parts->payload));
abort();
@ -469,7 +487,7 @@ int main(int argc, char *argv[])
* * ms10fauxxxxxxxxxxxxxxxxxxxxxxxxxxxx0z26tfn0ulw3p
*/
parts = codex32_decode(tmpctx, "ms10fauxxxxxxxxxxxxxxxxxxxxxxxxxxxx0z26tfn0ulw3p", &fail);
parts = codex32_decode(tmpctx, NULL, "ms10fauxxxxxxxxxxxxxxxxxxxxxxxxxxxx0z26tfn0ulw3p", &fail);
if (parts) {
abort();
} else {
@ -482,7 +500,7 @@ int main(int argc, char *argv[])
* * ms1fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxda3kr3s0s2swg
*/
parts = codex32_decode(tmpctx, "ms1fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxda3kr3s0s2swg", &fail);
parts = codex32_decode(tmpctx, NULL, "ms1fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxda3kr3s0s2swg", &fail);
if (parts) {
abort();
} else {
@ -516,11 +534,11 @@ int main(int argc, char *argv[])
};
for (size_t i = 0; i < ARRAY_SIZE(addr_invalid3); i++) {
parts = codex32_decode(tmpctx, addr_invalid3[i], &fail);
parts = codex32_decode(tmpctx, "ms", addr_invalid3[i], &fail);
if (parts) {
abort();
} else {
assert(streq(fail, "Invalid HRP!") ||
assert(strstr(fail, "Invalid hrp ") ||
streq(fail, "Separator doesn't exist!"));
}
tal_free(parts);
@ -548,7 +566,7 @@ int main(int argc, char *argv[])
};
for (size_t i = 0; i < ARRAY_SIZE(addr_invalid4); i++) {
parts = codex32_decode(tmpctx, addr_invalid4[i], &fail);
parts = codex32_decode(tmpctx, NULL, addr_invalid4[i], &fail);
if (parts) {
abort();
} else {

View File

@ -64,7 +64,7 @@ This hook is called whenever the node is started using the --recovery flag. So b
The payload consists of the following information:
```json
{
"codex32": "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqtum9pgv99ycma"
"codex32": "cl10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqjdsjnzedu43ns"
}
```
@ -628,4 +628,4 @@ The payload for a call follows this format:
All fields shown here are optional.
We suggest just returning `{'result': 'continue'}`; any other result will cause the message not to be handed to any other hooks.
We suggest just returning `{'result': 'continue'}`; any other result will cause the message not to be handed to any other hooks.

View File

@ -1275,7 +1275,7 @@ static char *opt_set_announce_dns(const char *optarg, struct lightningd *ld)
static char *opt_set_codex32(const char *arg, struct lightningd *ld)
{
char *err;
struct codex32 *parts = codex32_decode(tmpctx, arg, &err);
struct codex32 *parts = codex32_decode(tmpctx, "cl", arg, &err);
if (!parts) {
return err;

View File

@ -1350,14 +1350,14 @@ def test_recover(node_factory, bitcoind):
"""
# Start the node with --recovery with valid codex32 secret
l1 = node_factory.get_node(start=False,
options={"recover": "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqtum9pgv99ycma"})
options={"recover": "cl10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqjdsjnzedu43ns"})
os.unlink(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret"))
l1.daemon.start()
cmd_line = ["tools/hsmtool", "getcodexsecret", os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")]
out = subprocess.check_output(cmd_line + ["leet", "0"]).decode('utf-8')
assert out == "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqtum9pgv99ycma\n"
assert out == "cl10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqjdsjnzedu43ns\n"
# Check bad ids.
out = subprocess.run(cmd_line + ["lee", "0"], stderr=subprocess.PIPE, timeout=TIMEOUT)
@ -1388,7 +1388,7 @@ def test_recover(node_factory, bitcoind):
os.unlink(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "lightningd.sqlite3"))
# Node should throw error to recover flag if HSM already exists.
l1.daemon.opts['recover'] = "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqtum9pgv99ycma"
l1.daemon.opts['recover'] = "cl10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqjdsjnzedu43ns"
l1.daemon.start(wait_for_initialized=False, stderr_redir=True)
# Will exit with failure code.
@ -1397,10 +1397,10 @@ def test_recover(node_factory, bitcoind):
os.unlink(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret"))
l1.daemon.opts.update({"recover": "MS12NAMES6XQGUZTTXKEQNJSJZV4JV3NZ5K3KWGSPHUH6EVW"})
l1.daemon.opts.update({"recover": "CL10LEETSLLHDMN9M42VCSAMX24ZRXGS3QQAT3LTDVAKMT73"})
l1.daemon.start(wait_for_initialized=False, stderr_redir=True)
assert l1.daemon.wait() == 1
assert l1.daemon.is_in_stderr(r"Expected 32 Byte secret: d1808e096b35b209ca12132b264662a5")
assert l1.daemon.is_in_stderr(r"Expected 32 Byte secret: ffeeddccbbaa99887766554433221100")
l1.daemon.opts.pop("recover")
l1.start()

View File

@ -254,7 +254,7 @@ static int make_codexsecret(const char *hsm_secret_path,
const char *err;
get_hsm_secret(&hsm_secret, hsm_secret_path);
err = codex32_secret_encode(tmpctx, id, 0, hsm_secret.data, 32, &bip93);
err = codex32_secret_encode(tmpctx, "cl", id, 0, hsm_secret.data, 32, &bip93);
if (err)
errx(ERROR_USAGE, "%s", err);