mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-20 15:44:21 +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:
committed by
neil saitug
parent
034ed1711c
commit
e5d9c7effc
@@ -8,6 +8,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <gossipd/gen_gossip_wire.h>
|
#include <gossipd/gen_gossip_wire.h>
|
||||||
#include <hsmd/gen_hsm_wire.h>
|
#include <hsmd/gen_hsm_wire.h>
|
||||||
|
#include <lightningd/subd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wire/wire_sync.h>
|
#include <wire/wire_sync.h>
|
||||||
|
|
||||||
@@ -120,6 +121,32 @@ static const struct json_command json_signmessage_cmd = {
|
|||||||
};
|
};
|
||||||
AUTODATA(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,
|
static struct command_result *json_checkmessage(struct command *cmd,
|
||||||
const char *buffer,
|
const char *buffer,
|
||||||
const jsmntok_t *obj UNNEEDED,
|
const jsmntok_t *obj UNNEEDED,
|
||||||
@@ -136,7 +163,7 @@ static struct command_result *json_checkmessage(struct command *cmd,
|
|||||||
if (!param(cmd, buffer, params,
|
if (!param(cmd, buffer, params,
|
||||||
p_req("message", param_string, &message),
|
p_req("message", param_string, &message),
|
||||||
p_req("zbase", param_string, &zb),
|
p_req("zbase", param_string, &zb),
|
||||||
p_req("pubkey", param_pubkey, &pubkey),
|
p_opt("pubkey", param_pubkey, &pubkey),
|
||||||
NULL))
|
NULL))
|
||||||
return command_param_failed();
|
return command_param_failed();
|
||||||
|
|
||||||
@@ -162,13 +189,34 @@ static struct command_result *json_checkmessage(struct command *cmd,
|
|||||||
sha256_update(&sctx, message, strlen(message));
|
sha256_update(&sctx, message, strlen(message));
|
||||||
sha256_double_done(&sctx, &shad);
|
sha256_double_done(&sctx, &shad);
|
||||||
|
|
||||||
response = json_stream_success(cmd);
|
|
||||||
if (!secp256k1_ecdsa_recover(secp256k1_ctx, &reckey.pubkey, &rsig,
|
if (!secp256k1_ecdsa_recover(secp256k1_ctx, &reckey.pubkey, &rsig,
|
||||||
shad.sha.u.u8)) {
|
shad.sha.u.u8)) {
|
||||||
|
response = json_stream_success(cmd);
|
||||||
json_add_bool(response, "verified", false);
|
json_add_bool(response, "verified", false);
|
||||||
} else {
|
return command_success(cmd, response);
|
||||||
json_add_bool(response, "verified", pubkey_eq(pubkey, &reckey));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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);
|
return command_success(cmd, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1649,7 +1649,7 @@ def test_relative_config_dir(node_factory):
|
|||||||
|
|
||||||
|
|
||||||
def test_signmessage(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,
|
corpus = [[None,
|
||||||
"this is a test!",
|
"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 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']
|
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']
|
||||||
|
|||||||
Reference in New Issue
Block a user