forked from ElementsProject/lightning
-
Notifications
You must be signed in to change notification settings - Fork 0
/
signmessage.c
249 lines (217 loc) · 8.75 KB
/
signmessage.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#include "config.h"
#include <common/bech32.h>
#include <common/configdir.h>
#include <common/json_command.h>
#include <common/json_param.h>
#include <errno.h>
#include <hsmd/hsmd_wiregen.h>
#include <lightningd/hsm_control.h>
#include <lightningd/plugin.h>
/* These tables copied from zbase32 src:
* copyright 2002-2007 Zooko "Zooko" Wilcox-O'Hearn
* mailto:[email protected]
*
* 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).
*/
static const char*const zbase32_chars="ybndrfg8ejkmcpqxot1uwisza345h769";
/* 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 const char *to_zbase32(const tal_t *ctx, const u8 *msg, size_t srclen)
{
size_t outlen;
char *out = tal_arr(ctx, char, (srclen * 8 + 4) / 5 + 1);
outlen = 0;
if (!bech32_convert_bits((uint8_t *)out, &outlen, 5, msg, srclen, 8, true))
return tal_free(out);
assert(outlen < tal_bytelen(out));
for (size_t i = 0; i < outlen; i++)
out[i] = zbase32_chars[(unsigned)out[i]];
out[outlen] = '\0';
return out;
}
static const u8 *from_zbase32(const tal_t *ctx, const char *msg)
{
u5 *u5arr;
u8 *u8arr;
size_t len;
u5arr = tal_arr(tmpctx, u5, strlen(msg));
for (size_t i = 0; i < tal_bytelen(u5arr); i++) {
u5arr[i] = zbase32_revchars[(unsigned char)msg[i]];
if (u5arr[i] > 31)
return NULL;
}
u8arr = tal_arr(ctx, u8, (tal_bytelen(u5arr) * 5 + 7) / 8);
len = 0;
if (!bech32_convert_bits(u8arr, &len, 8,
u5arr, tal_bytelen(u5arr), 5, false))
return tal_free(u8arr);
return len == tal_bytelen(u8arr) ? u8arr : tal_free(u8arr);
}
static struct command_result *json_signmessage(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
const char *message;
secp256k1_ecdsa_recoverable_signature rsig;
struct json_stream *response;
u8 sig[65];
const u8 *msg;
int recid;
if (!param_check(cmd, buffer, params,
p_req("message", param_string, &message),
NULL))
return command_param_failed();
if (strlen(message) > 65535)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Message must be < 64k");
if (command_check_only(cmd))
return command_check_done(cmd);
msg = towire_hsmd_sign_message(NULL,
tal_dup_arr(tmpctx, u8, (u8 *)message,
strlen(message), 0));
msg = hsm_sync_req(tmpctx, cmd->ld, take(msg));
if (!fromwire_hsmd_sign_message_reply(msg, &rsig))
fatal("HSM gave bad hsm_sign_message_reply %s",
tal_hex(msg, msg));
secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_ctx,
sig+1, &recid,
&rsig);
response = json_stream_success(cmd);
json_add_hex(response, "signature", sig+1, sizeof(sig)-1);
sig[0] = recid;
json_add_hex(response, "recid", sig, 1);
/* From https://twitter.com/rusty_twit/status/1182102005914800128:
* @roasbeef & @bitconner point out that #lnd algo is:
* zbase32(SigRec(SHA256(SHA256("Lightning Signed Message:" + msg)))).
* zbase32 from https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt
* and SigRec has first byte 31 + recovery id, followed by 64 byte sig.
* #specinatweet */
sig[0] += 31;
json_add_string(response, "zbase",
to_zbase32(response, sig, sizeof(sig)));
return command_success(cmd, response);
}
static const struct json_command json_signmessage_cmd = {
"signmessage",
"utility",
json_signmessage,
"Create a digital signature of {message}",
};
AUTODATA(json_command, &json_signmessage_cmd);
struct command_and_node {
struct command *cmd;
struct node_id id;
};
/* topology tells us if it's a known node by returning details. */
static void listnodes_done(const char *buffer,
const jsmntok_t *toks,
const jsmntok_t *idtok UNUSED,
struct command_and_node *can)
{
struct json_stream *response;
const jsmntok_t *t;
t = json_get_member(buffer, toks, "result");
if (t)
t = json_get_member(buffer, t, "nodes");
if (!t || t->size == 0) {
response = json_stream_fail(can->cmd,
SIGNMESSAGE_PUBKEY_NOT_FOUND,
"pubkey not found in the graph");
json_add_node_id(response, "claimed_key", &can->id);
json_object_end(response);
was_pending(command_failed(can->cmd, response));
return;
}
response = json_stream_success(can->cmd);
json_add_node_id(response, "pubkey", &can->id);
json_add_bool(response, "verified", t && t->size == 1);
was_pending(command_success(can->cmd, response));
}
static struct command_result *json_checkmessage(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct pubkey *pubkey, reckey;
const u8 *u8sig;
const char *message, *zb;
secp256k1_ecdsa_recoverable_signature rsig;
struct sha256_ctx sctx = SHA256_INIT;
struct sha256_double shad;
struct json_stream *response;
if (!param_check(cmd, buffer, params,
p_req("message", param_string, &message),
p_req("zbase", param_string, &zb),
p_opt("pubkey", param_pubkey, &pubkey),
NULL))
return command_param_failed();
u8sig = from_zbase32(tmpctx, zb);
if (!u8sig)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"zbase is not valid zbase32");
if (tal_bytelen(u8sig) != 65)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"zbase is too %s",
tal_bytelen(u8sig) < 65 ? "short" : "long");
if (!secp256k1_ecdsa_recoverable_signature_parse_compact(secp256k1_ctx,
&rsig,
u8sig + 1,
u8sig[0] - 31))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"cannot parse zbase signature");
if (command_check_only(cmd))
return command_check_done(cmd);
sha256_update(&sctx, "Lightning Signed Message:",
strlen("Lightning Signed Message:"));
sha256_update(&sctx, message, strlen(message));
sha256_double_done(&sctx, &shad);
if (!secp256k1_ecdsa_recover(secp256k1_ctx, &reckey.pubkey, &rsig,
shad.sha.u.u8)) {
response = json_stream_success(cmd);
json_add_bool(response, "verified", false);
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) {
struct jsonrpc_request *req;
struct plugin *plugin;
struct command_and_node *can = tal(cmd, struct command_and_node);
node_id_from_pubkey(&can->id, &reckey);
can->cmd = cmd;
/* Only works if we have listnodes! */
plugin = find_plugin_for_command(cmd->ld, "listnodes");
if (plugin) {
req = jsonrpc_request_start(cmd, "listnodes",
cmd->id,
plugin->non_numeric_ids,
command_log(cmd),
NULL, listnodes_done,
can);
json_add_node_id(req->stream, "id", &can->id);
jsonrpc_request_end(req);
plugin_request_send(plugin, req);
return command_still_pending(cmd);
}
}
response = json_stream_success(cmd);
json_add_pubkey(response, "pubkey", &reckey);
json_add_bool(response, "verified",
pubkey && pubkey_eq(pubkey, &reckey));
return command_success(cmd, response);
}
static const struct json_command json_checkmessage_cmd = {
"checkmessage",
"utility",
json_checkmessage,
"Verify a digital signature {zbase} of {message} signed with {pubkey}",
};
AUTODATA(json_command, &json_checkmessage_cmd);