plugins/offers: Extend the capability of decode to decrypt the contents of emergency.recover file.

Changelog-Added: JSON-RPC: `decode` can now decide emergency.recover files (clnemerg1...)
This commit is contained in:
Aditya Sharma
2023-10-26 13:42:13 +10:30
committed by Rusty Russell
parent 990f4d0dad
commit d130982aad

View File

@@ -6,6 +6,7 @@
#include <ccan/rune/rune.h>
#include <ccan/tal/str/str.h>
#include <common/bech32.h>
#include <common/bech32_util.h>
#include <common/bolt11.h>
#include <common/bolt11_json.h>
#include <common/bolt12_merkle.h>
@@ -17,6 +18,10 @@
#include <plugins/offers_inv_hook.h>
#include <plugins/offers_invreq_hook.h>
#include <plugins/offers_offer.h>
#include <sodium.h>
#define HEADER_LEN crypto_secretstream_xchacha20poly1305_HEADERBYTES
#define ABYTES crypto_secretstream_xchacha20poly1305_ABYTES
struct pubkey id;
u32 blockheight;
@@ -159,8 +164,41 @@ struct decodable {
struct tlv_invoice *invoice;
struct tlv_invoice_request *invreq;
struct rune *rune;
u8 *emergency_recover;
};
static u8 *encrypted_decode(const tal_t *ctx, const char *str, char **fail) {
if (strlen(str) < 8) {
*fail = tal_fmt(ctx, "invalid payload");
return NULL;
}
size_t hrp_maxlen = strlen(str) - 6;
char *hrp = tal_arr(ctx, char, hrp_maxlen);
size_t data_maxlen = strlen(str) - 8;
u5 *data = tal_arr(ctx, u5, data_maxlen);
size_t datalen = 0;
if (bech32_decode(hrp, data, &datalen, str, (size_t)-1)
== BECH32_ENCODING_NONE) {
*fail = tal_fmt(ctx, "invalid bech32 encoding");
goto fail;
}
if (!streq(hrp, "clnemerg")) {
*fail = tal_fmt(ctx, "hrp should be `clnemerg`");
goto fail;
}
u8 *data8bit = tal_arr(data, u8, 0);
bech32_pull_bits(&data8bit, data, datalen*5);
return data8bit;
fail:
tal_free(data);
return NULL;
}
static struct command_result *param_decodable(struct command *cmd,
const char *name,
const char *buffer,
@@ -214,6 +252,17 @@ static struct command_result *param_decodable(struct command *cmd,
return NULL;
}
decodable->emergency_recover = encrypted_decode(cmd, tal_strndup(tmpctx, buffer + tok.start,
tok.end - tok.start),
json_tok_startswith(buffer, &tok,
"clnemerg1")
? &likely_fail : &fail);
if (decodable->emergency_recover) {
decodable->type = "emergency recover";
return NULL;
}
/* If no other was likely, bolt11 decoder gives us failure string. */
decodable->b11 = bolt11_decode(cmd,
tal_strndup(tmpctx, buffer + tok.start,
@@ -1009,6 +1058,55 @@ static void json_add_rune(struct command *cmd, struct json_stream *js, const str
json_add_bool(js, "valid", true);
}
static struct command_result *after_makesecret(struct command *cmd,
const char *buf,
const jsmntok_t *result,
struct decodable *decodable)
{
struct secret secret;
struct json_stream *response;
const jsmntok_t *secrettok;
secrettok = json_get_member(buf, result, "secret");
json_to_secret(buf, secrettok, &secret);
crypto_secretstream_xchacha20poly1305_state crypto_state;
if (tal_bytelen(decodable->emergency_recover) < ABYTES +
HEADER_LEN)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Can't decrypt, hex is too short!");
u8 *decrypt_blob = tal_arr(tmpctx, u8,
tal_bytelen(decodable->emergency_recover) -
ABYTES -
HEADER_LEN);
/* The header part */
if (crypto_secretstream_xchacha20poly1305_init_pull(&crypto_state,
decodable->emergency_recover,
secret.data) != 0) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Can't decrypt!");
}
if (crypto_secretstream_xchacha20poly1305_pull(&crypto_state, decrypt_blob,
NULL, 0,
decodable->emergency_recover +
HEADER_LEN,
tal_bytelen(decodable->emergency_recover) -
HEADER_LEN,
NULL, 0) != 0) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Can't decrypt!");
}
response = jsonrpc_stream_success(cmd);
json_add_bool(response, "valid", true);
json_add_string(response, "type", decodable->type);
json_add_hex(response, "decrypted", decrypt_blob,
tal_bytelen(decrypt_blob));
return command_finished(cmd, response);
}
static struct command_result *json_decode(struct command *cmd,
const char *buffer,
const jsmntok_t *params)
@@ -1036,6 +1134,17 @@ static struct command_result *json_decode(struct command *cmd,
}
if (decodable->rune)
json_add_rune(cmd, response, decodable->rune);
if (decodable->emergency_recover) {
struct out_req *req;
req = jsonrpc_request_start(cmd->plugin, cmd, "makesecret",
after_makesecret, &forward_error,
decodable);
json_add_string(req->js, "string", "scb secret");
return send_outreq(cmd->plugin, req);
}
return command_finished(cmd, response);
}