Skip to content

Commit 2da4c05

Browse files
committed
add signmessagewithkey RPC
signmessagewithkey: allows to sign a message with a key associated with one bitcoin address in our wallet. Signed-off-by: Lagrang3 <[email protected]>
1 parent 6f5e175 commit 2da4c05

File tree

2 files changed

+197
-0
lines changed

2 files changed

+197
-0
lines changed

Diff for: doc/schemas/lightning-signmessagewithkey.json

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{
2+
"$schema": "../rpc-schema-draft.json",
3+
"type": "object",
4+
"rpc": "signmessagewithkey",
5+
"title": "Command to create a signature using a key from the wallet",
6+
"description": [
7+
"The **signmessagewithkey** RPC command creates a digital signature of *message* using the key associated with the address provided in the input."
8+
],
9+
"added": "v25.05",
10+
"request": {
11+
"required": [
12+
"message",
13+
"address"
14+
],
15+
"additionalProperties": false,
16+
"properties": {
17+
"message": {
18+
"type": "string",
19+
"description": [
20+
"Less than 65536 characters long message to be signed by the node."
21+
]
22+
},
23+
"address": {
24+
"type": "string",
25+
"description": [
26+
"A Bitcoin accepted type, including a bech32, address for lookup in the list of addresses issued to date."
27+
]
28+
}
29+
}
30+
},
31+
"response": {
32+
"required": [
33+
"signature",
34+
"keyidx"
35+
],
36+
"additionalProperties": false,
37+
"properties": {
38+
"signature": {
39+
"type": "string",
40+
"description": [
41+
"The signature in base64."
42+
]
43+
},
44+
"keyidx": {
45+
"type": "u64",
46+
"description": [
47+
"The key index of the address requested."
48+
]
49+
},
50+
"bech32": {
51+
"type": "string",
52+
"description": [
53+
"The bech32 (native segwit) address."
54+
]
55+
},
56+
"p2tr": {
57+
"type": "string",
58+
"description": [
59+
"The taproot address."
60+
]
61+
}
62+
}
63+
},
64+
"author": [
65+
"Lagrang3 <<[email protected]>> is mainly responsible."
66+
],
67+
"see_also": [
68+
"lightning-checkmessagewithkey(7)"
69+
],
70+
"resources": [
71+
"Main web site: <https://github.com/ElementsProject/lightning>"
72+
],
73+
"examples": [
74+
]
75+
}
76+

Diff for: wallet/walletrpc.c

+121
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <ccan/array_size/array_size.h>
55
#include <ccan/cast/cast.h>
66
#include <common/addr.h>
7+
#include <common/base64.h>
78
#include <common/bech32.h>
89
#include <common/configdir.h>
910
#include <common/json_command.h>
@@ -1066,3 +1067,123 @@ static const struct json_command sendpsbt_command = {
10661067
};
10671068

10681069
AUTODATA(json_command, &sendpsbt_command);
1070+
1071+
static struct command_result *
1072+
json_signmessagewithkey(struct command *cmd, const char *buffer,
1073+
const jsmntok_t *obj UNNEEDED, const jsmntok_t *params)
1074+
{
1075+
/* decoding the address */
1076+
const u8 *scriptpubkey;
1077+
const char *message;
1078+
1079+
/* from wallet BIP32 */
1080+
struct pubkey pubkey;
1081+
1082+
if (!param(
1083+
cmd, buffer, params,
1084+
p_req("message", param_string, &message),
1085+
p_req("address", param_bitcoin_address, &scriptpubkey),
1086+
NULL))
1087+
return command_param_failed();
1088+
1089+
const size_t script_len = tal_bytelen(scriptpubkey);
1090+
1091+
/* FIXME: we already had the address from the input */
1092+
char *addr;
1093+
addr = encode_scriptpubkey_to_addr(tmpctx, chainparams, scriptpubkey);
1094+
enum addrtype addrtype;
1095+
1096+
if (is_p2tr(scriptpubkey, script_len, NULL))
1097+
addrtype = ADDR_P2TR;
1098+
else if (is_p2wpkh(scriptpubkey, script_len, NULL))
1099+
addrtype = ADDR_BECH32;
1100+
else {
1101+
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
1102+
"address %s is neither p2wpkh nor p2tr and "
1103+
"it is not supported",
1104+
addr);
1105+
}
1106+
1107+
u32 keyidx;
1108+
1109+
/* loop over all generated keys, find a matching key */
1110+
/* FIXME: alternatively, can we use the our_addresses hash table?
1111+
*struct script_with_len *key;
1112+
*struct wallet_address *addr =
1113+
* wallet_address_htable_get(cmd->ld->wallet->our_addresses, key);
1114+
*/
1115+
struct issued_address_type *listaddrtypes =
1116+
wallet_list_addresses(tmpctx, cmd->ld->wallet, 1, NULL);
1117+
for (size_t i = 0; i < tal_count(listaddrtypes); i++) {
1118+
if (listaddrtypes[i].keyidx == BIP32_INITIAL_HARDENED_CHILD) {
1119+
break;
1120+
}
1121+
bip32_pubkey(cmd->ld, &pubkey, listaddrtypes[i].keyidx);
1122+
char *out_p2wpkh = "";
1123+
char *out_p2tr = "";
1124+
if (listaddrtypes[i].addrtype == ADDR_BECH32 ||
1125+
listaddrtypes[i].addrtype == ADDR_ALL) {
1126+
u8 *redeemscript_p2wpkh;
1127+
out_p2wpkh = encode_pubkey_to_addr(
1128+
cmd, &pubkey, ADDR_BECH32, &redeemscript_p2wpkh);
1129+
if (!out_p2wpkh) {
1130+
abort();
1131+
}
1132+
}
1133+
if (listaddrtypes[i].addrtype == ADDR_P2TR ||
1134+
listaddrtypes[i].addrtype == ADDR_ALL) {
1135+
out_p2tr =
1136+
encode_pubkey_to_addr(cmd, &pubkey, ADDR_P2TR,
1137+
/* out_redeemscript */ NULL);
1138+
if (!out_p2tr) {
1139+
abort();
1140+
}
1141+
}
1142+
1143+
if (streq(addr, out_p2wpkh) || streq(addr, out_p2tr)) {
1144+
keyidx = listaddrtypes[i].keyidx;
1145+
break;
1146+
}
1147+
}
1148+
1149+
/* wire to hsmd a sign request */
1150+
u8 *msg = towire_hsmd_sign_message_with_key(
1151+
cmd, tal_dup_arr(tmpctx, u8, (u8 *)message, strlen(message), 0),
1152+
keyidx);
1153+
if (!wire_sync_write(cmd->ld->hsm_fd, take(msg))) {
1154+
fatal("Could not write sign_with_key to HSM: %s",
1155+
strerror(errno));
1156+
}
1157+
1158+
/* read form hsmd a sign reply */
1159+
msg = wire_sync_read(cmd, cmd->ld->hsm_fd);
1160+
1161+
size_t len = 72;
1162+
u8 der[72];
1163+
secp256k1_ecdsa_signature signature;
1164+
1165+
if (!fromwire_hsmd_sign_message_with_key_reply(msg, &signature)) {
1166+
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
1167+
"HSM gave bad sign_with_key_reply %s",
1168+
tal_hex(tmpctx, msg));
1169+
}
1170+
1171+
secp256k1_ecdsa_signature_serialize_der(secp256k1_ctx, der, &len,
1172+
&signature);
1173+
1174+
struct json_stream *response;
1175+
response = json_stream_success(cmd);
1176+
if (addrtype & ADDR_BECH32)
1177+
json_add_string(response, "bech32", addr);
1178+
if (addrtype & ADDR_P2TR)
1179+
json_add_string(response, "p2tr", addr);
1180+
json_add_u32(response, "keyidx", keyidx);
1181+
json_add_string(response, "signature", b64_encode(tmpctx, der, len));
1182+
return command_success(cmd, response);
1183+
}
1184+
1185+
static const struct json_command signmessagewithkey_command = {
1186+
"signmessagewithkey",
1187+
json_signmessagewithkey
1188+
};
1189+
AUTODATA(json_command, &signmessagewithkey_command);

0 commit comments

Comments
 (0)