aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-11-27 20:56:58 +0100
committerFlorian Dold <florian@dold.me>2021-11-27 20:57:07 +0100
commit5c4c25516df9d65d29dc7f3f38b5a2a1a8e9e374 (patch)
tree4665e79a6033ab949de211fd9de8de8ca681c2e0 /packages/taler-wallet-core/src/operations
parent403de8170ef538ef74505859b1c04e3542cad9fb (diff)
downloadwallet-core-5c4c25516df9d65d29dc7f3f38b5a2a1a8e9e374.tar.xz
wallet: support both protocol versions
Diffstat (limited to 'packages/taler-wallet-core/src/operations')
-rw-r--r--packages/taler-wallet-core/src/operations/backup/import.ts6
-rw-r--r--packages/taler-wallet-core/src/operations/backup/index.ts30
-rw-r--r--packages/taler-wallet-core/src/operations/deposits.ts74
-rw-r--r--packages/taler-wallet-core/src/operations/exchanges.ts62
-rw-r--r--packages/taler-wallet-core/src/operations/merchants.ts14
-rw-r--r--packages/taler-wallet-core/src/operations/pay.ts70
-rw-r--r--packages/taler-wallet-core/src/operations/refresh.ts48
-rw-r--r--packages/taler-wallet-core/src/operations/tip.ts14
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts43
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,