commit 1fb7d167bcce30e59d782e16151d63190f4fcb51
parent 40273e71e65ec916c4a809191f4b56a130c462ce
Author: Florian Dold <dold@taler.net>
Date: Wed, 22 Jul 2026 12:21:19 +0200
wallet: verify the master signature of exchange signing keys
The online signing keys were the only part of /keys stored without checking
the signature that the exchange master key made over them; denominations,
wire accounts and global fees were all validated.
Diffstat:
1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts
@@ -48,6 +48,7 @@ import {
ExchangeEntryState,
ExchangeGlobalFees,
ExchangeKeysResponse,
+ ExchangeSignKeyJson,
ExchangeListItem,
ExchangeTosStatus,
ExchangeUpdateStatus,
@@ -672,6 +673,32 @@ async function validateWireInfo(
*
* Throw an exception if they are invalid.
*/
+/**
+ * Check the master signature of every online signing key of the exchange.
+ */
+async function validateSignKeys(
+ wex: WalletExecutionContext,
+ signKeys: ExchangeSignKeyJson[],
+ masterPub: string,
+): Promise<void> {
+ for (const sk of signKeys) {
+ logger.trace("validating exchange signing key");
+ let isValid = false;
+ if (wex.ws.config.testing.insecureTrustExchange) {
+ isValid = true;
+ } else {
+ const { valid: v } = await wex.cryptoApi.isValidSignKey({
+ masterPub,
+ sk,
+ });
+ isValid = v;
+ }
+ if (!isValid) {
+ throw Error("exchange signing key signature invalid: " + sk.key);
+ }
+ }
+}
+
async function validateGlobalFees(
wex: WalletExecutionContext,
fees: GlobalFees[],
@@ -1828,6 +1855,8 @@ export async function updateExchangeFromUrlHandler(
keysInfo.master_public_key,
);
+ await validateSignKeys(wex, keysInfo.signkeys, keysInfo.master_public_key);
+
logger.trace("finished validating exchange /wire info");
const tosMeta = await downloadTosMeta(wex, exchangeBaseUrl);
@@ -2031,7 +2060,6 @@ export async function updateExchangeFromUrlHandler(
const drRowId = await tx.upsertExchangeDetails(newDetails);
for (const sk of keysInfo.signkeys) {
- // FIXME: validate signing keys before inserting them
await tx.upsertExchangeSignKey({
exchangeDetailsRowId: drRowId,
masterSig: sk.master_sig,