mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
lightningd: checkmessage can intuit pubkey in some cases.
*If* we know the key has signed something else (as is the case for channel_announcement) then we can effectively trust the key derivation. This matches how LND's VerifyMessage works, though in the next patch we will document it exactly. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
034ed1711c
commit
e5d9c7effc
@ -8,6 +8,7 @@
|
||||
#include <errno.h>
|
||||
#include <gossipd/gen_gossip_wire.h>
|
||||
#include <hsmd/gen_hsm_wire.h>
|
||||
#include <lightningd/subd.h>
|
||||
#include <string.h>
|
||||
#include <wire/wire_sync.h>
|
||||
|
||||
@ -120,6 +121,32 @@ static const struct json_command json_signmessage_cmd = {
|
||||
};
|
||||
AUTODATA(json_command, &json_signmessage_cmd);
|
||||
|
||||
struct command_and_node {
|
||||
struct command *cmd;
|
||||
struct node_id id;
|
||||
};
|
||||
|
||||
/* Gossipd tells us if it's a known node by returning details. */
|
||||
static void getnode_reply(struct subd *gossip UNUSED, const u8 *reply,
|
||||
const int *fds UNUSED,
|
||||
struct command_and_node *can)
|
||||
{
|
||||
struct gossip_getnodes_entry **nodes;
|
||||
struct json_stream *response;
|
||||
|
||||
if (!fromwire_gossip_getnodes_reply(reply, reply, &nodes)) {
|
||||
log_broken(can->cmd->ld->log,
|
||||
"Malformed gossip_getnodes response %s",
|
||||
tal_hex(tmpctx, reply));
|
||||
nodes = NULL;
|
||||
}
|
||||
|
||||
response = json_stream_success(can->cmd);
|
||||
json_add_node_id(response, "pubkey", &can->id);
|
||||
json_add_bool(response, "verified", tal_count(nodes) > 0);
|
||||
was_pending(command_success(can->cmd, response));
|
||||
}
|
||||
|
||||
static struct command_result *json_checkmessage(struct command *cmd,
|
||||
const char *buffer,
|
||||
const jsmntok_t *obj UNNEEDED,
|
||||
@ -136,7 +163,7 @@ static struct command_result *json_checkmessage(struct command *cmd,
|
||||
if (!param(cmd, buffer, params,
|
||||
p_req("message", param_string, &message),
|
||||
p_req("zbase", param_string, &zb),
|
||||
p_req("pubkey", param_pubkey, &pubkey),
|
||||
p_opt("pubkey", param_pubkey, &pubkey),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
@ -162,13 +189,34 @@ static struct command_result *json_checkmessage(struct command *cmd,
|
||||
sha256_update(&sctx, message, strlen(message));
|
||||
sha256_double_done(&sctx, &shad);
|
||||
|
||||
response = json_stream_success(cmd);
|
||||
if (!secp256k1_ecdsa_recover(secp256k1_ctx, &reckey.pubkey, &rsig,
|
||||
shad.sha.u.u8)) {
|
||||
response = json_stream_success(cmd);
|
||||
json_add_bool(response, "verified", false);
|
||||
} else {
|
||||
json_add_bool(response, "verified", pubkey_eq(pubkey, &reckey));
|
||||
return command_success(cmd, response);
|
||||
}
|
||||
|
||||
/* If they didn't specify pubkey, we only accept the signature if it's
|
||||
* in the graph (thus, they've signed something with it). This idea
|
||||
* was stolen directly from lnd, thanks @roasbeef.
|
||||
*
|
||||
* FIXME: We could also look through known invoices: AFAICT you can't
|
||||
* make two (different) signed messages with the same recovered key
|
||||
* unless you know the secret key */
|
||||
if (!pubkey) {
|
||||
u8 *req;
|
||||
struct command_and_node *can = tal(cmd, struct command_and_node);
|
||||
|
||||
node_id_from_pubkey(&can->id, &reckey);
|
||||
can->cmd = cmd;
|
||||
req = towire_gossip_getnodes_request(cmd, &can->id);
|
||||
subd_req(cmd, cmd->ld->gossip, req, -1, 0, getnode_reply, can);
|
||||
return command_still_pending(cmd);
|
||||
}
|
||||
|
||||
response = json_stream_success(cmd);
|
||||
json_add_pubkey(response, "pubkey", &reckey);
|
||||
json_add_bool(response, "verified", pubkey_eq(pubkey, &reckey));
|
||||
return command_success(cmd, response);
|
||||
}
|
||||
|
||||
|
@ -1649,7 +1649,7 @@ def test_relative_config_dir(node_factory):
|
||||
|
||||
|
||||
def test_signmessage(node_factory):
|
||||
l1 = node_factory.get_node()
|
||||
l1, l2 = node_factory.line_graph(2, wait_for_announce=True)
|
||||
|
||||
corpus = [[None,
|
||||
"this is a test!",
|
||||
@ -1685,3 +1685,16 @@ def test_signmessage(node_factory):
|
||||
|
||||
assert l1.rpc.call('checkmessage', [c[1], c[2], c[3]])['verified']
|
||||
assert not l1.rpc.call('checkmessage', [c[1] + "modified", c[2], c[3]])['verified']
|
||||
checknokey = l1.rpc.call('checkmessage', [c[1], c[2]])
|
||||
# Of course, we know our own pubkey
|
||||
if c[3] == l1.info['id']:
|
||||
assert checknokey['verified']
|
||||
else:
|
||||
assert not checknokey['verified']
|
||||
assert checknokey['pubkey'] == c[3]
|
||||
|
||||
# l2 knows about l1, so it can validate it.
|
||||
zm = l1.rpc.call('signmessage', ["message for you"])['zbase']
|
||||
checknokey = l2.rpc.call('checkmessage', ["message for you", zm])
|
||||
assert checknokey['pubkey'] == l1.info['id']
|
||||
assert checknokey['verified']
|
||||
|
Loading…
Reference in New Issue
Block a user