aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-11-23 23:51:12 +0100
committerFlorian Dold <florian@dold.me>2021-11-23 23:51:12 +0100
commitae8af3f27c0ed1746c49a7608fe05af24ae8a18b (patch)
treec28f588071bdd1d4cda2279e62563a3664d79be9 /packages/taler-wallet-core
parent829a59e1a24d6a99ce7554d28acfd05f21baeaf8 (diff)
downloadwallet-core-ae8af3f27c0ed1746c49a7608fe05af24ae8a18b.tar.xz
wallet: tipping protocol change / merchant version info
Diffstat (limited to 'packages/taler-wallet-core')
-rw-r--r--packages/taler-wallet-core/src/common.ts18
-rw-r--r--packages/taler-wallet-core/src/db.ts6
-rw-r--r--packages/taler-wallet-core/src/operations/backup/export.ts1
-rw-r--r--packages/taler-wallet-core/src/operations/backup/import.ts1
-rw-r--r--packages/taler-wallet-core/src/operations/exchanges.ts8
-rw-r--r--packages/taler-wallet-core/src/operations/merchants.ts68
-rw-r--r--packages/taler-wallet-core/src/operations/tip.ts46
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts6
-rw-r--r--packages/taler-wallet-core/src/wallet.ts9
9 files changed, 149 insertions, 14 deletions
diff --git a/packages/taler-wallet-core/src/common.ts b/packages/taler-wallet-core/src/common.ts
index dd8542def..81c43cf14 100644
--- a/packages/taler-wallet-core/src/common.ts
+++ b/packages/taler-wallet-core/src/common.ts
@@ -51,6 +51,21 @@ export interface TrustInfo {
isAudited: boolean;
}
+export interface MerchantInfo {
+ supportsMerchantProtocolV1: boolean;
+ supportsMerchantProtocolV2: boolean;
+}
+
+/**
+ * Interface for merchant-related operations.
+ */
+export interface MerchantOperations {
+ getMerchantInfo(
+ ws: InternalWalletState,
+ merchantBaseUrl: string,
+ ): Promise<MerchantInfo>;
+}
+
/**
* Interface for exchange-related operations.
*/
@@ -131,8 +146,11 @@ export interface InternalWalletState {
initCalled: boolean;
+ merchantInfoCache: Record<string, MerchantInfo>;
+
exchangeOps: ExchangeOperations;
recoupOps: RecoupOperations;
+ merchantOps: MerchantOperations;
db: DbAccess<typeof WalletStoresV1>;
http: HttpRequestLibrary;
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index 483cb16c2..ff47cf30d 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -484,9 +484,15 @@ export interface WireInfo {
export interface ExchangeDetailsPointer {
masterPublicKey: string;
+
currency: string;
/**
+ * Last observed protocol version range offered by the exchange.
+ */
+ protocolVersionRange: string;
+
+ /**
* Timestamp when the (masterPublicKey, currency) pointer
* has been updated.
*/
diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts b/packages/taler-wallet-core/src/operations/backup/export.ts
index a66bc2e84..75724dca7 100644
--- a/packages/taler-wallet-core/src/operations/backup/export.ts
+++ b/packages/taler-wallet-core/src/operations/backup/export.ts
@@ -273,6 +273,7 @@ export async function exportBackup(
currency: dp.currency,
master_public_key: dp.masterPublicKey,
update_clock: dp.updateClock,
+ protocol_version_range: dp.protocolVersionRange,
});
});
diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts
index e8e1de0b9..40fa4cdec 100644
--- a/packages/taler-wallet-core/src/operations/backup/import.ts
+++ b/packages/taler-wallet-core/src/operations/backup/import.ts
@@ -267,6 +267,7 @@ export async function importBackup(
currency: backupExchange.currency,
masterPublicKey: backupExchange.master_public_key,
updateClock: backupExchange.update_clock,
+ protocolVersionRange: backupExchange.protocol_version_range,
},
permanent: true,
retryInfo: initRetryInfo(),
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts
index c170c5469..638af813a 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -23,7 +23,6 @@ import {
canonicalizeBaseUrl,
codecForExchangeKeysJson,
codecForExchangeWireJson,
- compare,
Denomination,
Duration,
durationFromSpec,
@@ -40,6 +39,7 @@ import {
TalerErrorDetails,
Timestamp,
hashDenomPub,
+ LibtoolVersion,
} from "@gnu-taler/taler-util";
import { decodeCrock, encodeCrock, hash } from "@gnu-taler/taler-util";
import { CryptoApi } from "../crypto/workers/cryptoApi.js";
@@ -365,7 +365,10 @@ async function downloadKeysInfo(
const protocolVersion = exchangeKeysJson.version;
- const versionRes = compare(WALLET_EXCHANGE_PROTOCOL_VERSION, protocolVersion);
+ const versionRes = LibtoolVersion.compare(
+ WALLET_EXCHANGE_PROTOCOL_VERSION,
+ protocolVersion,
+ );
if (versionRes?.compatible != true) {
const opErr = makeErrorDetails(
TalerErrorCode.WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE,
@@ -548,6 +551,7 @@ async function updateExchangeFromUrlImpl(
masterPublicKey: details.masterPublicKey,
// FIXME: only change if pointer really changed
updateClock: getTimestampNow(),
+ protocolVersionRange: keysInfo.protocolVersion,
};
await tx.exchanges.put(r);
await tx.exchangeDetails.put(details);
diff --git a/packages/taler-wallet-core/src/operations/merchants.ts b/packages/taler-wallet-core/src/operations/merchants.ts
new file mode 100644
index 000000000..d12417c7c
--- /dev/null
+++ b/packages/taler-wallet-core/src/operations/merchants.ts
@@ -0,0 +1,68 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A..
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import {
+ canonicalizeBaseUrl,
+ Logger,
+ URL,
+ codecForMerchantConfigResponse,
+ LibtoolVersion,
+} from "@gnu-taler/taler-util";
+import { InternalWalletState, MerchantInfo } from "../common.js";
+import { readSuccessResponseJsonOrThrow } from "../index.js";
+
+const logger = new Logger("taler-wallet-core:merchants.ts");
+
+export async function getMerchantInfo(
+ ws: InternalWalletState,
+ merchantBaseUrl: string,
+): Promise<MerchantInfo> {
+ const canonBaseUrl = canonicalizeBaseUrl(merchantBaseUrl);
+
+ const existingInfo = ws.merchantInfoCache[canonBaseUrl];
+ if (existingInfo) {
+ return existingInfo;
+ }
+
+ const configUrl = new URL("config", canonBaseUrl);
+ const resp = await ws.http.get(configUrl.href);
+
+ const configResp = await readSuccessResponseJsonOrThrow(
+ resp,
+ codecForMerchantConfigResponse(),
+ );
+
+ logger.info(
+ `merchant "${canonBaseUrl}" reports protocol ${configResp.version}"`,
+ );
+
+ const merchantInfo: MerchantInfo = {
+ supportsMerchantProtocolV1: !!LibtoolVersion.compare(
+ "1:0:0",
+ configResp.version,
+ )?.compatible,
+ supportsMerchantProtocolV2: !!LibtoolVersion.compare(
+ "2:0:0",
+ configResp.version,
+ )?.compatible,
+ };
+
+ ws.merchantInfoCache[canonBaseUrl] = merchantInfo;
+ return merchantInfo;
+}
diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts
index 07ce00d2e..0253930ea 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -27,10 +27,12 @@ import {
NotificationType,
TipPlanchetDetail,
TalerErrorCode,
- codecForTipResponse,
+ codecForMerchantTipResponseV1,
Logger,
URL,
DenomKeyType,
+ BlindedDenominationSignature,
+ codecForMerchantTipResponseV2,
} from "@gnu-taler/taler-util";
import { DerivedTipPlanchet } from "../crypto/cryptoTypes.js";
import {
@@ -304,31 +306,57 @@ async function processTipImpl(
return;
}
- const response = await readSuccessResponseJsonOrThrow(
- merchantResp,
- codecForTipResponse(),
+ // FIXME: Do this earlier?
+ const merchantInfo = await ws.merchantOps.getMerchantInfo(
+ ws,
+ tipRecord.merchantBaseUrl,
);
- if (response.blind_sigs.length !== planchets.length) {
+ let blindedSigs: BlindedDenominationSignature[] = [];
+
+ if (merchantInfo.supportsMerchantProtocolV2) {
+ const response = await readSuccessResponseJsonOrThrow(
+ merchantResp,
+ codecForMerchantTipResponseV2(),
+ );
+ blindedSigs = response.blind_sigs.map((x) => x.blind_sig);
+ } else if (merchantInfo.supportsMerchantProtocolV1) {
+ const response = await readSuccessResponseJsonOrThrow(
+ merchantResp,
+ codecForMerchantTipResponseV1(),
+ );
+ blindedSigs = response.blind_sigs.map((x) => ({
+ cipher: DenomKeyType.Rsa,
+ blinded_rsa_signature: x.blind_sig,
+ }));
+ } else {
+ throw Error("unsupported merchant protocol version");
+ }
+
+ if (blindedSigs.length !== planchets.length) {
throw Error("number of tip responses does not match requested planchets");
}
const newCoinRecords: CoinRecord[] = [];
- for (let i = 0; i < response.blind_sigs.length; i++) {
- const blindedSig = response.blind_sigs[i].blind_sig;
+ for (let i = 0; i < blindedSigs.length; i++) {
+ const blindedSig = blindedSigs[i];
const denom = denomForPlanchet[i];
checkLogicInvariant(!!denom);
const planchet = planchets[i];
checkLogicInvariant(!!planchet);
- if (denom.denomPub.cipher !== 1) {
+ if (denom.denomPub.cipher !== DenomKeyType.Rsa) {
+ throw Error("unsupported cipher");
+ }
+
+ if (blindedSig.cipher !== DenomKeyType.Rsa) {
throw Error("unsupported cipher");
}
const denomSigRsa = await ws.cryptoApi.rsaUnblind(
- blindedSig,
+ blindedSig.blinded_rsa_signature,
planchet.blindingKey,
denom.denomPub.rsa_public_key,
);
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index 57bd49d23..a5a8653c6 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -24,7 +24,6 @@ import {
codecForTalerConfigResponse,
codecForWithdrawOperationStatusResponse,
codecForWithdrawResponse,
- compare,
durationFromSpec,
ExchangeListItem,
getDurationRemaining,
@@ -42,6 +41,7 @@ import {
WithdrawUriInfoResponse,
VersionMatchResult,
DenomKeyType,
+ LibtoolVersion,
} from "@gnu-taler/taler-util";
import {
CoinRecord,
@@ -285,7 +285,7 @@ export async function getBankWithdrawalInfo(
codecForTalerConfigResponse(),
);
- const versionRes = compare(
+ const versionRes = LibtoolVersion.compare(
WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
config.version,
);
@@ -985,7 +985,7 @@ export async function getExchangeWithdrawalInfo(
let versionMatch;
if (exchangeDetails.protocolVersion) {
- versionMatch = compare(
+ versionMatch = LibtoolVersion.compare(
WALLET_EXCHANGE_PROTOCOL_VERSION,
exchangeDetails.protocolVersion,
);
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index cd2dd7f1e..44591a268 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -99,6 +99,8 @@ import {
import {
ExchangeOperations,
InternalWalletState,
+ MerchantInfo,
+ MerchantOperations,
NotificationListener,
RecoupOperations,
} from "./common.js";
@@ -180,6 +182,7 @@ import {
HttpRequestLibrary,
readSuccessResponseJsonOrThrow,
} from "./util/http.js";
+import { getMerchantInfo } from "./operations/merchants.js";
const builtinAuditors: AuditorTrustRecord[] = [
{
@@ -1069,6 +1072,8 @@ class InternalWalletStateImpl implements InternalWalletState {
memoProcessDeposit: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
cryptoApi: CryptoApi;
+ merchantInfoCache: Record<string, MerchantInfo> = {};
+
timerGroup: TimerGroup = new TimerGroup();
latch = new AsyncCondition();
stopped = false;
@@ -1088,6 +1093,10 @@ class InternalWalletStateImpl implements InternalWalletState {
processRecoupGroup: processRecoupGroup,
};
+ merchantOps: MerchantOperations = {
+ getMerchantInfo: getMerchantInfo,
+ };
+
/**
* Promises that are waiting for a particular resource.
*/