diff --git a/common/codex32.c b/common/codex32.c index b611c7484..78a137477 100644 --- a/common/codex32.c +++ b/common/codex32.c @@ -166,6 +166,13 @@ static void input_data_str(u8 *generator, u8 *residue, const char *datastr, size return; } +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); + } +} + /* Helper to verify codex32 checksum */ static bool checksum_verify(const char *hrp, const char *codex_datastr, const struct checksum_engine *initial_engine) @@ -178,6 +185,19 @@ static bool checksum_verify(const char *hrp, const char *codex_datastr, return memcmp(engine.target, engine.residue, engine.len) == 0; } +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]]; +} + /* Pull len chars from cursor into dst. */ static bool pull_chars(char *dst, size_t len, const char **cursor, size_t *max) { @@ -374,3 +394,47 @@ struct codex32 *codex32_decode(const tal_t *ctx, return parts; } + +/* Returns Codex32 encoded secret of the seed provided. */ +char *codex32_secret_encode(const tal_t *ctx, + const char *id, + const u32 threshold, + const u8 *seed, + size_t seedlen) +{ + const struct checksum_engine *csum_engine; + const char *hrp = "ms"; + assert(threshold <= 9 && threshold >= 0 && + threshold != 1 && strlen(id) == 4); + + /* Every codex32 has hrp `ms` and since we are generating a + * secret it's share index would be `s` and threshold given by user. */ + char *ret = tal_fmt(ctx, "%s1%d%ss", hrp, threshold, id); + + 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); + + tal_append_fmt(&ret, "%c", bech32_charset[u5]); + 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) { + tal_append_fmt(&ret, "%c", bech32_charset[next_u5 >> (rem - 2)]); + next_u5 &= (1 << (rem - 2)) - 1; + } + rem = (rem + 8) % 5; + } + if(rem > 0) { + tal_append_fmt(&ret, "%c", bech32_charset[next_u5 << (5 - rem)]); + } + + csum_engine = &initial_engine_csum[seedlen >= 51]; + char csum[csum_engine->len]; + calculate_checksum(hrp, csum, ret + 3, csum_engine); + tal_append_fmt(&ret, "%.*s", (int)csum_engine->len, csum); + return ret; +} diff --git a/common/codex32.h b/common/codex32.h index b43018870..0c5812ac3 100644 --- a/common/codex32.h +++ b/common/codex32.h @@ -40,4 +40,20 @@ struct codex32 *codex32_decode(const tal_t *ctx, const char *codex32str, char **fail); +/** Encode a seed into codex32 secret format. + * + * Out: char *: String containing the codex32 (BIP93) format secret. + * fail: Asserting error if invalid threshold is used. + * In: input: 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. + * Returns a string which contains the seed provided in bip93 format. + */ +char *codex32_secret_encode(const tal_t *ctx, + const char *id, + const u32 threshold, + const u8 *seed, + size_t seedlen); + #endif /* LIGHTNING_COMMON_CODEX32_H */ diff --git a/common/test/run-codex32.c b/common/test/run-codex32.c index 74c94b76f..9c946df9f 100644 --- a/common/test/run-codex32.c +++ b/common/test/run-codex32.c @@ -125,6 +125,18 @@ int main(int argc, char *argv[]) char *fail; struct codex32 *parts; + /* Test vector for codex32_secret_encode*/ + u8 seed_b[32] = { + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, + }; + + char *c = codex32_secret_encode(tmpctx, "leet", 0, seed_b, ARRAY_SIZE(seed_b)); + assert(streq(c, + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqtum9pgv99ycma")); + /* * Test vector 1 *