diff --git a/devtools/.gitignore b/devtools/.gitignore index 9787bdc74..0266f1158 100644 --- a/devtools/.gitignore +++ b/devtools/.gitignore @@ -20,3 +20,4 @@ topology fp16 rune gossmap-compress +bip137-verifysignature diff --git a/devtools/Makefile b/devtools/Makefile index d7f86f9d7..bdceecc7e 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -1,4 +1,4 @@ -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/mkquery devtools/lightning-checkmessage devtools/topology devtools/route devtools/bolt12-cli devtools/encodeaddr devtools/features devtools/fp16 devtools/rune devtools/gossmap-compress +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/mkquery devtools/lightning-checkmessage devtools/topology devtools/route devtools/bolt12-cli devtools/encodeaddr devtools/features devtools/fp16 devtools/rune devtools/gossmap-compress devtools/bip137-verifysignature ifeq ($(HAVE_SQLITE3),1) DEVTOOLS += devtools/checkchannels endif @@ -99,6 +99,8 @@ devtools/mkquery: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/t devtools/lightning-checkmessage: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/lightning-checkmessage.o +devtools/bip137-verifysignature: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/bip137-verifysignature.o + devtools/route: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/tlvstream.o common/gossmap.o common/fp16.o common/random_select.o common/route.o common/dijkstra.o devtools/clean_topo.o devtools/route.o devtools/topology: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/tlvstream.o common/gossmap.o common/fp16.o common/random_select.o common/dijkstra.o common/route.o devtools/clean_topo.o devtools/topology.o diff --git a/devtools/bip137-verifysignature.c b/devtools/bip137-verifysignature.c new file mode 100644 index 000000000..48db7b95d --- /dev/null +++ b/devtools/bip137-verifysignature.c @@ -0,0 +1,114 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void usage(void) +{ + fprintf(stderr, + "Usage: bip137-verifysignature message hex-sig [address] [network]\n" + "If key does not match, signature is not valid!\n"); + exit(1); +} + +static char *encode_pubkey_to_p2wpkh_addr(const tal_t *ctx, + const struct pubkey *pubkey, + const struct chainparams *chain) +{ + char *out; + const char *hrp; + struct ripemd160 h160; + bool ok; + hrp = chain->onchain_hrp; + + /* out buffer is 73 + strlen(human readable part), + * see common/bech32.h*/ + out = tal_arr(ctx, char, 73 + strlen(hrp)); + pubkey_to_hash160(pubkey, &h160); + ok = segwit_addr_encode(out, hrp, 0, h160.u.u8, sizeof(h160)); + if(!ok) + tal_free(out); + return out; +} + +int main(int argc, char *argv[]) +{ + u8 *sig; + u8 varint[VARINT_MAX_LEN]; + size_t varintlen, msg_len; + secp256k1_ecdsa_recoverable_signature rsig; + struct sha256_ctx sctx = SHA256_INIT; + struct sha256_double shad; + struct pubkey reckey; + const char *addr; + const struct chainparams *chain = NULL; + const char *input_chain = NULL, *input_address = NULL; + + setup_locale(); + err_set_progname(argv[0]); + wally_init(0); + secp256k1_ctx = wally_get_secp_context(); + + if (argc != 3 && argc != 4 && argc != 5) + usage(); + if (argc > 3) + input_address = argv[3]; + if (argc > 4) + input_chain = argv[4]; + + sig = tal_hexdata(NULL, argv[2], strlen(argv[2])); + if (!sig) + errx(1, "Not a valid hex string"); + + if (sig[0] < 39 || sig[0] >= 43) + errx(1, + "Signature header does not correspond to a P2WPKH type"); + + if (!secp256k1_ecdsa_recoverable_signature_parse_compact( + secp256k1_ctx, &rsig, sig + 1, sig[0] - 39)) + errx(1, "Signature not parsable"); + + sha256_update(&sctx, + "\x18" + "Bitcoin Signed Message:\n", + strlen("\x18" + "Bitcoin Signed Message:\n")); + msg_len = strlen(argv[1]); + varintlen = varint_put(varint, msg_len); + sha256_update(&sctx, varint, varintlen); + sha256_update(&sctx, argv[1], msg_len); + sha256_double_done(&sctx, &shad); + + if (!secp256k1_ecdsa_recover(secp256k1_ctx, &reckey.pubkey, &rsig, + shad.sha.u.u8)) + errx(1, "Signature not recoverable"); + + if (input_chain) { + chain = chainparams_for_network(input_chain); + if (!chain) + errx(1, "Invalid network"); + } else { + /* By default, assume we are verifying a mainnet signature. */ + chain = chainparams_for_network("bitcoin"); + } + addr = encode_pubkey_to_p2wpkh_addr(NULL, &reckey, chain); + if (!addr) + errx(1, "Failed to derive address from recovered key"); + if (input_address) { + if (!streq(addr, input_address)) + errx(1, "Signature is invalid"); + printf("Signature is valid!\n"); + } else + printf("Signature claims to be from address %s\n", addr); + return 0; +} +