2023-07-31 01:36:38 +02:00
|
|
|
/* Implementation of BIP-93 "codex32: Checksummed SSSS-aware BIP32 seeds".
|
|
|
|
*
|
|
|
|
* There are two representations, short and long:
|
|
|
|
*
|
|
|
|
* CODEX32 := HRP "1" SHORT-DATA | LONG-DATA
|
|
|
|
* HRP := "ms" | "MS"
|
|
|
|
* SHORT-DATA := THRESHOLD IDENTIFIER SHAREINDEX SHORT-PAYLOAD SHORT-CHECKSUM
|
|
|
|
* LONG-DATA := THRESHOLD IDENTIFIER SHAREINDEX LONG-PAYLOAD LONG-CHECKSUM
|
|
|
|
*
|
|
|
|
* THRESHOLD = "0" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
|
|
|
* IDENTIFER := BECH32*4
|
|
|
|
* SHAREINDEX := BECH32
|
|
|
|
*
|
|
|
|
* SHORT-PAYLOAD := BECH32 [0 - 74 times]
|
|
|
|
* SHORT-CHECKSUM := BECH32*13
|
|
|
|
*
|
|
|
|
* LONG-PAYLOAD := BECH32 [75 - 103 times]
|
|
|
|
* LONG-CHECKSUM := BECH32*15
|
|
|
|
*
|
|
|
|
* Thus, a short codex32 string has 22 bytes of non-payload, so 22 to 96 characters long.
|
|
|
|
* A long codex32 string has 24 bytes of non-payload, so 99 to 127 characters.
|
|
|
|
*/
|
2023-07-31 01:33:33 +02:00
|
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#include <bitcoin/chainparams.h>
|
|
|
|
#include <ccan/array_size/array_size.h>
|
|
|
|
#include <ccan/mem/mem.h>
|
|
|
|
#include <ccan/tal/str/str.h>
|
|
|
|
#include <common/bech32.h>
|
|
|
|
#include <common/bech32_util.h>
|
|
|
|
#include <common/bolt12.h>
|
|
|
|
#include <common/bolt12_merkle.h>
|
|
|
|
#include <common/codex32.h>
|
|
|
|
#include <common/configdir.h>
|
|
|
|
#include <common/features.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <secp256k1_schnorrsig.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
2023-07-31 01:33:33 +02:00
|
|
|
struct checksum_engine {
|
|
|
|
u8 generator[15];
|
|
|
|
u8 residue[15];
|
|
|
|
u8 target[15];
|
|
|
|
size_t len;
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
size_t max_payload_len;
|
2023-07-31 01:33:33 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct checksum_engine initial_engine_csum[] = {
|
|
|
|
/* Short Codex32 Engine */
|
|
|
|
{
|
|
|
|
{
|
|
|
|
25, 27, 17, 8, 0, 25,
|
|
|
|
25, 25, 31, 27, 24, 16,
|
|
|
|
16,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0,
|
|
|
|
1,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
16, 25, 24, 3, 25, 11,
|
|
|
|
16, 23, 29, 3, 25, 17,
|
|
|
|
10,
|
|
|
|
},
|
|
|
|
13,
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
74,
|
2023-07-31 01:33:33 +02:00
|
|
|
},
|
|
|
|
/* Long Codex32 Engine */
|
|
|
|
{
|
|
|
|
{
|
|
|
|
15, 10, 25, 26, 9, 25,
|
|
|
|
21, 6, 23, 21, 6, 5,
|
|
|
|
22, 4, 23
|
|
|
|
},
|
|
|
|
{
|
|
|
|
0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
16, 25, 24, 3, 25, 11,
|
|
|
|
16, 23, 29, 3, 25, 17,
|
|
|
|
10, 25, 6
|
|
|
|
},
|
|
|
|
15,
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
103,
|
2023-07-31 01:33:33 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static const uint8_t logi[32] =
|
|
|
|
{
|
|
|
|
0, 0, 1, 14, 2, 28, 15, 22,
|
|
|
|
3, 5, 29, 26, 16, 7, 23, 11,
|
|
|
|
4, 25, 6, 10, 30, 13, 27, 21,
|
|
|
|
17, 18, 8, 19, 24, 9, 12, 20,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const uint8_t log_inv[31] =
|
|
|
|
{
|
|
|
|
1, 2, 4, 8, 16, 9, 18, 13,
|
|
|
|
26, 29, 19, 15, 30, 21, 3, 6,
|
|
|
|
12, 24, 25, 27, 31, 23, 7, 14,
|
|
|
|
28, 17, 11, 22, 5, 10, 20,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void addition_gf32(uint8_t *x, uint8_t y)
|
|
|
|
{
|
|
|
|
*x = *x ^ y;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void multiply_gf32(uint8_t *x, uint8_t y)
|
|
|
|
{
|
|
|
|
if (*x == 0 || y == 0) {
|
|
|
|
*x = 0;
|
|
|
|
} else {
|
|
|
|
*x = log_inv[(logi[*x] + logi[y]) % 31];
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Helper to input a single field element in the checksum engine. */
|
2023-07-31 01:36:36 +02:00
|
|
|
static void input_fe(const u8 *generator, u8 *residue, uint8_t e, size_t len)
|
2023-07-31 01:33:33 +02:00
|
|
|
{
|
|
|
|
size_t res_len = len;
|
|
|
|
u8 xn = residue[0];
|
|
|
|
|
|
|
|
for(size_t i = 1; i < res_len; i++) {
|
|
|
|
residue[i - 1] = residue[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
residue[res_len - 1] = e;
|
|
|
|
|
|
|
|
for(size_t i = 0; i < res_len; i++) {
|
|
|
|
u8 x = generator[i];
|
|
|
|
multiply_gf32(&x, xn);
|
|
|
|
addition_gf32(&residue[i], x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Helper to input the HRP of codex32 string into the checksum engine */
|
2023-07-31 01:36:36 +02:00
|
|
|
static void input_hrp(const u8 *generator, u8 *residue, const char *hrp, size_t len)
|
2023-07-31 01:33:33 +02:00
|
|
|
{
|
|
|
|
size_t i = 0;
|
|
|
|
for (i = 0; i < strlen(hrp); i++) {
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
input_fe(generator, residue, hrp[i] >> 5, len);
|
2023-07-31 01:33:33 +02:00
|
|
|
}
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
input_fe(generator, residue, hrp[i] >> 0, len);
|
2023-07-31 01:33:33 +02:00
|
|
|
for (i = 0; i < strlen(hrp); i++) {
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
input_fe(generator, residue, hrp[i] & 0x1f, len);
|
2023-07-31 01:33:33 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Helper to input data strong of codex32 into the checksum engine. */
|
2023-07-31 01:36:36 +02:00
|
|
|
static void input_data_str(u8 *generator, u8 *residue, const char *datastr, size_t len)
|
2023-07-31 01:33:33 +02:00
|
|
|
{
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < strlen(datastr); i++) {
|
|
|
|
input_fe(generator, residue, bech32_charset_rev[(int)datastr[i]], len);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-31 06:10:15 +02:00
|
|
|
static void input_own_target(const u8 *generator, u8 *residue, const u8 *target, size_t len)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
|
|
input_fe(generator, residue, target[i], len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-31 01:33:33 +02:00
|
|
|
/* Helper to verify codex32 checksum */
|
2023-07-31 01:36:36 +02:00
|
|
|
static bool checksum_verify(const char *hrp, const char *codex_datastr,
|
|
|
|
const struct checksum_engine *initial_engine)
|
2023-07-31 01:33:33 +02:00
|
|
|
{
|
2023-07-31 01:36:36 +02:00
|
|
|
struct checksum_engine engine = *initial_engine;
|
2023-07-31 01:33:33 +02:00
|
|
|
|
2023-07-31 01:36:36 +02:00
|
|
|
input_hrp(engine.generator, engine.residue ,hrp, engine.len);
|
|
|
|
input_data_str(engine.generator, engine.residue, codex_datastr, engine.len);
|
2023-07-31 01:33:33 +02:00
|
|
|
|
2023-07-31 01:36:36 +02:00
|
|
|
return memcmp(engine.target, engine.residue, engine.len) == 0;
|
2023-07-31 01:33:33 +02:00
|
|
|
}
|
2023-07-31 01:33:33 +02:00
|
|
|
|
2023-07-31 06:10:15 +02:00
|
|
|
static void calculate_checksum(const char *hrp, char *csum, const char *codex_datastr,
|
|
|
|
const struct checksum_engine *initial_engine)
|
|
|
|
{
|
|
|
|
struct checksum_engine engine = *initial_engine;
|
|
|
|
|
|
|
|
input_hrp(engine.generator, engine.residue, hrp, engine.len);
|
|
|
|
input_data_str(engine.generator, engine.residue, codex_datastr, engine.len);
|
|
|
|
input_own_target(engine.generator, engine.residue, engine.target, engine.len);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < engine.len; i++)
|
|
|
|
csum[i] = bech32_charset[engine.residue[i]];
|
|
|
|
}
|
|
|
|
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
/* Pull len chars from cursor into dst. */
|
|
|
|
static bool pull_chars(char *dst, size_t len, const char **cursor, size_t *max)
|
2023-07-31 01:33:33 +02:00
|
|
|
{
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
if (*max < len)
|
|
|
|
return false;
|
|
|
|
memcpy(dst, *cursor, len);
|
2023-07-31 01:33:33 +02:00
|
|
|
*cursor += len;
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
*max -= len;
|
|
|
|
return true;
|
2023-07-31 01:33:33 +02:00
|
|
|
}
|
|
|
|
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
/* Truncate length of *cursor (i.e. trim from end) */
|
|
|
|
static bool trim_chars(size_t len, const char **cursor, size_t *max)
|
2023-07-31 01:33:33 +02:00
|
|
|
{
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
if (*max < len)
|
|
|
|
return false;
|
|
|
|
*max -= len;
|
|
|
|
return true;
|
2023-07-31 01:33:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Helper to fetch data from payload as a valid hex buffer */
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
static const u8 *decode_payload(const tal_t *ctx, const char *payload, size_t payload_len)
|
2023-07-31 01:33:33 +02:00
|
|
|
{
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
u8 *ret = tal_arr(ctx, u8, (payload_len * 5 + 7) / 8);
|
2023-07-31 01:33:33 +02:00
|
|
|
uint8_t next_byte = 0;
|
|
|
|
uint8_t rem = 0;
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
size_t j = 0;
|
|
|
|
|
|
|
|
/* We have already checked this is a valid bech32 string! */
|
|
|
|
for (size_t i = 0; i < payload_len; i++) {
|
|
|
|
int ch = payload[i];
|
|
|
|
uint8_t fe = bech32_charset_rev[ch];
|
2023-07-31 01:33:33 +02:00
|
|
|
|
|
|
|
if (rem < 3) {
|
|
|
|
// If we are within 3 bits of the start we can fit the whole next char in
|
|
|
|
next_byte |= fe << (3 - rem);
|
|
|
|
}
|
|
|
|
else if (rem == 3) {
|
|
|
|
// If we are exactly 3 bits from the start then this char fills in the byte
|
|
|
|
ret[j++] = next_byte | fe;
|
|
|
|
next_byte = 0;
|
|
|
|
}
|
|
|
|
else { // rem > 3
|
|
|
|
// Otherwise we have to break it in two
|
|
|
|
u8 overshoot = rem - 3;
|
|
|
|
assert(overshoot > 0);
|
|
|
|
ret[j++] = next_byte | (fe >> overshoot);
|
|
|
|
next_byte = fe << (8 - overshoot);
|
|
|
|
}
|
|
|
|
|
|
|
|
rem = (rem + 5) % 8;
|
|
|
|
}
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
|
|
|
|
/* BIP-93:
|
|
|
|
* Any incomplete group at the end MUST be 4 bits or less, and is discarded.
|
|
|
|
*/
|
|
|
|
if (rem > 4)
|
|
|
|
return tal_free(ret);
|
|
|
|
|
|
|
|
/* As a result, we often don't use the final byte */
|
|
|
|
tal_resize(&ret, j);
|
2023-07-31 01:33:33 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
/* Checks case inconsistency, and for non-bech32 chars. */
|
|
|
|
static const char *bech32_case_fixup(const tal_t *ctx,
|
|
|
|
const char *codex32str,
|
|
|
|
const char **sep)
|
2023-07-31 01:33:33 +02:00
|
|
|
{
|
|
|
|
size_t str_len = strlen(codex32str);
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
char *was_upper_str;
|
|
|
|
|
|
|
|
*sep = NULL;
|
|
|
|
|
|
|
|
/* If first is upper, lower-case the rest */
|
|
|
|
if (cisupper(codex32str[0])) {
|
|
|
|
/* We need a non-const str var, and a flag */
|
|
|
|
was_upper_str = tal_strdup(ctx, codex32str);
|
|
|
|
codex32str = was_upper_str;
|
|
|
|
} else {
|
|
|
|
was_upper_str = NULL;
|
|
|
|
}
|
|
|
|
|
2023-07-31 01:33:33 +02:00
|
|
|
for (size_t i = 0; i < str_len; i++) {
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
int c = codex32str[i];
|
|
|
|
if (c == '1') {
|
|
|
|
/* Two separators? */
|
|
|
|
if (*sep)
|
|
|
|
goto fail;
|
|
|
|
*sep = codex32str + i;
|
|
|
|
continue;
|
2023-07-31 01:33:33 +02:00
|
|
|
}
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
if (c < 0 || c > 128)
|
|
|
|
goto fail;
|
|
|
|
if (was_upper_str) {
|
|
|
|
/* Mixed case not allowed! */
|
|
|
|
if (cislower(c))
|
|
|
|
goto fail;
|
|
|
|
was_upper_str[i] = c = tolower(c);
|
|
|
|
} else {
|
|
|
|
if (cisupper(c))
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (bech32_charset_rev[c] == -1)
|
|
|
|
goto fail;
|
2023-07-31 01:33:33 +02:00
|
|
|
}
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
|
|
|
|
return codex32str;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return tal_free(was_upper_str);
|
2023-07-31 01:33:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return NULL if the codex32 is invalid */
|
|
|
|
struct codex32 *codex32_decode(const tal_t *ctx,
|
2023-08-02 04:57:43 +02:00
|
|
|
const char *hrp,
|
2023-07-31 01:36:36 +02:00
|
|
|
const char *codex32str,
|
|
|
|
char **fail)
|
2023-07-31 01:33:33 +02:00
|
|
|
{
|
|
|
|
struct codex32 *parts = tal(ctx, struct codex32);
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
const char *sep, *codex_datastr;
|
|
|
|
char threshold_char;
|
|
|
|
size_t maxlen;
|
2023-07-31 01:36:36 +02:00
|
|
|
const struct checksum_engine *csum_engine;
|
2023-07-31 01:33:33 +02:00
|
|
|
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
/* Lowercase it all, iff it's all uppercase. */
|
|
|
|
codex32str = bech32_case_fixup(tmpctx, codex32str, &sep);
|
|
|
|
if (!codex32str) {
|
|
|
|
*fail = tal_fmt(ctx, "Not a valid bech32 string!");
|
2023-07-31 01:33:33 +02:00
|
|
|
return tal_free(parts);
|
|
|
|
}
|
|
|
|
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
if (!sep) {
|
|
|
|
*fail = tal_fmt(ctx, "Separator doesn't exist!");
|
2023-07-31 01:33:33 +02:00
|
|
|
return tal_free(parts);
|
|
|
|
}
|
|
|
|
|
2023-07-31 01:36:36 +02:00
|
|
|
parts->hrp = tal_strndup(parts, codex32str, sep - codex32str);
|
2023-08-02 04:57:43 +02:00
|
|
|
if (hrp && !streq(parts->hrp, hrp)) {
|
|
|
|
*fail = tal_fmt(ctx, "Invalid hrp %s!", parts->hrp);
|
2023-07-31 01:33:33 +02:00
|
|
|
return tal_free(parts);
|
|
|
|
}
|
|
|
|
|
2023-07-31 01:36:36 +02:00
|
|
|
codex_datastr = sep + 1;
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
maxlen = strlen(codex_datastr);
|
|
|
|
|
|
|
|
/* If it's short, use short checksum engine. If it's invalid,
|
|
|
|
* use short checksum and we'll fail when payload is too long. */
|
|
|
|
csum_engine = &initial_engine_csum[maxlen >= 96];
|
|
|
|
if (!checksum_verify(parts->hrp, codex_datastr, csum_engine)) {
|
|
|
|
*fail = tal_fmt(ctx, "Invalid checksum!");
|
|
|
|
return tal_free(parts);
|
2023-07-31 01:33:33 +02:00
|
|
|
}
|
|
|
|
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
/* Pull fixed parts and discard checksum */
|
|
|
|
if (!pull_chars(&threshold_char, 1, &codex_datastr, &maxlen)
|
|
|
|
|| !pull_chars(parts->id, ARRAY_SIZE(parts->id) - 1, &codex_datastr, &maxlen)
|
|
|
|
|| !pull_chars(&parts->share_idx, 1, &codex_datastr, &maxlen)
|
|
|
|
|| !trim_chars(csum_engine->len, &codex_datastr, &maxlen)) {
|
|
|
|
*fail = tal_fmt(ctx, "Too short!");
|
|
|
|
return tal_free(parts);
|
|
|
|
}
|
|
|
|
parts->id[ARRAY_SIZE(parts->id)-1] = 0;
|
|
|
|
/* Is payload too long for this checksum? */
|
|
|
|
if (maxlen > csum_engine->max_payload_len) {
|
2023-07-31 01:33:33 +02:00
|
|
|
*fail = tal_fmt(ctx, "Invalid length!");
|
|
|
|
return tal_free(parts);
|
|
|
|
}
|
|
|
|
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
parts->payload = decode_payload(parts, codex_datastr, maxlen);
|
|
|
|
if (!parts->payload) {
|
|
|
|
*fail = tal_fmt(ctx, "Invalid payload!");
|
2023-07-31 01:33:33 +02:00
|
|
|
return tal_free(parts);
|
|
|
|
}
|
2023-07-31 01:33:33 +02:00
|
|
|
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
if (parts->share_idx == 's') {
|
2023-07-31 01:33:33 +02:00
|
|
|
parts->type = CODEX32_ENCODING_SECRET;
|
|
|
|
} else {
|
|
|
|
parts->type = CODEX32_ENCODING_SHARE;
|
|
|
|
}
|
|
|
|
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
parts->threshold = threshold_char - '0';
|
|
|
|
if (parts->threshold > 9 ||
|
|
|
|
parts->threshold < 0 ||
|
|
|
|
/* Can't happen because bech32 `1` is invalid, but worth noting */
|
|
|
|
parts->threshold == 1) {
|
|
|
|
*fail = tal_fmt(ctx, "Invalid threshold!");
|
2023-07-31 01:33:33 +02:00
|
|
|
return tal_free(parts);
|
codex32: rework.
Firstly, I wanted the results easier to use:
1. Make them always lower case, even if the string was UPPER.
2. Decode the payload for them.
3. Don't give the user any fields they don't need, and make
the field sizes explicit.
Secondly, I wanted to avoid the pattern of "check in one place, assume
in another", in favour of "check on use".
So, I changed the code to lower the string if it needs to at the start,
and then changed the pull functions so we always use them to get data:
this way we should fail clearly and gracefully if we don't have enough data.
I made all the checks explicit, where we assign the fields.
I also addressed the FIXME: I think the array is *often* one shorter,
but not always, so I trim the last byte at the end if needed.
[ Aditya modified the tests to work ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-07-31 01:37:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (parts->threshold == 0 && parts->type != CODEX32_ENCODING_SECRET) {
|
|
|
|
*fail = tal_fmt(ctx, "Expected share index s for threshold 0!");
|
|
|
|
return tal_free(parts);
|
|
|
|
}
|
2023-07-31 01:33:33 +02:00
|
|
|
|
|
|
|
return parts;
|
|
|
|
}
|
2023-07-31 06:10:15 +02:00
|
|
|
|
|
|
|
/* Returns Codex32 encoded secret of the seed provided. */
|
2023-08-01 03:29:54 +02:00
|
|
|
const char *codex32_secret_encode(const tal_t *ctx,
|
2023-08-02 04:57:43 +02:00
|
|
|
const char *hrp,
|
2023-08-01 03:29:54 +02:00
|
|
|
const char *id,
|
|
|
|
const u32 threshold,
|
|
|
|
const u8 *seed,
|
|
|
|
size_t seedlen,
|
|
|
|
char **bip93)
|
2023-07-31 06:10:15 +02:00
|
|
|
{
|
|
|
|
const struct checksum_engine *csum_engine;
|
2023-08-02 04:57:43 +02:00
|
|
|
|
|
|
|
/* 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);
|
2023-08-01 03:29:54 +02:00
|
|
|
|
|
|
|
if (threshold > 9 || threshold < 0 || threshold == 1)
|
|
|
|
return tal_fmt(ctx, "Invalid threshold %u", threshold);
|
|
|
|
|
|
|
|
if (strlen(id) != 4)
|
|
|
|
return tal_fmt(ctx, "Invalid id: must be 4 characters");
|
|
|
|
|
|
|
|
for (size_t i = 0; id[i]; i++) {
|
|
|
|
s8 rev;
|
|
|
|
|
|
|
|
if (id[i] & 0x80)
|
|
|
|
return tal_fmt(ctx, "Invalid id: must be ASCII");
|
|
|
|
|
|
|
|
rev = bech32_charset_rev[(int)id[i]];
|
|
|
|
if (rev == -1)
|
|
|
|
return tal_fmt(ctx, "Invalid id: must be valid bech32 string");
|
|
|
|
if (bech32_charset[rev] != id[i])
|
|
|
|
return tal_fmt(ctx, "Invalid id: must be lower-case");
|
|
|
|
}
|
2023-07-31 06:10:15 +02:00
|
|
|
|
|
|
|
/* Every codex32 has hrp `ms` and since we are generating a
|
|
|
|
* secret it's share index would be `s` and threshold given by user. */
|
2023-08-01 03:29:54 +02:00
|
|
|
*bip93 = tal_fmt(ctx, "%s1%d%ss", hrp, threshold, id);
|
2023-07-31 06:10:15 +02:00
|
|
|
|
|
|
|
uint8_t next_u5 = 0, rem = 0;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < seedlen; i++) {
|
|
|
|
/* Each byte provides at least one u5. Push that. */
|
|
|
|
uint8_t u5 = (next_u5 << (5 - rem)) | seed[i] >> (3 + rem);
|
|
|
|
|
2023-08-01 03:29:54 +02:00
|
|
|
tal_append_fmt(bip93, "%c", bech32_charset[u5]);
|
2023-07-31 06:10:15 +02:00
|
|
|
next_u5 = seed[i] & ((1 << (3 + rem)) - 1);
|
|
|
|
|
|
|
|
/* If there were 2 or more bits from the last iteration, then
|
|
|
|
* this iteration will push *two* u5s. */
|
|
|
|
if(rem >= 2) {
|
2023-08-01 03:29:54 +02:00
|
|
|
tal_append_fmt(bip93, "%c", bech32_charset[next_u5 >> (rem - 2)]);
|
2023-07-31 06:10:15 +02:00
|
|
|
next_u5 &= (1 << (rem - 2)) - 1;
|
|
|
|
}
|
|
|
|
rem = (rem + 8) % 5;
|
|
|
|
}
|
|
|
|
if(rem > 0) {
|
2023-08-01 03:29:54 +02:00
|
|
|
tal_append_fmt(bip93, "%c", bech32_charset[next_u5 << (5 - rem)]);
|
2023-07-31 06:10:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
csum_engine = &initial_engine_csum[seedlen >= 51];
|
|
|
|
char csum[csum_engine->len];
|
2023-08-01 03:29:54 +02:00
|
|
|
calculate_checksum(hrp, csum, *bip93 + 3, csum_engine);
|
|
|
|
tal_append_fmt(bip93, "%.*s", (int)csum_engine->len, csum);
|
|
|
|
return NULL;
|
2023-07-31 06:10:15 +02:00
|
|
|
}
|