From 7174d06a707b442cb6c508f4664fcb577e9b640c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 30 Mar 2023 15:45:53 +1030 Subject: [PATCH] wallet/psbt_fixup: routine to fix invalid PBSTs which modern libwally won't load. Signed-off-by: Rusty Russell --- wallet/Makefile | 1 + wallet/db.c | 3 + wallet/psbt_fixup.c | 188 ++++++++++++++++++++++++++++++++++++++++++++ wallet/psbt_fixup.h | 12 +++ 4 files changed, 204 insertions(+) create mode 100644 wallet/psbt_fixup.c create mode 100644 wallet/psbt_fixup.h diff --git a/wallet/Makefile b/wallet/Makefile index 58d546416..c3707dde2 100644 --- a/wallet/Makefile +++ b/wallet/Makefile @@ -3,6 +3,7 @@ WALLET_LIB_SRC := \ wallet/db.c \ wallet/invoices.c \ + wallet/psbt_fixup.c \ wallet/txfilter.c \ wallet/wallet.c \ wallet/walletrpc.c diff --git a/wallet/db.c b/wallet/db.c index 6484873a2..ac46d1090 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -51,6 +51,9 @@ static void migrate_payments_scids_as_integers(struct lightningd *ld, static void fillin_missing_lease_satoshi(struct lightningd *ld, struct db *db); +static void fillin_missing_lease_satoshi(struct lightningd *ld, + struct db *db); + /* Do not reorder or remove elements from this array, it is used to * migrate existing databases from a previous state, based on the * string indices */ diff --git a/wallet/psbt_fixup.c b/wallet/psbt_fixup.c new file mode 100644 index 000000000..b5509635d --- /dev/null +++ b/wallet/psbt_fixup.c @@ -0,0 +1,188 @@ +/* This is designed to fix up malformed PBSTs, where prior to v0.12.0 + * (commit 572942c783a58e518f0a1b449412a82717594636) we would put raw + * signatures, not DER-encoded signatures, inside our PSBT inputs' + * PSBT_IN_PARTIAL_SIG. + * + * As of libwally 0.88 (and perhaps 0.87?) it will refuse to load them. + */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +struct keypair { + u64 keytype; + u8 *key; + u8 *value; +}; + +static size_t compact_size_len(u64 v) +{ + if (v < 0xfd) { + return 1; + } else if (v <= 0xffff) { + return 3; + } else if (v <= 0xffffffff) { + return 5; + } else { + return 9; + } +} + +static u64 fromwire_compact_size(const u8 **cursor, size_t *max) +{ + u8 v; + le16 v16; + le32 v32; + le64 v64; + + v = fromwire_u8(cursor, max); + switch (v) { + case 0xfd: + fromwire(cursor, max, &v16, sizeof(v16)); + return le16_to_cpu(v16); + case 0xfe: + fromwire(cursor, max, &v32, sizeof(v32)); + return le32_to_cpu(v32); + case 0xff: + fromwire(cursor, max, &v64, sizeof(v64)); + return le64_to_cpu(v64); + default: + return v; + } +} + +static size_t fromwire_compact_len(const u8 **cursor, size_t *max) +{ + u64 len = fromwire_compact_size(cursor, max); + if (len > *max) { + fromwire_fail(cursor, max); + return 0; + } + return len; +} + +/* BIP-0174: + * := + * := + * := + */ +static struct keypair *fromwire_keypair(const tal_t *ctx, + const u8 **cursor, + size_t *max) +{ + struct keypair *kp = tal(ctx, struct keypair); + u64 len; + size_t keylen; + + /* 0 byte terminates */ + len = fromwire_compact_len(cursor, max); + if (len == 0) + return tal_free(kp); + + kp->keytype = fromwire_compact_size(cursor, max); + /* Sanity check */ + if (compact_size_len(kp->keytype) > len) + return tal_free(kp); + keylen = len - compact_size_len(kp->keytype); + kp->key = tal_arr(kp, u8, keylen); + fromwire_u8_array(cursor, max, kp->key, keylen); + + len = fromwire_compact_len(cursor, max); + kp->value = tal_arr(kp, u8, len); + fromwire_u8_array(cursor, max, kp->value, len); + return kp; +} + +static void towire_compact_size(u8 **pptr, u64 v) +{ + if (v < 0xfd) { + towire_u8(pptr, v); + } else if (v <= 0xffff) { + le16 v16 = cpu_to_le16(v); + towire_u8(pptr, 0xfd); + towire(pptr, &v16, sizeof(v16)); + } else if (v <= 0xffffffff) { + le32 v32 = cpu_to_le32(v); + towire_u8(pptr, 0xfe); + towire(pptr, &v32, sizeof(v32)); + } else { + le64 v64 = cpu_to_le64(v); + towire_u8(pptr, 0xff); + towire(pptr, &v64, sizeof(v64)); + } +} + +static void towire_keypair(u8 **pptr, const struct keypair *kp) +{ + towire_compact_size(pptr, + compact_size_len(kp->keytype) + tal_bytelen(kp->key)); + towire_compact_size(pptr, kp->keytype); + towire_u8_array(pptr, kp->key, tal_bytelen(kp->key)); + towire_compact_size(pptr, tal_bytelen(kp->value)); + towire_u8_array(pptr, kp->value, tal_bytelen(kp->value)); +} + +static bool fixup_sig(struct keypair *kp) +{ + const u8 *valcursor = kp->value; + size_t vallen = tal_bytelen(kp->value); + struct bitcoin_signature sig; + size_t derlen; + u8 der[73]; + + fromwire_secp256k1_ecdsa_signature(&valcursor, &vallen, &sig.s); + sig.sighash_type = SIGHASH_ALL; + + /* If that didn't parse, or there are more bytes + * left, ignore it */ + if (valcursor == NULL || vallen != 0) + return false; + + derlen = signature_to_der(der, &sig); + kp->value = tal_dup_arr(kp, u8, der, derlen, 0); + return true; +} + +/* I am deeply, deeply unhappy with this code. I initially tried parsing the + * entire PSBT, but that turns out not to be possible without decoding the + * tranaction. Literally WTF */ +const u8 *psbt_fixup(const tal_t *ctx, const u8 *psbtblob) +{ + const u8 *prev_cursor, *cursor = psbtblob; + size_t max = tal_bytelen(psbtblob); + u8 *ret; + struct keypair *kp, *changed_kp; + + /* Skip magic */ + fromwire_pad(&cursor, &max, 5); + + /* Skip global map */ + while ((kp = fromwire_keypair(tmpctx, &cursor, &max)) != NULL); + + /* Now input map */ + changed_kp = NULL; + prev_cursor = cursor; + while ((kp = fromwire_keypair(tmpctx, &cursor, &max)) != NULL) { + /* PSBT_IN_PARTIAL_SIG = 0x02 */ + if (kp->keytype == 2 && fixup_sig(kp)) { + changed_kp = kp; + break; + } + prev_cursor = cursor; + } + + if (!changed_kp) + return NULL; + + ret = tal_dup_arr(ctx, u8, psbtblob, prev_cursor - psbtblob, 0); + towire_keypair(&ret, changed_kp); + towire_u8_array(&ret, cursor, max); + + return ret; +} diff --git a/wallet/psbt_fixup.h b/wallet/psbt_fixup.h new file mode 100644 index 000000000..49ef14363 --- /dev/null +++ b/wallet/psbt_fixup.h @@ -0,0 +1,12 @@ +#ifndef LIGHTNING_WALLET_PSBT_FIXUP_H +#define LIGHTNING_WALLET_PSBT_FIXUP_H +#include "config.h" +#include +#include + +/* If psbtblob cannot be parse, try rewriting to fix signature. + * Returns NULL if it doesn't parse or was unchanged. + */ +const u8 *psbt_fixup(const tal_t *ctx, const u8 *psbtblob); + +#endif /* LIGHTNING_WALLET_PSBT_FIXUP_H */