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:
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;