diff --git a/devtools/Makefile b/devtools/Makefile index cf5810c16..d2510aa09 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -1,6 +1,6 @@ DEVTOOLS_SRC := devtools/gen_print_wire.c devtools/gen_print_onion_wire.c devtools/print_wire.c DEVTOOLS_OBJS := $(DEVTOOLS_SRC:.c=.o) -DEVTOOLS := devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore devtools/gossipwith devtools/create-gossipstore devtools/mkcommit devtools/mkfunding devtools/mkclose devtools/mkgossip devtools/mkencoded devtools/checkchannels devtools/mkquery +DEVTOOLS := devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore devtools/gossipwith devtools/create-gossipstore devtools/mkcommit devtools/mkfunding devtools/mkclose devtools/mkgossip devtools/mkencoded devtools/checkchannels devtools/mkquery devtools/lightning-checkmessage DEVTOOLS_TOOL_SRC := $(DEVTOOLS:=.c) DEVTOOLS_TOOL_OBJS := $(DEVTOOLS_TOOL_SRC:.c=.o) @@ -80,6 +80,8 @@ devtools/checkchannels: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) com devtools/mkquery: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/tlvstream.o devtools/mkquery.o +devtools/lightning-checkmessage: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/tlvstream.o devtools/lightning-checkmessage.o + # Make sure these depend on everything. ALL_PROGRAMS += $(DEVTOOLS) ALL_OBJS += $(DEVTOOLS_OBJS) $(DEVTOOLS_TOOL_OBJS) diff --git a/devtools/lightning-checkmessage.c b/devtools/lightning-checkmessage.c new file mode 100644 index 000000000..acd8020fa --- /dev/null +++ b/devtools/lightning-checkmessage.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* These tables copied from zbase32 src: + * copyright 2002-2007 Zooko "Zooko" Wilcox-O'Hearn + * mailto:zooko@zooko.com + * + * Permission is hereby granted to any person obtaining a copy of this work to + * deal in this work without restriction (including the rights to use, modify, + * distribute, sublicense, and/or sell copies). + */ + +/* revchars: index into this table with the ASCII value of the char. The result is the value of that quintet. */ +static const u8 zbase32_revchars[]={ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 18, 255, 25, 26, 27, 30, 29, 7, 31, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 24, 1, 12, 3, 8, 5, 6, 28, 21, 9, 10, 255, 11, 2, 16, 13, 14, 4, 22, 17, 19, 255, 20, 15, 0, 23, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, }; + +static void usage(void) +{ + fprintf(stderr, "Usage: lightning-checkmessage message zbase32-sig [key]\n" + "If key does not match, signature is not valid!\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + u8 *u5sig, *u8sig; + size_t len; + secp256k1_ecdsa_recoverable_signature rsig; + struct sha256_ctx sctx = SHA256_INIT; + struct sha256_double shad; + struct pubkey reckey; + const char *keystr; + + setup_locale(); + err_set_progname(argv[0]); + wally_init(0); + secp256k1_ctx = wally_get_secp_context(); + + if (argc != 3 && argc != 4) + usage(); + + u5sig = tal_arr(NULL, u8, strlen(argv[2])); + for (size_t i = 0; argv[2][i]; i++) { + u5sig[i] = zbase32_revchars[(unsigned char)argv[2][i]]; + if (u5sig[i] > 31) + errx(1, "Not a valid zbase32 string"); + } + u8sig = tal_arr(NULL, u8, (strlen(argv[2]) * 5 + 7) / 8 + 1); + len = 0; + if (!bech32_convert_bits(u8sig, &len, 8, u5sig, strlen(argv[2]), 5, false)) + errx(1, "Invalid string"); + + if (len != 65) + errx(1, "Signature too %s", len < 65 ? "short" : "long"); + + if (!secp256k1_ecdsa_recoverable_signature_parse_compact(secp256k1_ctx, + &rsig, + u8sig + 1, + u8sig[0] - 31)) + errx(1, "Signature not parsable"); + + sha256_update(&sctx, "Lightning Signed Message:", + strlen("Lightning Signed Message:")); + sha256_update(&sctx, argv[1], strlen(argv[1])); + sha256_double_done(&sctx, &shad); + + if (!secp256k1_ecdsa_recover(secp256k1_ctx, &reckey.pubkey, &rsig, + shad.sha.u.u8)) + errx(1, "Signature not recoverable"); + + keystr = pubkey_to_hexstr(NULL, &reckey); + if (argv[3]) { + if (!streq(keystr, argv[3])) + errx(1, "Signature is invalid"); + printf("Signature is valid!\n"); + } else + printf("Signature claims to be from key %s\n", keystr); + return 0; +}