diff options
Diffstat (limited to 'packages/taler-wallet-core/src/operations')
9 files changed, 267 insertions, 94 deletions
diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index 40fa4cdec..564d39797 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -27,6 +27,7 @@ import { BackupRefundState, RefreshReason, BackupRefreshReason, + DenomKeyType, } from "@gnu-taler/taler-util"; import { WalletContractData, @@ -331,7 +332,10 @@ export async function importBackup( } for (const backupDenomination of backupExchangeDetails.denominations) { - if (backupDenomination.denom_pub.cipher !== 1) { + if ( + backupDenomination.denom_pub.cipher !== DenomKeyType.Rsa && + backupDenomination.denom_pub.cipher !== DenomKeyType.LegacyRsa + ) { throw Error("unsupported cipher"); } const denomPubHash = diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts index 9027625cd..e3950ef90 100644 --- a/packages/taler-wallet-core/src/operations/backup/index.ts +++ b/packages/taler-wallet-core/src/operations/backup/index.ts @@ -38,14 +38,15 @@ import { codecForString, codecOptional, ConfirmPayResultType, + DenomKeyType, durationFromSpec, getTimestampNow, hashDenomPub, HttpStatusCode, j2s, + LibtoolVersion, Logger, notEmpty, - NotificationType, PreparePayResultType, RecoveryLoadRequest, RecoveryMergeStrategy, @@ -167,7 +168,10 @@ async function computeBackupCryptoData( }; for (const backupExchangeDetails of backupContent.exchange_details) { for (const backupDenom of backupExchangeDetails.denominations) { - if (backupDenom.denom_pub.cipher !== 1) { + if ( + backupDenom.denom_pub.cipher !== DenomKeyType.Rsa && + backupDenom.denom_pub.cipher !== DenomKeyType.LegacyRsa + ) { throw Error("unsupported cipher"); } for (const backupCoin of backupDenom.coins) { @@ -184,9 +188,25 @@ async function computeBackupCryptoData( coinPub, }; } - cryptoData.rsaDenomPubToHash[ - backupDenom.denom_pub.rsa_public_key - ] = encodeCrock(hashDenomPub(backupDenom.denom_pub)); + if ( + LibtoolVersion.compare(backupExchangeDetails.protocol_version, "9") + ?.compatible + ) { + cryptoData.rsaDenomPubToHash[ + backupDenom.denom_pub.rsa_public_key + ] = encodeCrock( + hash(decodeCrock(backupDenom.denom_pub.rsa_public_key)), + ); + } else if ( + LibtoolVersion.compare(backupExchangeDetails.protocol_version, "10") + ?.compatible + ) { + cryptoData.rsaDenomPubToHash[ + backupDenom.denom_pub.rsa_public_key + ] = encodeCrock(hashDenomPub(backupDenom.denom_pub)); + } else { + throw Error("unsupported exchange protocol version"); + } } for (const backupReserve of backupExchangeDetails.reserves) { cryptoData.reservePrivToPub[backupReserve.reserve_priv] = encodeCrock( diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index 8fe3702f5..f90172a45 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -26,6 +26,7 @@ import { CreateDepositGroupRequest, CreateDepositGroupResponse, decodeCrock, + DenomKeyType, durationFromSpec, getTimestampNow, Logger, @@ -59,6 +60,8 @@ import { getCandidatePayCoins, getEffectiveDepositAmount, getTotalPaymentCost, + hashWire, + hashWireLegacy, } from "./pay.js"; /** @@ -103,16 +106,6 @@ const codecForDepositSuccess = (): Codec<DepositSuccess> => .property("transaction_base_url", codecOptional(codecForString())) .build("DepositSuccess"); -function hashWire(paytoUri: string, salt: string): string { - const r = kdf( - 64, - stringToBytes(paytoUri + "\0"), - decodeCrock(salt), - stringToBytes("merchant-wire-signature"), - ); - return encodeCrock(r); -} - async function resetDepositGroupRetry( ws: InternalWalletState, depositGroupId: string, @@ -211,21 +204,50 @@ async function processDepositGroupImpl( continue; } const perm = depositPermissions[i]; + let requestBody: any; + if ( + typeof perm.ub_sig === "string" || + perm.ub_sig.cipher === DenomKeyType.LegacyRsa + ) { + // Legacy request + logger.info("creating legacy deposit request"); + const wireHash = hashWireLegacy( + depositGroup.wire.payto_uri, + depositGroup.wire.salt, + ); + requestBody = { + contribution: Amounts.stringify(perm.contribution), + wire: depositGroup.wire, + h_wire: wireHash, + h_contract_terms: depositGroup.contractTermsHash, + ub_sig: perm.ub_sig, + timestamp: depositGroup.contractTermsRaw.timestamp, + wire_transfer_deadline: + depositGroup.contractTermsRaw.wire_transfer_deadline, + refund_deadline: depositGroup.contractTermsRaw.refund_deadline, + coin_sig: perm.coin_sig, + denom_pub_hash: perm.h_denom, + merchant_pub: depositGroup.merchantPub, + }; + } else { + logger.info("creating v10 deposit request"); + requestBody = { + contribution: Amounts.stringify(perm.contribution), + merchant_payto_uri: depositGroup.wire.payto_uri, + wire_salt: depositGroup.wire.salt, + h_contract_terms: depositGroup.contractTermsHash, + ub_sig: perm.ub_sig, + timestamp: depositGroup.contractTermsRaw.timestamp, + wire_transfer_deadline: + depositGroup.contractTermsRaw.wire_transfer_deadline, + refund_deadline: depositGroup.contractTermsRaw.refund_deadline, + coin_sig: perm.coin_sig, + denom_pub_hash: perm.h_denom, + merchant_pub: depositGroup.merchantPub, + }; + } const url = new URL(`coins/${perm.coin_pub}/deposit`, perm.exchange_url); - const httpResp = await ws.http.postJson(url.href, { - contribution: Amounts.stringify(perm.contribution), - merchant_payto_uri: depositGroup.wire.payto_uri, - wire_salt: depositGroup.wire.salt, - h_contract_terms: depositGroup.contractTermsHash, - ub_sig: perm.ub_sig, - timestamp: depositGroup.contractTermsRaw.timestamp, - wire_transfer_deadline: - depositGroup.contractTermsRaw.wire_transfer_deadline, - refund_deadline: depositGroup.contractTermsRaw.refund_deadline, - coin_sig: perm.coin_sig, - denom_pub_hash: perm.h_denom, - merchant_pub: depositGroup.merchantPub, - }); + const httpResp = await ws.http.postJson(url.href, requestBody); await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess()); await ws.db .mktx((x) => ({ depositGroups: x.depositGroups })) @@ -358,6 +380,7 @@ export async function createDepositGroup( const merchantPair = await ws.cryptoApi.createEddsaKeypair(); const wireSalt = encodeCrock(getRandomBytes(16)); const wireHash = hashWire(req.depositPaytoUri, wireSalt); + const wireHashLegacy = hashWireLegacy(req.depositPaytoUri, wireSalt); const contractTerms: ContractTerms = { auditors: [], exchanges: exchangeInfos, @@ -371,7 +394,10 @@ export async function createDepositGroup( nonce: noncePair.pub, wire_transfer_deadline: timestampRound, order_id: "", + // This is always the v2 wire hash, as we're the "merchant" and support v2. h_wire: wireHash, + // Required for older exchanges. + h_wire_legacy: wireHashLegacy, pay_deadline: timestampAddDuration( timestampRound, durationFromSpec({ hours: 1 }), diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index a10378a8c..16e37fd3e 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -19,11 +19,11 @@ */ import { Amounts, - Auditor, + ExchangeAuditor, canonicalizeBaseUrl, codecForExchangeKeysJson, codecForExchangeWireJson, - Denomination, + ExchangeDenomination, Duration, durationFromSpec, ExchangeSignKeyJson, @@ -40,6 +40,9 @@ import { Timestamp, hashDenomPub, LibtoolVersion, + codecForAny, + DenominationPubKey, + DenomKeyType, } from "@gnu-taler/taler-util"; import { decodeCrock, encodeCrock, hash } from "@gnu-taler/taler-util"; import { CryptoApi } from "../crypto/workers/cryptoApi.js"; @@ -77,11 +80,21 @@ function denominationRecordFromKeys( exchangeBaseUrl: string, exchangeMasterPub: string, listIssueDate: Timestamp, - denomIn: Denomination, + denomIn: ExchangeDenomination, ): DenominationRecord { - const denomPubHash = encodeCrock(hashDenomPub(denomIn.denom_pub)); + let denomPub: DenominationPubKey; + // We support exchange protocol v9 and v10. + if (typeof denomIn.denom_pub === "string") { + denomPub = { + cipher: DenomKeyType.LegacyRsa, + rsa_public_key: denomIn.denom_pub, + }; + } else { + denomPub = denomIn.denom_pub; + } + const denomPubHash = encodeCrock(hashDenomPub(denomPub)); const d: DenominationRecord = { - denomPub: denomIn.denom_pub, + denomPub, denomPubHash, exchangeBaseUrl, exchangeMasterPub, @@ -205,6 +218,7 @@ export async function acceptExchangeTermsOfService( } async function validateWireInfo( + versionCurrent: number, wireInfo: ExchangeWireJson, masterPublicKey: string, cryptoApi: CryptoApi, @@ -212,6 +226,7 @@ async function validateWireInfo( for (const a of wireInfo.accounts) { logger.trace("validating exchange acct"); const isValid = await cryptoApi.isValidWireAccount( + versionCurrent, a.payto_uri, a.master_sig, masterPublicKey, @@ -321,7 +336,7 @@ async function provideExchangeRecord( interface ExchangeKeysDownloadResult { masterPublicKey: string; currency: string; - auditors: Auditor[]; + auditors: ExchangeAuditor[]; currentDenominations: DenominationRecord[]; protocolVersion: string; signingKeys: ExchangeSignKeyJson[]; @@ -345,14 +360,14 @@ async function downloadKeysInfo( const resp = await http.get(keysUrl.href, { timeout, }); - const exchangeKeysJson = await readSuccessResponseJsonOrThrow( + const exchangeKeysJsonUnchecked = await readSuccessResponseJsonOrThrow( resp, codecForExchangeKeysJson(), ); logger.info("received /keys response"); - if (exchangeKeysJson.denoms.length === 0) { + if (exchangeKeysJsonUnchecked.denoms.length === 0) { const opErr = makeErrorDetails( TalerErrorCode.WALLET_EXCHANGE_DENOMINATIONS_INSUFFICIENT, "exchange doesn't offer any denominations", @@ -363,7 +378,7 @@ async function downloadKeysInfo( throw new OperationFailedError(opErr); } - const protocolVersion = exchangeKeysJson.version; + const protocolVersion = exchangeKeysJsonUnchecked.version; const versionRes = LibtoolVersion.compare( WALLET_EXCHANGE_PROTOCOL_VERSION, @@ -382,29 +397,29 @@ async function downloadKeysInfo( } const currency = Amounts.parseOrThrow( - exchangeKeysJson.denoms[0].value, + exchangeKeysJsonUnchecked.denoms[0].value, ).currency.toUpperCase(); return { - masterPublicKey: exchangeKeysJson.master_public_key, + masterPublicKey: exchangeKeysJsonUnchecked.master_public_key, currency, - auditors: exchangeKeysJson.auditors, - currentDenominations: exchangeKeysJson.denoms.map((d) => + auditors: exchangeKeysJsonUnchecked.auditors, + currentDenominations: exchangeKeysJsonUnchecked.denoms.map((d) => denominationRecordFromKeys( baseUrl, - exchangeKeysJson.master_public_key, - exchangeKeysJson.list_issue_date, + exchangeKeysJsonUnchecked.master_public_key, + exchangeKeysJsonUnchecked.list_issue_date, d, ), ), - protocolVersion: exchangeKeysJson.version, - signingKeys: exchangeKeysJson.signkeys, - reserveClosingDelay: exchangeKeysJson.reserve_closing_delay, + protocolVersion: exchangeKeysJsonUnchecked.version, + signingKeys: exchangeKeysJsonUnchecked.signkeys, + reserveClosingDelay: exchangeKeysJsonUnchecked.reserve_closing_delay, expiry: getExpiryTimestamp(resp, { minDuration: durationFromSpec({ hours: 1 }), }), - recoup: exchangeKeysJson.recoup ?? [], - listIssueDate: exchangeKeysJson.list_issue_date, + recoup: exchangeKeysJsonUnchecked.recoup ?? [], + listIssueDate: exchangeKeysJsonUnchecked.list_issue_date, }; } @@ -466,7 +481,14 @@ async function updateExchangeFromUrlImpl( logger.info("validating exchange /wire info"); + const version = LibtoolVersion.parseVersion(keysInfo.protocolVersion); + if (!version) { + // Should have been validated earlier. + throw Error("unexpected invalid version"); + } + const wireInfo = await validateWireInfo( + version.current, wireInfoDownload, keysInfo.masterPublicKey, ws.cryptoApi, diff --git a/packages/taler-wallet-core/src/operations/merchants.ts b/packages/taler-wallet-core/src/operations/merchants.ts index d12417c7c..fd628fa98 100644 --- a/packages/taler-wallet-core/src/operations/merchants.ts +++ b/packages/taler-wallet-core/src/operations/merchants.ts @@ -52,15 +52,13 @@ export async function getMerchantInfo( `merchant "${canonBaseUrl}" reports protocol ${configResp.version}"`, ); + const parsedVersion = LibtoolVersion.parseVersion(configResp.version); + if (!parsedVersion) { + throw Error("invalid merchant version"); + } + const merchantInfo: MerchantInfo = { - supportsMerchantProtocolV1: !!LibtoolVersion.compare( - "1:0:0", - configResp.version, - )?.compatible, - supportsMerchantProtocolV2: !!LibtoolVersion.compare( - "2:0:0", - configResp.version, - )?.compatible, + protocolVersionCurrent: parsedVersion.current, }; ws.merchantInfoCache[canonBaseUrl] = merchantInfo; diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts index acc592a72..73fc6537c 100644 --- a/packages/taler-wallet-core/src/operations/pay.ts +++ b/packages/taler-wallet-core/src/operations/pay.ts @@ -54,6 +54,10 @@ import { URL, getDurationRemaining, HttpStatusCode, + DenomKeyType, + kdf, + stringToBytes, + decodeCrock, } from "@gnu-taler/taler-util"; import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util"; import { @@ -108,6 +112,26 @@ import { */ const logger = new Logger("pay.ts"); +export function hashWire(paytoUri: string, salt: string): string { + const r = kdf( + 64, + stringToBytes(paytoUri + "\0"), + decodeCrock(salt), + stringToBytes("merchant-wire-signature"), + ); + return encodeCrock(r); +} + +export function hashWireLegacy(paytoUri: string, salt: string): string { + const r = kdf( + 64, + stringToBytes(paytoUri + "\0"), + stringToBytes(salt + "\0"), + stringToBytes("merchant-wire-signature"), + ); + return encodeCrock(r); +} + /** * Compute the total cost of a payment to the customer. * @@ -193,9 +217,9 @@ export async function getEffectiveDepositAmount( if (!exchangeDetails) { continue; } - // FIXME/NOTE: the line below _likely_ throws exception - // about "find method not found on undefined" when the wireType - // is not supported by the Exchange. + // FIXME/NOTE: the line below _likely_ throws exception + // about "find method not found on undefined" when the wireType + // is not supported by the Exchange. const fee = exchangeDetails.wireInfo.feesForType[wireType].find((x) => { return timestampIsBetween( getTimestampNow(), @@ -669,6 +693,7 @@ export function extractContractData( timestamp: parsedContractTerms.timestamp, wireMethod: parsedContractTerms.wire_method, wireInfoHash: parsedContractTerms.h_wire, + wireInfoLegacyHash: parsedContractTerms.h_wire_legacy, maxDepositFee: Amounts.parseOrThrow(parsedContractTerms.max_fee), merchant: parsedContractTerms.merchant, products: parsedContractTerms.products, @@ -882,7 +907,6 @@ async function startDownloadProposal( claimToken: string | undefined, noncePriv: string | undefined, ): Promise<string> { - const oldProposal = await ws.db .mktx((x) => ({ proposals: x.proposals })) .runReadOnly(async (tx) => { @@ -891,20 +915,24 @@ async function startDownloadProposal( orderId, ]); }); - + /** * If we have already claimed this proposal with the same sessionId * nonce and claim token, reuse it. */ - if (oldProposal && - oldProposal.downloadSessionId === sessionId && - (!noncePriv || oldProposal.noncePriv === noncePriv) && - oldProposal.claimToken === claimToken) { + if ( + oldProposal && + oldProposal.downloadSessionId === sessionId && + (!noncePriv || oldProposal.noncePriv === noncePriv) && + oldProposal.claimToken === claimToken + ) { await processDownloadProposal(ws, oldProposal.proposalId); return oldProposal.proposalId; } - const { priv, pub } = await (noncePriv ? ws.cryptoApi.eddsaGetPublic(noncePriv) : ws.cryptoApi.createEddsaKeypair()); + const { priv, pub } = await (noncePriv + ? ws.cryptoApi.eddsaGetPublic(noncePriv) + : ws.cryptoApi.createEddsaKeypair()); const proposalId = encodeCrock(getRandomBytes(32)); const proposalRecord: ProposalRecord = { @@ -1169,6 +1197,11 @@ async function submitPay( logger.trace("paying with session ID", sessionId); + const merchantInfo = await ws.merchantOps.getMerchantInfo( + ws, + purchase.download.contractData.merchantBaseUrl, + ); + if (!purchase.merchantPaySig) { const payUrl = new URL( `orders/${purchase.download.contractData.orderId}/pay`, @@ -1568,11 +1601,21 @@ export async function generateDepositPermissions( for (let i = 0; i < payCoinSel.coinPubs.length; i++) { const { coin, denom } = coinWithDenom[i]; + let wireInfoHash: string; + if ( + coin.denomPub.cipher === DenomKeyType.LegacyRsa && + contractData.wireInfoLegacyHash + ) { + wireInfoHash = contractData.wireInfoLegacyHash; + } else { + wireInfoHash = contractData.wireInfoHash; + } const dp = await ws.cryptoApi.signDepositPermission({ coinPriv: coin.coinPriv, coinPub: coin.coinPub, contractTermsHash: contractData.contractTermsHash, denomPubHash: coin.denomPubHash, + denomKeyType: coin.denomPub.cipher, denomSig: coin.denomSig, exchangeBaseUrl: coin.exchangeBaseUrl, feeDeposit: denom.feeDeposit, @@ -1580,7 +1623,7 @@ export async function generateDepositPermissions( refundDeadline: contractData.refundDeadline, spendAmount: payCoinSel.coinContributions[i], timestamp: contractData.timestamp, - wireInfoHash: contractData.wireInfoHash, + wireInfoHash, }); depositPermissions.push(dp); } @@ -1613,6 +1656,11 @@ export async function confirmPay( throw Error("proposal is in invalid state"); } + const merchantInfo = await ws.merchantOps.getMerchantInfo( + ws, + d.contractData.merchantBaseUrl, + ); + const existingPurchase = await ws.db .mktx((x) => ({ purchases: x.purchases })) .runReadWrite(async (tx) => { diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index c1e672d63..51eac4a64 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -365,18 +365,29 @@ async function refreshMelt( `coins/${oldCoin.coinPub}/melt`, oldCoin.exchangeBaseUrl, ); - const meltReq = { - coin_pub: oldCoin.coinPub, - confirm_sig: derived.confirmSig, - denom_pub_hash: oldCoin.denomPubHash, - denom_sig: oldCoin.denomSig, - rc: derived.hash, - value_with_fee: Amounts.stringify(derived.meltValueWithFee), - }; - logger.trace(`melt request for coin:`, meltReq); + let meltReqBody: any; + if (oldCoin.denomPub.cipher === DenomKeyType.LegacyRsa) { + meltReqBody = { + coin_pub: oldCoin.coinPub, + confirm_sig: derived.confirmSig, + denom_pub_hash: oldCoin.denomPubHash, + denom_sig: oldCoin.denomSig.rsa_signature, + rc: derived.hash, + value_with_fee: Amounts.stringify(derived.meltValueWithFee), + }; + } else { + meltReqBody = { + coin_pub: oldCoin.coinPub, + confirm_sig: derived.confirmSig, + denom_pub_hash: oldCoin.denomPubHash, + denom_sig: oldCoin.denomSig, + rc: derived.hash, + value_with_fee: Amounts.stringify(derived.meltValueWithFee), + }; + } const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], async () => { - return await ws.http.postJson(reqUrl.href, meltReq, { + return await ws.http.postJson(reqUrl.href, meltReqBody, { timeout: getRefreshRequestTimeout(refreshGroup), }); }); @@ -604,15 +615,26 @@ async function refreshReveal( continue; } const pc = derived.planchetsForGammas[norevealIndex][newCoinIndex]; - if (denom.denomPub.cipher !== 1) { + if ( + denom.denomPub.cipher !== DenomKeyType.Rsa && + denom.denomPub.cipher !== DenomKeyType.LegacyRsa + ) { throw Error("cipher unsupported"); } const evSig = reveal.ev_sigs[newCoinIndex].ev_sig; - if (evSig.cipher !== DenomKeyType.Rsa) { + let rsaSig: string; + if (typeof evSig === "string") { + rsaSig = evSig; + } else if ( + evSig.cipher === DenomKeyType.Rsa || + evSig.cipher === DenomKeyType.LegacyRsa + ) { + rsaSig = evSig.blinded_rsa_signature; + } else { throw Error("unsupported cipher"); } const denomSigRsa = await ws.cryptoApi.rsaUnblind( - evSig.blinded_rsa_signature, + rsaSig, pc.blindingKey, denom.denomPub.rsa_public_key, ); diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts index 0253930ea..cf3502ecd 100644 --- a/packages/taler-wallet-core/src/operations/tip.ts +++ b/packages/taler-wallet-core/src/operations/tip.ts @@ -314,13 +314,13 @@ async function processTipImpl( let blindedSigs: BlindedDenominationSignature[] = []; - if (merchantInfo.supportsMerchantProtocolV2) { + if (merchantInfo.protocolVersionCurrent === 2) { const response = await readSuccessResponseJsonOrThrow( merchantResp, codecForMerchantTipResponseV2(), ); blindedSigs = response.blind_sigs.map((x) => x.blind_sig); - } else if (merchantInfo.supportsMerchantProtocolV1) { + } else if (merchantInfo.protocolVersionCurrent === 1) { const response = await readSuccessResponseJsonOrThrow( merchantResp, codecForMerchantTipResponseV1(), @@ -347,11 +347,17 @@ async function processTipImpl( const planchet = planchets[i]; checkLogicInvariant(!!planchet); - if (denom.denomPub.cipher !== DenomKeyType.Rsa) { + if ( + denom.denomPub.cipher !== DenomKeyType.Rsa && + denom.denomPub.cipher !== DenomKeyType.LegacyRsa + ) { throw Error("unsupported cipher"); } - if (blindedSig.cipher !== DenomKeyType.Rsa) { + if ( + blindedSig.cipher !== DenomKeyType.Rsa && + blindedSig.cipher !== DenomKeyType.LegacyRsa + ) { throw Error("unsupported cipher"); } diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 979bd0e53..8c9178f59 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -42,6 +42,7 @@ import { VersionMatchResult, DenomKeyType, LibtoolVersion, + UnblindedSignature, } from "@gnu-taler/taler-util"; import { CoinRecord, @@ -591,12 +592,28 @@ async function processPlanchetVerifyAndStoreCoin( const { planchet, exchangeBaseUrl } = d; const planchetDenomPub = planchet.denomPub; - if (planchetDenomPub.cipher !== DenomKeyType.Rsa) { - throw Error("cipher not supported"); + if ( + planchetDenomPub.cipher !== DenomKeyType.Rsa && + planchetDenomPub.cipher !== DenomKeyType.LegacyRsa + ) { + throw Error(`cipher (${planchetDenomPub.cipher}) not supported`); } - const evSig = resp.ev_sig; - if (evSig.cipher !== DenomKeyType.Rsa) { + let evSig = resp.ev_sig; + if (typeof resp.ev_sig === "string") { + evSig = { + cipher: DenomKeyType.LegacyRsa, + blinded_rsa_signature: resp.ev_sig, + }; + } else { + evSig = resp.ev_sig; + } + if ( + !( + evSig.cipher === DenomKeyType.Rsa || + evSig.cipher === DenomKeyType.LegacyRsa + ) + ) { throw Error("unsupported cipher"); } @@ -633,6 +650,19 @@ async function processPlanchetVerifyAndStoreCoin( return; } + let denomSig: UnblindedSignature; + if ( + planchet.denomPub.cipher === DenomKeyType.LegacyRsa || + planchet.denomPub.cipher === DenomKeyType.Rsa + ) { + denomSig = { + cipher: planchet.denomPub.cipher, + rsa_signature: denomSigRsa, + }; + } else { + throw Error("unsupported cipher"); + } + const coin: CoinRecord = { blindingKey: planchet.blindingKey, coinPriv: planchet.coinPriv, @@ -640,10 +670,7 @@ async function processPlanchetVerifyAndStoreCoin( currentAmount: planchet.coinValue, denomPub: planchet.denomPub, denomPubHash: planchet.denomPubHash, - denomSig: { - cipher: DenomKeyType.Rsa, - rsa_signature: denomSigRsa, - }, + denomSig, coinEvHash: planchet.coinEvHash, exchangeBaseUrl: exchangeBaseUrl, status: CoinStatus.Fresh, |