taler-typescript-core

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

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:
Mpackages/taler-wallet-core/src/exchanges.ts | 30+++++++++++++++++++++++++++++-
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,