Skip to content

Commit db43d18

Browse files
committed
add devtool/bip137-verifysignature utility
To validate BIP137 signatures produced by core-lightning in tests. Changelog-None. Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
1 parent 72f91cd commit db43d18

3 files changed

Lines changed: 146 additions & 1 deletion

File tree

devtools/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ topology
2020
fp16
2121
rune
2222
gossmap-compress
23+
bip137-verifysignature

devtools/Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
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
1+
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
22
ifeq ($(HAVE_SQLITE3),1)
33
DEVTOOLS += devtools/checkchannels
44
endif
@@ -99,6 +99,8 @@ devtools/mkquery: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/t
9999

100100
devtools/lightning-checkmessage: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/lightning-checkmessage.o
101101

102+
devtools/bip137-verifysignature: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/bip137-verifysignature.o
103+
102104
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
103105

104106
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

devtools/bip137-verifysignature.c

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#include "config.h"
2+
#include <assert.h>
3+
#include <bitcoin/base58.h>
4+
#include <bitcoin/pubkey.h>
5+
#include <bitcoin/shadouble.h>
6+
#include <bitcoin/varint.h>
7+
#include <ccan/err/err.h>
8+
#include <common/addr.h>
9+
#include <common/bech32.h>
10+
#include <common/utils.h>
11+
#include <secp256k1_recovery.h>
12+
#include <stdio.h>
13+
#include <wally_core.h>
14+
15+
static void usage(void)
16+
{
17+
fprintf(stderr,
18+
"Usage: bip137-verifysignature message hex-sig [address]\n"
19+
"If key does not match, signature is not valid!\n");
20+
exit(1);
21+
}
22+
23+
static char *encode_pubkey_to_p2wpkh_addr(const tal_t *ctx,
24+
const struct pubkey *pubkey,
25+
const struct chainparams *chain)
26+
{
27+
char *out;
28+
const char *hrp;
29+
struct ripemd160 h160;
30+
bool ok;
31+
hrp = chain->onchain_hrp;
32+
33+
/* out buffer is 73 + strlen(human readable part),
34+
* see common/bech32.h*/
35+
out = tal_arr(ctx, char, 73 + strlen(hrp));
36+
pubkey_to_hash160(pubkey, &h160);
37+
ok = segwit_addr_encode(out, hrp, 0, h160.u.u8, sizeof(h160));
38+
if(!ok)
39+
tal_free(out);
40+
return out;
41+
}
42+
43+
static const struct chainparams *chainparams_from_address(const char *addr)
44+
{
45+
const struct chainparams *network[3];
46+
network[0] = chainparams_for_network("regtest");
47+
network[1] = chainparams_for_network("testnet");
48+
network[2] = chainparams_for_network("bitcoin");
49+
50+
/* Is it base58? */
51+
u8 addr_version;
52+
struct ripemd160 h160;
53+
if (ripemd160_from_base58(&addr_version, &h160, addr, strlen(addr))) {
54+
for (int i = 0; i < 3; i++) {
55+
if (addr_version == network[i]->p2sh_version ||
56+
addr_version == network[i]->p2pkh_version)
57+
return network[i];
58+
}
59+
return NULL;
60+
}
61+
62+
/* Is it bech32 or bech32m? */
63+
uint8_t data[84];
64+
char hrp_actual[84];
65+
size_t data_len;
66+
bech32_encoding enc =
67+
bech32_decode(hrp_actual, data, &data_len, addr, strlen(addr));
68+
if (enc == BECH32_ENCODING_NONE)
69+
return NULL;
70+
for (int i = 0; i < 3; i++)
71+
if (strcmp(network[i]->onchain_hrp, hrp_actual) == 0)
72+
return network[i];
73+
return NULL;
74+
}
75+
76+
int main(int argc, char *argv[])
77+
{
78+
u8 *sig;
79+
u8 varint[VARINT_MAX_LEN];
80+
size_t varintlen, msg_len;
81+
secp256k1_ecdsa_recoverable_signature rsig;
82+
struct sha256_ctx sctx = SHA256_INIT;
83+
struct sha256_double shad;
84+
struct pubkey reckey;
85+
const char *addr;
86+
const struct chainparams *chain;
87+
88+
setup_locale();
89+
err_set_progname(argv[0]);
90+
wally_init(0);
91+
secp256k1_ctx = wally_get_secp_context();
92+
93+
if (argc != 3 && argc != 4)
94+
usage();
95+
96+
sig = tal_hexdata(NULL, argv[2], strlen(argv[2]));
97+
if (!sig)
98+
errx(1, "Not a valid hex string");
99+
100+
if (sig[0] < 39 || sig[0] >= 43)
101+
errx(1,
102+
"Signature header does not correspond to a P2WPKH type");
103+
104+
if (!secp256k1_ecdsa_recoverable_signature_parse_compact(
105+
secp256k1_ctx, &rsig, sig + 1, sig[0] - 39))
106+
errx(1, "Signature not parsable");
107+
108+
sha256_update(&sctx,
109+
"\x18"
110+
"Bitcoin Signed Message:\n",
111+
strlen("\x18"
112+
"Bitcoin Signed Message:\n"));
113+
msg_len = strlen(argv[1]);
114+
varintlen = varint_put(varint, msg_len);
115+
sha256_update(&sctx, varint, varintlen);
116+
sha256_update(&sctx, argv[1], msg_len);
117+
sha256_double_done(&sctx, &shad);
118+
119+
if (!secp256k1_ecdsa_recover(secp256k1_ctx, &reckey.pubkey, &rsig,
120+
shad.sha.u.u8))
121+
errx(1, "Signature not recoverable");
122+
123+
if (argv[3]) {
124+
chain = chainparams_from_address(argv[3]);
125+
if (!chain)
126+
errx(1, "Invalid address");
127+
} else {
128+
/* By default, assume we are verifying a mainnet signature. */
129+
chain = chainparams_for_network("bitcoin");
130+
}
131+
addr = encode_pubkey_to_p2wpkh_addr(NULL, &reckey, chain);
132+
if (!addr)
133+
errx(1, "Failed to derive address from recovered key");
134+
if (argv[3]) {
135+
if (!streq(addr, argv[3]))
136+
errx(1, "Signature is invalid");
137+
printf("Signature is valid!\n");
138+
} else
139+
printf("Signature claims to be from address %s\n", addr);
140+
return 0;
141+
}
142+

0 commit comments

Comments
 (0)