taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 65a9d6018681316fc58347b50fae3f6fd2629884
parent 19c41e767739a409083e93cd9a17ed6cce57f8e6
Author: Florian Dold <dold@taler.net>
Date:   Wed, 22 Jul 2026 19:24:20 +0200

wallet: derive the donation unit key hash instead of reading it

This fixes test failures due to a breaking protocol change in donau.

Diffstat:
Mpackages/taler-util/src/types-donau.ts | 39++++++++++++++++++++++++++++++++++++---
Mpackages/taler-wallet-core/src/donau.ts | 5+++--
2 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/packages/taler-util/src/types-donau.ts b/packages/taler-util/src/types-donau.ts @@ -46,6 +46,8 @@ import { AmountString, } from "./types-taler-common.js"; import { DenomKeyType } from "./types-taler-exchange.js"; +import { assertUnreachable } from "./errors.js"; +import { decodeCrock, encodeCrock, hash } from "./taler-crypto.js"; export interface DonauVersionResponse { // libtool-style representation of the Donau protocol version, see @@ -137,10 +139,27 @@ export interface DonationUnitKeyGroupCommon { lost?: boolean; } +/** + * Public key of a donation unit, as it appears in the donau's key response. + * + * The hash of the key is not part of this: the donau does not send it + * (`DONAU_JSON_pack_donation_unit_pub` packs the cipher and the key only) and + * derives it locally when parsing, so clients do the same. See + * {@link hashDonationUnitPub}. + */ +export type DonationUnitPub = + | { + cipher: "RSA"; + rsa_public_key: RsaPublicKeyString; + } + | { + cipher: "CS"; + cs_public_key: Cs25519Point; + }; + export interface DonationUnitKeyGroupRsa extends DonationUnitKeyGroupCommon { donation_unit_pub: { cipher: "RSA"; - pub_key_hash: HashCodeString; rsa_public_key: RsaPublicKeyString; }; } @@ -148,11 +167,25 @@ export interface DonationUnitKeyGroupRsa extends DonationUnitKeyGroupCommon { export interface DonationUnitKeyGroupCs extends DonationUnitKeyGroupCommon { donation_unit_pub: { cipher: "CS"; - pub_key_hash: HashCodeString; - cs_pub: Cs25519Point; + cs_public_key: Cs25519Point; }; } +/** + * Hash of a donation unit public key, as the donau computes it: the hash of + * the encoded key, which is what the wire format carries. + */ +export function hashDonationUnitPub(pub: DonationUnitPub): HashCodeString { + switch (pub.cipher) { + case "RSA": + return encodeCrock(hash(decodeCrock(pub.rsa_public_key))); + case "CS": + return encodeCrock(hash(decodeCrock(pub.cs_public_key))); + default: + assertUnreachable(pub); + } +} + // FIXME: Validate properly! export const codecForDonationUnitKeyGroup: Codec<DonationUnitKeyGroup> = codecForAny(); diff --git a/packages/taler-wallet-core/src/donau.ts b/packages/taler-wallet-core/src/donau.ts @@ -36,6 +36,7 @@ import { DonationUnitKeyGroupRsa, DonauHttpClient, DonauKeysResponse, + hashDonationUnitPub, DonauStatementItem, DonauUnitPubKey, EmptyObject, @@ -357,7 +358,7 @@ async function getCandidateDonationUnits( continue; } candidates.push({ - unitHash: g.donation_unit_pub.pub_key_hash, + unitHash: hashDonationUnitPub(g.donation_unit_pub), unitKey: { cipher: DenomKeyType.Rsa, age_mask: 0, @@ -572,7 +573,7 @@ export async function acceptDonauBlindSigs( const candidate = keysResp.donation_units[j]; if ( candidate.donation_unit_pub.cipher === "RSA" && - candidate.donation_unit_pub.pub_key_hash === + hashDonationUnitPub(candidate.donation_unit_pub) === myPlanchet.donationUnitPubHash ) { unitKey = candidate as DonationUnitKeyGroupRsa;