commit bf5979fb35fc50a0e94764cc0b73f1e440dea4b9
parent 7739f02be1cfe8bb15bcc4df8b39ac0776b00f00
Author: Florian Dold <dold@taler.net>
Date: Tue, 21 Jul 2026 10:54:36 +0200
util: verify the bech32 checksum and the segwit version consistency
The checksum was only verified when the caller passed the expected
encoding, and the version guards used === where the reference uses !==, so
they rejected exactly the combinations they are meant to accept.
Diffstat:
2 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/packages/taler-util/src/bech32.ts b/packages/taler-util/src/bech32.ts
@@ -129,6 +129,14 @@ export namespace BitcoinBech32 {
WRONG_CHECKSUM,
}
+ /**
+ * Decode a bech32 string and verify its checksum.
+ *
+ * When @param enc is given, the checksum must match that encoding. When it
+ * is omitted, either encoding is accepted and the one that verified is
+ * reported back, so that callers such as the segwit address parser can check
+ * it against the witness version.
+ */
export function decode(
bechString: string,
enc?: Encodings,
@@ -136,6 +144,7 @@ export namespace BitcoinBech32 {
{
hrp: string;
data: number[];
+ enc: Encodings;
},
BitcoinParseError
> {
@@ -176,9 +185,16 @@ export namespace BitcoinBech32 {
}
data.push(d);
}
- if (enc && !verifyChecksum(hrp, data, enc)) {
+ const candidates =
+ enc !== undefined ? [enc] : [Encodings.BECH32, Encodings.BECH32M];
+ const actualEnc = candidates.find((c) => verifyChecksum(hrp, data, c));
+ if (actualEnc === undefined) {
return Result.error(BitcoinParseError.WRONG_CHECKSUM);
}
- return Result.of({ hrp, data: data.slice(0, data.length - 6) });
+ return Result.of({
+ hrp,
+ data: data.slice(0, data.length - 6),
+ enc: actualEnc,
+ });
}
}
diff --git a/packages/taler-util/src/segwit_addr.ts b/packages/taler-util/src/segwit_addr.ts
@@ -90,10 +90,12 @@ export namespace BitcoinSewgit {
if (dec.data[0] === 0 && res.length !== 20 && res.length !== 32) {
return Result.error(BitcoinSewgitParseError.DECODING_PROBLEM);
}
- if (dec.data[0] === 0 && enc === BitcoinBech32.Encodings.BECH32) {
+ // Version 0 programs are checksummed with bech32, later versions with
+ // bech32m (BIP-350).
+ if (dec.data[0] === 0 && dec.enc !== BitcoinBech32.Encodings.BECH32) {
return Result.error(BitcoinSewgitParseError.DECODING_PROBLEM);
}
- if (dec.data[0] !== 0 && enc === BitcoinBech32.Encodings.BECH32M) {
+ if (dec.data[0] !== 0 && dec.enc !== BitcoinBech32.Encodings.BECH32M) {
return Result.error(BitcoinSewgitParseError.DECODING_PROBLEM);
}
return Result.of({ version: dec.data[0], program: res });