From 9f0429cb2f8ad9cb2e98a787139602d913c1aefa Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 17 Nov 2021 10:23:22 +0100 Subject: [PATCH] wallet: implement exchange protocol v9 --- package.json | 2 +- packages/taler-util/src/backupTypes.ts | 5 +- packages/taler-util/src/helpers.ts | 11 +- packages/taler-util/src/talerCrypto.ts | 15 + packages/taler-util/src/talerTypes.ts | 77 +++- packages/taler-util/src/walletTypes.ts | 8 +- packages/taler-wallet-core/package.json | 2 +- .../src/crypto/cryptoTypes.ts | 6 +- .../crypto/workers/cryptoImplementation.ts | 50 ++- packages/taler-wallet-core/src/db.ts | 13 +- packages/taler-wallet-core/src/errors.ts | 2 +- .../src/operations/backup/import.ts | 9 +- .../src/operations/backup/index.ts | 19 +- .../src/operations/deposits.ts | 9 +- .../src/operations/exchanges.ts | 42 +- .../src/operations/refresh.ts | 25 +- .../taler-wallet-core/src/operations/tip.ts | 15 +- .../src/operations/withdraw.test.ts | 44 ++- .../src/operations/withdraw.ts | 35 +- .../src/util/coinSelection.test.ts | 5 +- .../src/util/coinSelection.ts | 21 +- packages/taler-wallet-core/src/wallet.ts | 3 +- pnpm-lock.yaml | 371 +++++++++++++++--- 23 files changed, 619 insertions(+), 170 deletions(-) diff --git a/package.json b/package.json index d857a0113..e00de0c53 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,6 @@ "@linaria/esbuild": "^3.0.0-beta.13", "@linaria/shaker": "^3.0.0-beta.13", "esbuild": "^0.12.29", - "prettier": "^2.2.1" + "prettier": "^2.4.1" } } diff --git a/packages/taler-util/src/backupTypes.ts b/packages/taler-util/src/backupTypes.ts index 70e52e63b..ecdd6fdf8 100644 --- a/packages/taler-util/src/backupTypes.ts +++ b/packages/taler-util/src/backupTypes.ts @@ -53,6 +53,7 @@ /** * Imports. */ +import { DenominationPubKey, UnblindedSignature } from "./talerTypes.js"; import { Duration, Timestamp } from "./time.js"; /** @@ -440,7 +441,7 @@ export interface BackupCoin { /** * Unblinded signature by the exchange. */ - denom_sig: string; + denom_sig: UnblindedSignature; /** * Amount that's left on the coin. @@ -831,7 +832,7 @@ export interface BackupDenomination { /** * The denomination public key. */ - denom_pub: string; + denom_pub: DenominationPubKey; /** * Fee for withdrawing. diff --git a/packages/taler-util/src/helpers.ts b/packages/taler-util/src/helpers.ts index 089602c9d..6c836c482 100644 --- a/packages/taler-util/src/helpers.ts +++ b/packages/taler-util/src/helpers.ts @@ -94,7 +94,7 @@ export function canonicalJson(obj: any): string { /** * Lexically compare two strings. */ -export function strcmp(s1: string, s2: string): number { +export function strcmp(s1: string, s2: string): -1 | 0 | 1 { if (s1 < s2) { return -1; } @@ -113,15 +113,14 @@ export function j2s(x: any): string { /** * Use this to filter null or undefined from an array in a type-safe fashion - * + * * example: * const array: Array = [undefined, null] * const filtered: Array = array.filter(notEmpty) - * - * @param value - * @returns + * + * @param value + * @returns */ export function notEmpty(value: T | null | undefined): value is T { return value !== null && value !== undefined; } - diff --git a/packages/taler-util/src/talerCrypto.ts b/packages/taler-util/src/talerCrypto.ts index d8ac75dc0..b107786cd 100644 --- a/packages/taler-util/src/talerCrypto.ts +++ b/packages/taler-util/src/talerCrypto.ts @@ -24,6 +24,7 @@ import * as nacl from "./nacl-fast.js"; import { kdf } from "./kdf.js"; import bigint from "big-integer"; +import { DenominationPubKey } from "./talerTypes.js"; export function getRandomBytes(n: number): Uint8Array { return nacl.randomBytes(n); @@ -348,6 +349,20 @@ export function hash(d: Uint8Array): Uint8Array { return nacl.hash(d); } +export function hashDenomPub(pub: DenominationPubKey): Uint8Array { + if (pub.cipher !== 1) { + throw Error("unsupported cipher"); + } + const pubBuf = decodeCrock(pub.rsa_public_key); + const hashInputBuf = new ArrayBuffer(pubBuf.length + 4 + 4); + const uint8ArrayBuf = new Uint8Array(hashInputBuf); + const dv = new DataView(hashInputBuf); + dv.setUint32(0, pub.age_mask ?? 0); + dv.setUint32(4, pub.cipher); + uint8ArrayBuf.set(pubBuf, 8); + return nacl.hash(uint8ArrayBuf); +} + export function eddsaSign(msg: Uint8Array, eddsaPriv: Uint8Array): Uint8Array { const pair = nacl.crypto_sign_keyPair_fromSeed(eddsaPriv); return nacl.sign_detached(msg, pair.secretKey); diff --git a/packages/taler-util/src/talerTypes.ts b/packages/taler-util/src/talerTypes.ts index 56110ec1e..04d700483 100644 --- a/packages/taler-util/src/talerTypes.ts +++ b/packages/taler-util/src/talerTypes.ts @@ -59,7 +59,7 @@ export class Denomination { /** * Public signing key of the denomination. */ - denom_pub: string; + denom_pub: DenominationPubKey; /** * Fee for withdrawing. @@ -158,7 +158,7 @@ export interface RecoupRequest { /** * Signature over the coin public key by the denomination. */ - denom_sig: string; + denom_sig: UnblindedSignature; /** * Coin public key of the coin we want to refund. @@ -198,6 +198,11 @@ export interface RecoupConfirmation { old_coin_pub?: string; } +export interface UnblindedSignature { + cipher: DenomKeyType.Rsa; + rsa_signature: string; +} + /** * Deposit permission for a single coin. */ @@ -213,7 +218,7 @@ export interface CoinDepositPermission { /** * Signature made by the denomination public key. */ - ub_sig: string; + ub_sig: UnblindedSignature; /** * The denomination public key associated with this coin. */ @@ -779,8 +784,38 @@ export class TipPickupGetResponse { expiration: Timestamp; } +export enum DenomKeyType { + Rsa = 1, + ClauseSchnorr = 2, +} + +export interface RsaBlindedDenominationSignature { + cipher: DenomKeyType.Rsa; + blinded_rsa_signature: string; +} + +export interface CSBlindedDenominationSignature { + cipher: DenomKeyType.ClauseSchnorr; +} + +export type BlindedDenominationSignature = + | RsaBlindedDenominationSignature + | CSBlindedDenominationSignature; + +export const codecForBlindedDenominationSignature = () => + buildCodecForUnion() + .discriminateOn("cipher") + .alternative(1, codecForRsaBlindedDenominationSignature()) + .build("BlindedDenominationSignature"); + +export const codecForRsaBlindedDenominationSignature = () => + buildCodecForObject() + .property("cipher", codecForConstNumber(1)) + .property("blinded_rsa_signature", codecForString()) + .build("RsaBlindedDenominationSignature"); + export class WithdrawResponse { - ev_sig: string; + ev_sig: BlindedDenominationSignature; } /** @@ -792,7 +827,7 @@ export interface CoinDumpJson { /** * The coin's denomination's public key. */ - denom_pub: string; + denom_pub: DenominationPubKey; /** * Hash of denom_pub. */ @@ -875,7 +910,7 @@ export interface ExchangeMeltResponse { } export interface ExchangeRevealItem { - ev_sig: string; + ev_sig: BlindedDenominationSignature; } export interface ExchangeRevealResponse { @@ -994,6 +1029,30 @@ export interface BankWithdrawalOperationPostResponse { transfer_done: boolean; } +export type DenominationPubKey = RsaDenominationPubKey | CsDenominationPubKey; + +export interface RsaDenominationPubKey { + cipher: 1; + rsa_public_key: string; + age_mask?: number; +} + +export interface CsDenominationPubKey { + cipher: 2; +} + +export const codecForDenominationPubKey = () => + buildCodecForUnion() + .discriminateOn("cipher") + .alternative(1, codecForRsaDenominationPubKey()) + .build("DenominationPubKey"); + +export const codecForRsaDenominationPubKey = () => + buildCodecForObject() + .property("cipher", codecForConstNumber(1)) + .property("rsa_public_key", codecForString()) + .build("DenominationPubKey"); + export const codecForBankWithdrawalOperationPostResponse = (): Codec => buildCodecForObject() .property("transfer_done", codecForBoolean()) @@ -1008,7 +1067,7 @@ export type CoinPublicKeyString = string; export const codecForDenomination = (): Codec => buildCodecForObject() .property("value", codecForString()) - .property("denom_pub", codecForString()) + .property("denom_pub", codecForDenominationPubKey()) .property("fee_withdraw", codecForString()) .property("fee_deposit", codecForString()) .property("fee_refresh", codecForString()) @@ -1242,7 +1301,7 @@ export const codecForRecoupConfirmation = (): Codec => export const codecForWithdrawResponse = (): Codec => buildCodecForObject() - .property("ev_sig", codecForString()) + .property("ev_sig", codecForBlindedDenominationSignature()) .build("WithdrawResponse"); export const codecForMerchantPayResponse = (): Codec => @@ -1260,7 +1319,7 @@ export const codecForExchangeMeltResponse = (): Codec => export const codecForExchangeRevealItem = (): Codec => buildCodecForObject() - .property("ev_sig", codecForString()) + .property("ev_sig", codecForBlindedDenominationSignature()) .build("ExchangeRevealItem"); export const codecForExchangeRevealResponse = (): Codec => diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts index 6e68ee080..879640e82 100644 --- a/packages/taler-util/src/walletTypes.ts +++ b/packages/taler-util/src/walletTypes.ts @@ -48,6 +48,8 @@ import { AmountString, codecForContractTerms, ContractTerms, + DenominationPubKey, + UnblindedSignature, } from "./talerTypes.js"; import { OrderShortInfo, codecForOrderShortInfo } from "./transactionsTypes.js"; import { BackupRecovery } from "./backupTypes.js"; @@ -454,7 +456,7 @@ export interface PlanchetCreationResult { coinPriv: string; reservePub: string; denomPubHash: string; - denomPub: string; + denomPub: DenominationPubKey; blindingKey: string; withdrawSig: string; coinEv: string; @@ -467,7 +469,7 @@ export interface PlanchetCreationRequest { coinIndex: number; value: AmountJson; feeWithdraw: AmountJson; - denomPub: string; + denomPub: DenominationPubKey; reservePub: string; reservePriv: string; } @@ -514,7 +516,7 @@ export interface DepositInfo { feeDeposit: AmountJson; wireInfoHash: string; denomPubHash: string; - denomSig: string; + denomSig: UnblindedSignature; } export interface ExchangesListRespose { diff --git a/packages/taler-wallet-core/package.json b/packages/taler-wallet-core/package.json index d8b344f2c..3f20811ff 100644 --- a/packages/taler-wallet-core/package.json +++ b/packages/taler-wallet-core/package.json @@ -58,7 +58,7 @@ "rollup-plugin-sourcemaps": "^0.6.3", "source-map-resolve": "^0.6.0", "typedoc": "^0.20.16", - "typescript": "^4.1.3" + "typescript": "^4.4.4" }, "dependencies": { "@gnu-taler/idb-bridge": "workspace:*", diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts index 922fbbfac..7d616ecb6 100644 --- a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts +++ b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts @@ -27,13 +27,13 @@ /** * Imports. */ -import { AmountJson } from "@gnu-taler/taler-util"; +import { AmountJson, DenominationPubKey } from "@gnu-taler/taler-util"; export interface RefreshNewDenomInfo { count: number; value: AmountJson; feeWithdraw: AmountJson; - denomPub: string; + denomPub: DenominationPubKey; } /** @@ -117,7 +117,7 @@ export interface DerivedRefreshSession { export interface DeriveTipRequest { secretSeed: string; - denomPub: string; + denomPub: DenominationPubKey; planchetIndex: number; } diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts index 169d1d9b9..389b98b22 100644 --- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts +++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts @@ -35,7 +35,9 @@ import { import { buildSigPS, CoinDepositPermission, + DenomKeyType, FreshCoin, + hashDenomPub, RecoupRequest, RefreshPlanchetInfo, TalerSignaturePurpose, @@ -152,17 +154,20 @@ export class CryptoImplementation { * reserve. */ createPlanchet(req: PlanchetCreationRequest): PlanchetCreationResult { + if (req.denomPub.cipher !== 1) { + throw Error("unsupported cipher"); + } const reservePub = decodeCrock(req.reservePub); const reservePriv = decodeCrock(req.reservePriv); - const denomPub = decodeCrock(req.denomPub); + const denomPubRsa = decodeCrock(req.denomPub.rsa_public_key); const derivedPlanchet = setupWithdrawPlanchet( decodeCrock(req.secretSeed), req.coinIndex, ); const coinPubHash = hash(derivedPlanchet.coinPub); - const ev = rsaBlind(coinPubHash, derivedPlanchet.bks, denomPub); + const ev = rsaBlind(coinPubHash, derivedPlanchet.bks, denomPubRsa); const amountWithFee = Amounts.add(req.value, req.feeWithdraw).amount; - const denomPubHash = hash(denomPub); + const denomPubHash = hashDenomPub(req.denomPub); const evHash = hash(ev); const withdrawRequest = buildSigPS( @@ -182,7 +187,10 @@ export class CryptoImplementation { coinPriv: encodeCrock(derivedPlanchet.coinPriv), coinPub: encodeCrock(derivedPlanchet.coinPub), coinValue: req.value, - denomPub: encodeCrock(denomPub), + denomPub: { + cipher: 1, + rsa_public_key: encodeCrock(denomPubRsa), + }, denomPubHash: encodeCrock(denomPubHash), reservePub: encodeCrock(reservePub), withdrawSig: encodeCrock(sig), @@ -195,8 +203,11 @@ export class CryptoImplementation { * Create a planchet used for tipping, including the private keys. */ createTipPlanchet(req: DeriveTipRequest): DerivedTipPlanchet { + if (req.denomPub.cipher !== 1) { + throw Error("unsupported cipher"); + } const fc = setupTipPlanchet(decodeCrock(req.secretSeed), req.planchetIndex); - const denomPub = decodeCrock(req.denomPub); + const denomPub = decodeCrock(req.denomPub.rsa_public_key); const coinPubHash = hash(fc.coinPub); const ev = rsaBlind(coinPubHash, fc.bks, denomPub); @@ -319,14 +330,9 @@ export class CryptoImplementation { sig: string, masterPub: string, ): boolean { - const h = kdf( - 64, - stringToBytes("exchange-wire-signature"), - stringToBytes(paytoUri + "\0"), - new Uint8Array(0), - ); + const paytoHash = hash(stringToBytes(paytoUri + "\0")); const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_DETAILS) - .put(h) + .put(paytoHash) .build(); return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub)); } @@ -385,8 +391,11 @@ export class CryptoImplementation { * and deposit permissions for each given coin. */ signDepositPermission(depositInfo: DepositInfo): CoinDepositPermission { + // FIXME: put extensions here if used + const hExt = new Uint8Array(64); const d = buildSigPS(TalerSignaturePurpose.WALLET_COIN_DEPOSIT) .put(decodeCrock(depositInfo.contractTermsHash)) + .put(hExt) .put(decodeCrock(depositInfo.wireInfoHash)) .put(decodeCrock(depositInfo.denomPubHash)) .put(timestampRoundedToBuffer(depositInfo.timestamp)) @@ -394,7 +403,6 @@ export class CryptoImplementation { .put(amountToBuffer(depositInfo.spendAmount)) .put(amountToBuffer(depositInfo.feeDeposit)) .put(decodeCrock(depositInfo.merchantPub)) - .put(decodeCrock(depositInfo.coinPub)) .build(); const coinSig = eddsaSign(d, decodeCrock(depositInfo.coinPriv)); @@ -404,7 +412,10 @@ export class CryptoImplementation { contribution: Amounts.stringify(depositInfo.spendAmount), h_denom: depositInfo.denomPubHash, exchange_url: depositInfo.exchangeBaseUrl, - ub_sig: depositInfo.denomSig, + ub_sig: { + cipher: DenomKeyType.Rsa, + rsa_signature: depositInfo.denomSig.rsa_signature, + }, }; return s; } @@ -455,8 +466,10 @@ export class CryptoImplementation { for (const denomSel of newCoinDenoms) { for (let i = 0; i < denomSel.count; i++) { - const r = decodeCrock(denomSel.denomPub); - sessionHc.update(r); + if (denomSel.denomPub.cipher !== 1) { + throw Error("unsupported cipher"); + } + sessionHc.update(hashDenomPub(denomSel.denomPub)); } } @@ -495,7 +508,10 @@ export class CryptoImplementation { blindingFactor = fresh.bks; } const pubHash = hash(coinPub); - const denomPub = decodeCrock(denomSel.denomPub); + if (denomSel.denomPub.cipher !== 1) { + throw Error("unsupported cipher"); + } + const denomPub = decodeCrock(denomSel.denomPub.rsa_public_key); const ev = rsaBlind(pubHash, blindingFactor, denomPub); const planchet: RefreshPlanchetInfo = { blindingKey: encodeCrock(blindingFactor), diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 902f749cf..483cb16c2 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -28,6 +28,7 @@ import { Auditor, CoinDepositPermission, ContractTerms, + DenominationPubKey, Duration, ExchangeSignKeyJson, InternationalizedString, @@ -36,6 +37,7 @@ import { RefreshReason, TalerErrorDetails, Timestamp, + UnblindedSignature, } from "@gnu-taler/taler-util"; import { RetryInfo } from "./util/retries.js"; import { PayCoinSelection } from "./util/coinSelection.js"; @@ -310,7 +312,7 @@ export interface DenominationRecord { /** * The denomination public key. */ - denomPub: string; + denomPub: DenominationPubKey; /** * Hash of the denomination public key. @@ -452,7 +454,7 @@ export interface ExchangeDetailsRecord { /** * content-type of the last downloaded termsOfServiceText. */ - termsOfServiceContentType: string | undefined; + termsOfServiceContentType: string | undefined; /** * ETag for last terms of service download. @@ -578,7 +580,8 @@ export interface PlanchetRecord { denomPubHash: string; - denomPub: string; + // FIXME: maybe too redundant? + denomPub: DenominationPubKey; blindingKey: string; @@ -668,7 +671,7 @@ export interface CoinRecord { /** * Key used by the exchange used to sign the coin. */ - denomPub: string; + denomPub: DenominationPubKey; /** * Hash of the public key that signs the coin. @@ -678,7 +681,7 @@ export interface CoinRecord { /** * Unblinded signature by the exchange. */ - denomSig: string; + denomSig: UnblindedSignature; /** * Amount that's left on the coin. diff --git a/packages/taler-wallet-core/src/errors.ts b/packages/taler-wallet-core/src/errors.ts index d788405ff..3109644ac 100644 --- a/packages/taler-wallet-core/src/errors.ts +++ b/packages/taler-wallet-core/src/errors.ts @@ -93,7 +93,7 @@ export async function guardOperationException( ): Promise { try { return await op(); - } catch (e) { + } catch (e: any) { if (e instanceof OperationFailedAndReportedError) { throw e; } diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index 7623ab189..e8e1de0b9 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -202,7 +202,7 @@ export interface CompletedCoin { * as the async crypto worker communication would auto-close the database transaction. */ export interface BackupCryptoPrecomputedData { - denomPubToHash: Record; + rsaDenomPubToHash: Record; coinPrivToCompletedCoin: Record; proposalNoncePrivToPub: { [priv: string]: string }; proposalIdToContractTermsHash: { [proposalId: string]: string }; @@ -330,8 +330,13 @@ export async function importBackup( } for (const backupDenomination of backupExchangeDetails.denominations) { + if (backupDenomination.denom_pub.cipher !== 1) { + throw Error("unsupported cipher"); + } const denomPubHash = - cryptoComp.denomPubToHash[backupDenomination.denom_pub]; + cryptoComp.rsaDenomPubToHash[ + backupDenomination.denom_pub.rsa_public_key + ]; checkLogicInvariant(!!denomPubHash); const existingDenom = await tx.denominations.get([ backupExchangeDetails.base_url, diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts index 3f4c02274..9027625cd 100644 --- a/packages/taler-wallet-core/src/operations/backup/index.ts +++ b/packages/taler-wallet-core/src/operations/backup/index.ts @@ -40,6 +40,7 @@ import { ConfirmPayResultType, durationFromSpec, getTimestampNow, + hashDenomPub, HttpStatusCode, j2s, Logger, @@ -57,10 +58,7 @@ import { import { gunzipSync, gzipSync } from "fflate"; import { InternalWalletState } from "../../common.js"; import { kdf } from "@gnu-taler/taler-util"; -import { - secretbox, - secretbox_open, -} from "@gnu-taler/taler-util"; +import { secretbox, secretbox_open } from "@gnu-taler/taler-util"; import { bytesToString, decodeCrock, @@ -162,13 +160,16 @@ async function computeBackupCryptoData( ): Promise { const cryptoData: BackupCryptoPrecomputedData = { coinPrivToCompletedCoin: {}, - denomPubToHash: {}, + rsaDenomPubToHash: {}, proposalIdToContractTermsHash: {}, proposalNoncePrivToPub: {}, reservePrivToPub: {}, }; for (const backupExchangeDetails of backupContent.exchange_details) { for (const backupDenom of backupExchangeDetails.denominations) { + if (backupDenom.denom_pub.cipher !== 1) { + throw Error("unsupported cipher"); + } for (const backupCoin of backupDenom.coins) { const coinPub = encodeCrock( eddsaGetPublic(decodeCrock(backupCoin.coin_priv)), @@ -176,16 +177,16 @@ async function computeBackupCryptoData( const blindedCoin = rsaBlind( hash(decodeCrock(backupCoin.coin_priv)), decodeCrock(backupCoin.blinding_key), - decodeCrock(backupDenom.denom_pub), + decodeCrock(backupDenom.denom_pub.rsa_public_key), ); cryptoData.coinPrivToCompletedCoin[backupCoin.coin_priv] = { coinEvHash: encodeCrock(hash(blindedCoin)), coinPub, }; } - cryptoData.denomPubToHash[backupDenom.denom_pub] = encodeCrock( - hash(decodeCrock(backupDenom.denom_pub)), - ); + cryptoData.rsaDenomPubToHash[ + backupDenom.denom_pub.rsa_public_key + ] = encodeCrock(hashDenomPub(backupDenom.denom_pub)); } 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 740242050..8fe3702f5 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -25,6 +25,7 @@ import { ContractTerms, CreateDepositGroupRequest, CreateDepositGroupResponse, + decodeCrock, durationFromSpec, getTimestampNow, Logger, @@ -106,7 +107,7 @@ function hashWire(paytoUri: string, salt: string): string { const r = kdf( 64, stringToBytes(paytoUri + "\0"), - stringToBytes(salt + "\0"), + decodeCrock(salt), stringToBytes("merchant-wire-signature"), ); return encodeCrock(r); @@ -213,8 +214,8 @@ async function processDepositGroupImpl( 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), - wire: depositGroup.wire, - h_wire: depositGroup.contractTermsRaw.h_wire, + 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, @@ -355,7 +356,7 @@ export async function createDepositGroup( const timestampRound = timestampTruncateToSecond(timestamp); const noncePair = await ws.cryptoApi.createEddsaKeypair(); const merchantPair = await ws.cryptoApi.createEddsaKeypair(); - const wireSalt = encodeCrock(getRandomBytes(64)); + const wireSalt = encodeCrock(getRandomBytes(16)); const wireHash = hashWire(req.depositPaytoUri, wireSalt); const contractTerms: ContractTerms = { auditors: [], diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index 629957efb..c170c5469 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -39,6 +39,7 @@ import { URL, TalerErrorDetails, Timestamp, + hashDenomPub, } from "@gnu-taler/taler-util"; import { decodeCrock, encodeCrock, hash } from "@gnu-taler/taler-util"; import { CryptoApi } from "../crypto/workers/cryptoApi.js"; @@ -78,7 +79,7 @@ function denominationRecordFromKeys( listIssueDate: Timestamp, denomIn: Denomination, ): DenominationRecord { - const denomPubHash = encodeCrock(hash(decodeCrock(denomIn.denom_pub))); + const denomPubHash = encodeCrock(hashDenomPub(denomIn.denom_pub)); const d: DenominationRecord = { denomPub: denomIn.denom_pub, denomPubHash, @@ -472,26 +473,29 @@ async function updateExchangeFromUrlImpl( let tosFound: ExchangeTosDownloadResult | undefined; //Remove this when exchange supports multiple content-type in accept header - if (acceptedFormat) for (const format of acceptedFormat) { - const resp = await downloadExchangeWithTermsOfService( - baseUrl, - ws.http, - timeout, - format - ); - if (resp.tosContentType === format) { - tosFound = resp - break + if (acceptedFormat) + for (const format of acceptedFormat) { + const resp = await downloadExchangeWithTermsOfService( + baseUrl, + ws.http, + timeout, + format, + ); + if (resp.tosContentType === format) { + tosFound = resp; + break; + } } - } // If none of the specified format was found try text/plain - const tosDownload = tosFound !== undefined ? tosFound : - await downloadExchangeWithTermsOfService( - baseUrl, - ws.http, - timeout, - "text/plain" - ); + const tosDownload = + tosFound !== undefined + ? tosFound + : await downloadExchangeWithTermsOfService( + baseUrl, + ws.http, + timeout, + "text/plain", + ); let recoupGroupId: string | undefined = undefined; diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index d727bd06f..956e4d65a 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -14,7 +14,12 @@ GNU Taler; see the file COPYING. If not, see */ -import { encodeCrock, getRandomBytes, HttpStatusCode } from "@gnu-taler/taler-util"; +import { + DenomKeyType, + encodeCrock, + getRandomBytes, + HttpStatusCode, +} from "@gnu-taler/taler-util"; import { CoinRecord, CoinSourceType, @@ -599,10 +604,17 @@ async function refreshReveal( continue; } const pc = derived.planchetsForGammas[norevealIndex][newCoinIndex]; - const denomSig = await ws.cryptoApi.rsaUnblind( - reveal.ev_sigs[newCoinIndex].ev_sig, + if (denom.denomPub.cipher !== 1) { + throw Error("cipher unsupported"); + } + const evSig = reveal.ev_sigs[newCoinIndex].ev_sig; + if (evSig.cipher !== DenomKeyType.Rsa) { + throw Error("unsupported cipher"); + } + const denomSigRsa = await ws.cryptoApi.rsaUnblind( + evSig.blinded_rsa_signature, pc.blindingKey, - denom.denomPub, + denom.denomPub.rsa_public_key, ); const coin: CoinRecord = { blindingKey: pc.blindingKey, @@ -611,7 +623,10 @@ async function refreshReveal( currentAmount: denom.value, denomPub: denom.denomPub, denomPubHash: denom.denomPubHash, - denomSig, + denomSig: { + cipher: DenomKeyType.Rsa, + rsa_signature: denomSigRsa, + }, exchangeBaseUrl: oldCoin.exchangeBaseUrl, status: CoinStatus.Fresh, coinSource: { diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts index a90e5270f..07ce00d2e 100644 --- a/packages/taler-wallet-core/src/operations/tip.ts +++ b/packages/taler-wallet-core/src/operations/tip.ts @@ -30,6 +30,7 @@ import { codecForTipResponse, Logger, URL, + DenomKeyType, } from "@gnu-taler/taler-util"; import { DerivedTipPlanchet } from "../crypto/cryptoTypes.js"; import { @@ -322,16 +323,20 @@ async function processTipImpl( const planchet = planchets[i]; checkLogicInvariant(!!planchet); - const denomSig = await ws.cryptoApi.rsaUnblind( + if (denom.denomPub.cipher !== 1) { + throw Error("unsupported cipher"); + } + + const denomSigRsa = await ws.cryptoApi.rsaUnblind( blindedSig, planchet.blindingKey, - denom.denomPub, + denom.denomPub.rsa_public_key, ); const isValid = await ws.cryptoApi.rsaVerify( planchet.coinPub, - denomSig, - denom.denomPub, + denomSigRsa, + denom.denomPub.rsa_public_key, ); if (!isValid) { @@ -364,7 +369,7 @@ async function processTipImpl( currentAmount: denom.value, denomPub: denom.denomPub, denomPubHash: denom.denomPubHash, - denomSig: denomSig, + denomSig: { cipher: DenomKeyType.Rsa, rsa_signature: denomSigRsa }, exchangeBaseUrl: tipRecord.exchangeBaseUrl, status: CoinStatus.Fresh, suspended: false, diff --git a/packages/taler-wallet-core/src/operations/withdraw.test.ts b/packages/taler-wallet-core/src/operations/withdraw.test.ts index b4f0d35e6..179852966 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.test.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.test.ts @@ -28,8 +28,11 @@ test("withdrawal selection bug repro", (t) => { const denoms: DenominationRecord[] = [ { - denomPub: - "040000XT67C8KBD6B75TTQ3SK8FWXMNQW4372T3BDDGPAMB9RFCA03638W8T3F71WFEFK9NP32VKYVNFXPYRWQ1N1HDKV5J0DFEKHBPJCYSWCBJDRNWD7G8BN8PT97FA9AMV75MYEK4X54D1HGJ207JSVJBGFCATSPNTEYNHEQF1F220W00TBZR1HNPDQFD56FG0DJQ9KGHM8EC33H6AY9YN9CNX5R3Z4TZ4Q23W47SBHB13H6W74FQJG1F50X38VRSC4SR8RWBAFB7S4K8D2H4NMRFSQT892A3T0BTBW7HM5C0H2CK6FRKG31F7W9WP1S29013K5CXYE55CT8TH6N8J9B780R42Y5S3ZB6J6E9H76XBPSGH4TGYSR2VZRB98J417KCQMZKX1BB67E7W5KVE37TC9SJ904002", + denomPub: { + cipher: 1, + rsa_public_key: + "040000XT67C8KBD6B75TTQ3SK8FWXMNQW4372T3BDDGPAMB9RFCA03638W8T3F71WFEFK9NP32VKYVNFXPYRWQ1N1HDKV5J0DFEKHBPJCYSWCBJDRNWD7G8BN8PT97FA9AMV75MYEK4X54D1HGJ207JSVJBGFCATSPNTEYNHEQF1F220W00TBZR1HNPDQFD56FG0DJQ9KGHM8EC33H6AY9YN9CNX5R3Z4TZ4Q23W47SBHB13H6W74FQJG1F50X38VRSC4SR8RWBAFB7S4K8D2H4NMRFSQT892A3T0BTBW7HM5C0H2CK6FRKG31F7W9WP1S29013K5CXYE55CT8TH6N8J9B780R42Y5S3ZB6J6E9H76XBPSGH4TGYSR2VZRB98J417KCQMZKX1BB67E7W5KVE37TC9SJ904002", + }, denomPubHash: "Q21FQSSG4FXNT96Z14CHXM8N1RZAG9GPHAV8PRWS0PZAAVWH7PBW6R97M2CH19KKP65NNSWXY7B6S53PT3CBM342E357ZXDDJ8RDVW8", exchangeBaseUrl: "https://exchange.demo.taler.net/", @@ -79,8 +82,12 @@ test("withdrawal selection bug repro", (t) => { listIssueDate: { t_ms: 0 }, }, { - denomPub: - "040000Y63CF78QFPKRY77BRK9P557Q1GQWX3NCZ3HSYSK0Z7TT0KGRA7N4SKBKEHSTVHX1Z9DNXMJR4EXSY1TXCKV0GJ3T3YYC6Z0JNMJFVYQAV4FX5J90NZH1N33MZTV8HS9SMNAA9S6K73G4P99GYBB01B0P6M1KXZ5JRDR7VWBR3MEJHHGJ6QBMCJR3NWJRE3WJW9PRY8QPQ2S7KFWTWRESH2DBXCXWBD2SRN6P9YX8GRAEMFEGXC9V5GVJTEMH6ZDGNXFPWZE3JVJ2Q4N9GDYKBCHZCJ7M7M2RJ9ZV4Y64NAN9BT6XDC68215GKKRHTW1BBF1MYY6AR3JCTT9HYAM923RMVQR3TAEB7SDX8J76XRZWYH3AGJCZAQGMN5C8SSH9AHQ9RNQJQ15CN45R37X4YNFJV904002", + denomPub: { + cipher: 1, + rsa_public_key: + "040000Y63CF78QFPKRY77BRK9P557Q1GQWX3NCZ3HSYSK0Z7TT0KGRA7N4SKBKEHSTVHX1Z9DNXMJR4EXSY1TXCKV0GJ3T3YYC6Z0JNMJFVYQAV4FX5J90NZH1N33MZTV8HS9SMNAA9S6K73G4P99GYBB01B0P6M1KXZ5JRDR7VWBR3MEJHHGJ6QBMCJR3NWJRE3WJW9PRY8QPQ2S7KFWTWRESH2DBXCXWBD2SRN6P9YX8GRAEMFEGXC9V5GVJTEMH6ZDGNXFPWZE3JVJ2Q4N9GDYKBCHZCJ7M7M2RJ9ZV4Y64NAN9BT6XDC68215GKKRHTW1BBF1MYY6AR3JCTT9HYAM923RMVQR3TAEB7SDX8J76XRZWYH3AGJCZAQGMN5C8SSH9AHQ9RNQJQ15CN45R37X4YNFJV904002", + }, + denomPubHash: "447WA23SCBATMABHA0793F92MYTBYVPYMMQHCPKMKVY5P7RZRFMQ6VRW0Y8HRA7177GTBT0TBT08R21DZD129AJ995H9G09XBFE55G8", exchangeBaseUrl: "https://exchange.demo.taler.net/", @@ -130,8 +137,11 @@ test("withdrawal selection bug repro", (t) => { listIssueDate: { t_ms: 0 }, }, { - denomPub: - "040000YDESWC2B962DA4WK356SC50MA3N9KV0ZSGY3RC48JCTY258W909C7EEMT5BTC5KZ5T4CERCZ141P9QF87EK2BD1XEEM5GB07MB3H19WE4CQGAS8X84JBWN83PQGQXVMWE5HFA992KMGHC566GT9ZS2QPHZB6X89C4A80Z663PYAAPXP728VHAKATGNNBQ01ZZ2XD1CH9Y38YZBSPJ4K7GB2J76GBCYAVD9ENHDVWXJAXYRPBX4KSS5TXRR3K5NEN9ZV3AJD2V65K7ABRZDF5D5V1FJZZMNJ5XZ4FEREEKEBV9TDFPGJTKDEHEC60K3DN24DAATRESDJ1ZYYSYSRCAT4BT2B62ARGVMJTT5N2R126DRW9TGRWCW0ZAF2N2WET1H4NJEW77X0QT46Z5R3MZ0XPHD04002", + denomPub: { + cipher: 1, + rsa_public_key: + "040000YDESWC2B962DA4WK356SC50MA3N9KV0ZSGY3RC48JCTY258W909C7EEMT5BTC5KZ5T4CERCZ141P9QF87EK2BD1XEEM5GB07MB3H19WE4CQGAS8X84JBWN83PQGQXVMWE5HFA992KMGHC566GT9ZS2QPHZB6X89C4A80Z663PYAAPXP728VHAKATGNNBQ01ZZ2XD1CH9Y38YZBSPJ4K7GB2J76GBCYAVD9ENHDVWXJAXYRPBX4KSS5TXRR3K5NEN9ZV3AJD2V65K7ABRZDF5D5V1FJZZMNJ5XZ4FEREEKEBV9TDFPGJTKDEHEC60K3DN24DAATRESDJ1ZYYSYSRCAT4BT2B62ARGVMJTT5N2R126DRW9TGRWCW0ZAF2N2WET1H4NJEW77X0QT46Z5R3MZ0XPHD04002", + }, denomPubHash: "JS61DTKAFM0BX8Q4XV3ZSKB921SM8QK745Z2AFXTKFMBHHFNBD8TQ5ETJHFNDGBGX22FFN2A2ERNYG1SGSDQWNQHQQ2B14DBVJYJG8R", exchangeBaseUrl: "https://exchange.demo.taler.net/", @@ -181,8 +191,12 @@ test("withdrawal selection bug repro", (t) => { listIssueDate: { t_ms: 0 }, }, { - denomPub: - "040000YG3T1ADB8DVA6BD3EPV6ZHSHTDW35DEN4VH1AE6CSB7P1PSDTNTJG866PHF6QB1CCWYCVRGA0FVBJ9Q0G7KV7AD9010GDYBQH0NNPHW744MTNXVXWBGGGRGQGYK4DTYN1DSWQ1FZNDSZZPB5BEKG2PDJ93NX2JTN06Y8QMS2G734Z9XHC10EENBG2KVB7EJ3CM8PV1T32RC7AY62F3496E8D8KRHJQQTT67DSGMNKK86QXVDTYW677FG27DP20E8XY3M6FQD53NDJ1WWES91401MV1A3VXVPGC76GZVDD62W3WTJ1YMKHTTA3MRXX3VEAAH3XTKDN1ER7X6CZPMYTF8VK735VP2B2TZGTF28TTW4FZS32SBS64APCDF6SZQ427N5538TJC7SRE71YSP5ET8GS904002", + denomPub: { + cipher: 1, + rsa_public_key: + "040000YG3T1ADB8DVA6BD3EPV6ZHSHTDW35DEN4VH1AE6CSB7P1PSDTNTJG866PHF6QB1CCWYCVRGA0FVBJ9Q0G7KV7AD9010GDYBQH0NNPHW744MTNXVXWBGGGRGQGYK4DTYN1DSWQ1FZNDSZZPB5BEKG2PDJ93NX2JTN06Y8QMS2G734Z9XHC10EENBG2KVB7EJ3CM8PV1T32RC7AY62F3496E8D8KRHJQQTT67DSGMNKK86QXVDTYW677FG27DP20E8XY3M6FQD53NDJ1WWES91401MV1A3VXVPGC76GZVDD62W3WTJ1YMKHTTA3MRXX3VEAAH3XTKDN1ER7X6CZPMYTF8VK735VP2B2TZGTF28TTW4FZS32SBS64APCDF6SZQ427N5538TJC7SRE71YSP5ET8GS904002", + }, + denomPubHash: "8T51NEY81VMPQ180EQ5WR0YH7GMNNT90W55Q0514KZM18AZT71FHJGJHQXGK0WTA7ACN1X2SD0S53XPBQ1A9KH960R48VCVVM6E3TH8", exchangeBaseUrl: "https://exchange.demo.taler.net/", @@ -232,8 +246,11 @@ test("withdrawal selection bug repro", (t) => { listIssueDate: { t_ms: 0 }, }, { - denomPub: - "040000ZC0G60E9QQ5PD81TSDWD9GV5Y6P8Z05NSPA696DP07NGQQVSRQXBA76Q6PRB0YFX295RG4MTQJXAZZ860ET307HSC2X37XAVGQXRVB8Q4F1V7NP5ZEVKTX75DZK1QRAVHEZGQYKSSH6DBCJNQF6V9WNQF3GEYVA4KCBHA7JF772KHXM9642C28Z0AS4XXXV2PABAN5C8CHYD5H7JDFNK3920W5Q69X0BS84XZ4RE2PW6HM1WZ6KGZ3MKWWWCPKQ1FSFABRBWKAB09PF563BEBXKY6M38QETPH5EDWGANHD0SC3QV0WXYVB7BNHNNQ0J5BNV56K563SYHM4E5ND260YRJSYA1GN5YSW2B1J5T1A1EBNYF2DN6JNJKWXWEQ42G5YS17ZSZ5EWDRA9QKV8EGTCNAD04002", + denomPub: { + cipher: 1, + rsa_public_key: + "040000ZC0G60E9QQ5PD81TSDWD9GV5Y6P8Z05NSPA696DP07NGQQVSRQXBA76Q6PRB0YFX295RG4MTQJXAZZ860ET307HSC2X37XAVGQXRVB8Q4F1V7NP5ZEVKTX75DZK1QRAVHEZGQYKSSH6DBCJNQF6V9WNQF3GEYVA4KCBHA7JF772KHXM9642C28Z0AS4XXXV2PABAN5C8CHYD5H7JDFNK3920W5Q69X0BS84XZ4RE2PW6HM1WZ6KGZ3MKWWWCPKQ1FSFABRBWKAB09PF563BEBXKY6M38QETPH5EDWGANHD0SC3QV0WXYVB7BNHNNQ0J5BNV56K563SYHM4E5ND260YRJSYA1GN5YSW2B1J5T1A1EBNYF2DN6JNJKWXWEQ42G5YS17ZSZ5EWDRA9QKV8EGTCNAD04002", + }, denomPubHash: "A41HW0Q2H9PCNMEWW0C0N45QAYVXZ8SBVRRAHE4W6X24SV1TH38ANTWDT80JXEBW9Z8PVPGT9GFV2EYZWJ5JW5W1N34NFNKHQSZ1PFR", exchangeBaseUrl: "https://exchange.demo.taler.net/", @@ -283,8 +300,11 @@ test("withdrawal selection bug repro", (t) => { listIssueDate: { t_ms: 0 }, }, { - denomPub: - "040000ZSK2PMVY6E3NBQ52KXMW029M60F4BWYTDS0FZSD0PE53CNZ9H6TM3GQK1WRTEKQ5GRWJ1J9DY6Y42SP47QVT1XD1G0W05SQ5F3F7P5KSWR0FJBJ9NZBXQEVN8Q4JRC94X3JJ3XV3KBYTZ2HTDFV28C3H2SRR0XGNZB4FY85NDZF1G4AEYJJ9QB3C0V8H70YB8RV3FKTNH7XS4K4HFNZHJ5H9VMX5SM9Z2DX37HA5WFH0E2MJBVVF2BWWA5M0HPPSB365RAE2AMD42Q65A96WD80X27SB2ZNQZ8WX0K13FWF85GZ6YNYAJGE1KGN06JDEKE9QD68Z651D7XE8V6664TVVC8M68S7WD0DSXMJQKQ0BNJXNDE29Q7MRX6DA3RW0PZ44B3TKRK0294FPVZTNSTA6XF04002", + denomPub: { + cipher: 1, + rsa_public_key: + "040000ZSK2PMVY6E3NBQ52KXMW029M60F4BWYTDS0FZSD0PE53CNZ9H6TM3GQK1WRTEKQ5GRWJ1J9DY6Y42SP47QVT1XD1G0W05SQ5F3F7P5KSWR0FJBJ9NZBXQEVN8Q4JRC94X3JJ3XV3KBYTZ2HTDFV28C3H2SRR0XGNZB4FY85NDZF1G4AEYJJ9QB3C0V8H70YB8RV3FKTNH7XS4K4HFNZHJ5H9VMX5SM9Z2DX37HA5WFH0E2MJBVVF2BWWA5M0HPPSB365RAE2AMD42Q65A96WD80X27SB2ZNQZ8WX0K13FWF85GZ6YNYAJGE1KGN06JDEKE9QD68Z651D7XE8V6664TVVC8M68S7WD0DSXMJQKQ0BNJXNDE29Q7MRX6DA3RW0PZ44B3TKRK0294FPVZTNSTA6XF04002", + }, denomPubHash: "F5NGBX33DTV4595XZZVK0S2MA1VMXFEJQERE5EBP5DS4QQ9EFRANN7YHWC1TKSHT2K6CQWDBRES8D3DWR0KZF5RET40B4AZXZ0RW1ZG", exchangeBaseUrl: "https://exchange.demo.taler.net/", diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 620ad88be..57bd49d23 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -41,6 +41,7 @@ import { URL, WithdrawUriInfoResponse, VersionMatchResult, + DenomKeyType, } from "@gnu-taler/taler-util"; import { CoinRecord, @@ -495,7 +496,7 @@ async function processPlanchetExchangeRequest( ]); if (!denom) { - console.error("db inconsistent: denom for planchet not found"); + logger.error("db inconsistent: denom for planchet not found"); return; } @@ -589,16 +590,26 @@ async function processPlanchetVerifyAndStoreCoin( const { planchet, exchangeBaseUrl } = d; - const denomSig = await ws.cryptoApi.rsaUnblind( - resp.ev_sig, + const planchetDenomPub = planchet.denomPub; + if (planchetDenomPub.cipher !== DenomKeyType.Rsa) { + throw Error("cipher not supported"); + } + + const evSig = resp.ev_sig; + if (evSig.cipher !== DenomKeyType.Rsa) { + throw Error("unsupported cipher"); + } + + const denomSigRsa = await ws.cryptoApi.rsaUnblind( + evSig.blinded_rsa_signature, planchet.blindingKey, - planchet.denomPub, + planchetDenomPub.rsa_public_key, ); const isValid = await ws.cryptoApi.rsaVerify( planchet.coinPub, - denomSig, - planchet.denomPub, + denomSigRsa, + planchetDenomPub.rsa_public_key, ); if (!isValid) { @@ -629,7 +640,10 @@ async function processPlanchetVerifyAndStoreCoin( currentAmount: planchet.coinValue, denomPub: planchet.denomPub, denomPubHash: planchet.denomPubHash, - denomSig, + denomSig: { + cipher: DenomKeyType.Rsa, + rsa_signature: denomSigRsa, + }, coinEvHash: planchet.coinEvHash, exchangeBaseUrl: exchangeBaseUrl, status: CoinStatus.Fresh, @@ -728,7 +742,9 @@ export async function updateWithdrawalDenoms( batchIdx++, current++ ) { const denom = denominations[current]; - if (denom.verificationStatus === DenominationVerificationStatus.Unverified) { + if ( + denom.verificationStatus === DenominationVerificationStatus.Unverified + ) { logger.trace( `Validating denomination (${current + 1}/${ denominations.length @@ -745,7 +761,8 @@ export async function updateWithdrawalDenoms( ); denom.verificationStatus = DenominationVerificationStatus.VerifiedBad; } else { - denom.verificationStatus = DenominationVerificationStatus.VerifiedGood; + denom.verificationStatus = + DenominationVerificationStatus.VerifiedGood; } updatedDenominations.push(denom); } diff --git a/packages/taler-wallet-core/src/util/coinSelection.test.ts b/packages/taler-wallet-core/src/util/coinSelection.test.ts index ed48b8dd1..b4dc2a18b 100644 --- a/packages/taler-wallet-core/src/util/coinSelection.test.ts +++ b/packages/taler-wallet-core/src/util/coinSelection.test.ts @@ -33,7 +33,10 @@ function fakeAci(current: string, feeDeposit: string): AvailableCoinInfo { return { availableAmount: a(current), coinPub: "foobar", - denomPub: "foobar", + denomPub: { + cipher: 1, + rsa_public_key: "foobar", + }, feeDeposit: a(feeDeposit), exchangeBaseUrl: "https://example.com/", }; diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts index 500cee5d8..ba26c98fe 100644 --- a/packages/taler-wallet-core/src/util/coinSelection.ts +++ b/packages/taler-wallet-core/src/util/coinSelection.ts @@ -23,7 +23,7 @@ /** * Imports. */ -import { AmountJson, Amounts } from "@gnu-taler/taler-util"; +import { AmountJson, Amounts, DenominationPubKey } from "@gnu-taler/taler-util"; import { strcmp, Logger } from "@gnu-taler/taler-util"; const logger = new Logger("coinSelection.ts"); @@ -72,7 +72,7 @@ export interface AvailableCoinInfo { /** * Coin's denomination public key. */ - denomPub: string; + denomPub: DenominationPubKey; /** * Amount still remaining (typically the full amount, @@ -206,6 +206,21 @@ function tallyFees( }; } +function denomPubCmp( + p1: DenominationPubKey, + p2: DenominationPubKey, +): -1 | 0 | 1 { + if (p1.cipher < p2.cipher) { + return -1; + } else if (p1.cipher > p2.cipher) { + return +1; + } + if (p1.cipher !== 1 || p2.cipher !== 1) { + throw Error("unsupported cipher"); + } + return strcmp(p1.rsa_public_key, p2.rsa_public_key); +} + /** * Given a list of candidate coins, select coins to spend under the merchant's * constraints. @@ -272,7 +287,7 @@ export function selectPayCoins( (o1, o2) => -Amounts.cmp(o1.availableAmount, o2.availableAmount) || Amounts.cmp(o1.feeDeposit, o2.feeDeposit) || - strcmp(o1.denomPub, o2.denomPub), + denomPubCmp(o1.denomPub, o2.denomPub), ); // FIXME: Here, we should select coins in a smarter way. diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 32e3945e8..cd2dd7f1e 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -387,6 +387,7 @@ async function runTaskLoop( } catch (e) { if (e instanceof OperationFailedAndReportedError) { logger.warn("operation processed resulted in reported error"); + logger.warn(`reporred error was: ${j2s(e.operationError)}`); } else { logger.error("Uncaught exception", e); ws.notify({ @@ -929,7 +930,7 @@ async function dispatchRequestInternal( } const components = pt.targetPath.split("/"); const creditorAcct = components[components.length - 1]; - logger.info(`making testbank transfer to '${creditorAcct}''`) + logger.info(`making testbank transfer to '${creditorAcct}''`); const fbReq = await ws.http.postJson( new URL(`${creditorAcct}/admin/add-incoming`, req.bank).href, { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9a425713c..7c3069267 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,12 +7,12 @@ importers: '@linaria/esbuild': ^3.0.0-beta.13 '@linaria/shaker': ^3.0.0-beta.13 esbuild: ^0.12.29 - prettier: ^2.2.1 + prettier: ^2.4.1 devDependencies: '@linaria/esbuild': 3.0.0-beta.13 '@linaria/shaker': 3.0.0-beta.13 esbuild: 0.12.29 - prettier: 2.2.1 + prettier: 2.4.1 packages/anastasis-core: specifiers: @@ -255,7 +255,7 @@ importers: source-map-support: ^0.5.19 tslib: ^2.1.0 typedoc: ^0.20.16 - typescript: ^4.1.3 + typescript: ^4.4.4 dependencies: '@gnu-taler/idb-bridge': link:../idb-bridge '@gnu-taler/taler-util': link:../taler-util @@ -269,11 +269,11 @@ importers: '@ava/typescript': 1.1.1 '@gnu-taler/pogen': link:../pogen '@microsoft/api-extractor': 7.13.0 - '@typescript-eslint/eslint-plugin': 4.14.0_980e7d90d2d08155204a38366bd3b934 - '@typescript-eslint/parser': 4.14.0_eslint@7.18.0+typescript@4.1.3 + '@typescript-eslint/eslint-plugin': 4.14.0_4f40ec8f9ae74407a8c29890901bb23f + '@typescript-eslint/parser': 4.14.0_eslint@7.18.0+typescript@4.4.4 ava: 3.15.0 eslint: 7.18.0 - eslint-config-airbnb-typescript: 12.0.0_aa91c0ea1e61103ae60b9cd49dfd9775 + eslint-config-airbnb-typescript: 12.0.0_b55a7168bd2ecdf8767ddb224d20fd7e eslint-plugin-import: 2.22.1_eslint@7.18.0 eslint-plugin-jsx-a11y: 6.4.1_eslint@7.18.0 eslint-plugin-react: 7.22.0_eslint@7.18.0 @@ -286,8 +286,8 @@ importers: rollup: 2.59.0 rollup-plugin-sourcemaps: 0.6.3_57eeb328ceff0756ae1d32f4d22d60f9 source-map-resolve: 0.6.0 - typedoc: 0.20.16_typescript@4.1.3 - typescript: 4.1.3 + typedoc: 0.20.16_typescript@4.4.4 + typescript: 4.4.4 packages/taler-wallet-embedded: specifiers: @@ -498,6 +498,11 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/compat-data/7.16.4: + resolution: {integrity: sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/core/7.12.9: resolution: {integrity: sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==} engines: {node: '>=6.9.0'} @@ -717,6 +722,18 @@ packages: semver: 6.3.0 dev: true + /@babel/helper-compilation-targets/7.16.3: + resolution: {integrity: sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.16.4 + '@babel/helper-validator-option': 7.14.5 + browserslist: 4.18.1 + semver: 6.3.0 + dev: true + /@babel/helper-create-class-features-plugin/7.15.0_@babel+core@7.13.16: resolution: {integrity: sha512-MdmDXgvTIi4heDVX/e9EFfeGpugqm9fobBVg/iioE8kueXrOHdRDe36FAY7SnE9xXLVeYCoJR/gdrBEIHRC83Q==} engines: {node: '>=6.9.0'} @@ -934,6 +951,23 @@ packages: - supports-color dev: true + /@babel/helper-define-polyfill-provider/0.3.0: + resolution: {integrity: sha512-7hfT8lUljl/tM3h+izTX/pO3W3frz2ok6Pk+gzys8iJqDfZrZy2pXjRTZAvG2YmfHun1X4q8/UZRLatMfqc5Tg==} + peerDependencies: + '@babel/core': ^7.4.0-0 + dependencies: + '@babel/helper-compilation-targets': 7.16.3 + '@babel/helper-module-imports': 7.16.0 + '@babel/helper-plugin-utils': 7.14.5 + '@babel/traverse': 7.16.3 + debug: 4.3.2 + lodash.debounce: 4.0.8 + resolve: 1.20.0 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/helper-explode-assignable-expression/7.14.5: resolution: {integrity: sha512-Htb24gnGJdIGT4vnRKMdoXiOIlqOLmdiUYpAQ0mYfgVT/GDm8GOYhgi4GL+hMKrkiPRohO4ts34ELFsGAPQLDQ==} engines: {node: '>=6.9.0'} @@ -1099,6 +1133,17 @@ packages: - supports-color dev: true + /@babel/helper-remap-async-to-generator/7.16.4: + resolution: {integrity: sha512-vGERmmhR+s7eH5Y/cp8PCVzj4XEjerq8jooMfxFdA5xVtAk9Sh4AQsrWgiErUEBjtGrBtOFKDUcWQFW4/dFwMA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-annotate-as-pure': 7.16.0 + '@babel/helper-wrap-function': 7.16.0 + '@babel/types': 7.16.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/helper-replace-supers/7.15.0: resolution: {integrity: sha512-6O+eWrhx+HEra/uJnifCwhwMd6Bp5+ZfZeJwbqUTuqkhIT6YcRhiZCOOFChRypOIe0cV46kFrRBlm+t5vHCEaA==} engines: {node: '>=6.9.0'} @@ -1206,7 +1251,7 @@ packages: dependencies: '@babel/helper-function-name': 7.16.0 '@babel/template': 7.16.0 - '@babel/traverse': 7.16.0 + '@babel/traverse': 7.16.3 '@babel/types': 7.16.0 transitivePeerDependencies: - supports-color @@ -1280,6 +1325,12 @@ packages: hasBin: true dev: true + /@babel/parser/7.16.4: + resolution: {integrity: sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==} + engines: {node: '>=6.0.0'} + hasBin: true + dev: true + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.16.2: resolution: {integrity: sha512-h37CvpLSf8gb2lIJ2CgC3t+EjFbi0t8qS7LCS1xcJIlEXE4czlofwaW7W1HA8zpgOCzI9C1nmoqNR1zWkk0pQg==} engines: {node: '>=6.9.0'} @@ -1401,6 +1452,19 @@ packages: - supports-color dev: true + /@babel/plugin-proposal-async-generator-functions/7.16.4: + resolution: {integrity: sha512-/CUekqaAaZCQHleSK/9HajvcD/zdnJiKRiuUFq8ITE+0HsPzquf53cpFiqAwl/UfmJbR6n5uGPQSPdrmKOvHHg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + '@babel/helper-remap-async-to-generator': 7.16.4 + '@babel/plugin-syntax-async-generators': 7.8.4 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-class-properties/7.14.5_@babel+core@7.13.16: resolution: {integrity: sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg==} engines: {node: '>=6.9.0'} @@ -1860,11 +1924,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.16.0 - '@babel/helper-compilation-targets': 7.16.0 + '@babel/compat-data': 7.16.4 + '@babel/helper-compilation-targets': 7.16.3 '@babel/helper-plugin-utils': 7.14.5 '@babel/plugin-syntax-object-rest-spread': 7.8.3 - '@babel/plugin-transform-parameters': 7.16.0 + '@babel/plugin-transform-parameters': 7.16.3 dev: true /@babel/plugin-proposal-object-rest-spread/7.16.0_@babel+core@7.16.0: @@ -2864,7 +2928,7 @@ packages: dependencies: '@babel/helper-module-imports': 7.16.0 '@babel/helper-plugin-utils': 7.14.5 - '@babel/helper-remap-async-to-generator': 7.16.0 + '@babel/helper-remap-async-to-generator': 7.16.4 transitivePeerDependencies: - supports-color dev: true @@ -3821,6 +3885,15 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-transform-parameters/7.16.3: + resolution: {integrity: sha512-3MaDpJrOXT1MZ/WCmkOFo7EtmVVC8H4EUZVrHvFOsmwkk4lOjQj8rzv8JKUZV4YoQKeoIgk07GO+acPU9IMu/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-transform-property-literals/7.14.5_@babel+core@7.13.16: resolution: {integrity: sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw==} engines: {node: '>=6.9.0'} @@ -4065,17 +4138,17 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true - /@babel/plugin-transform-runtime/7.16.0: - resolution: {integrity: sha512-zlPf1/XFn5+vWdve3AAhf+Sxl+MVa5VlwTwWgnLx23u4GlatSRQJ3Eoo9vllf0a9il3woQsT4SK+5Z7c06h8ag==} + /@babel/plugin-transform-runtime/7.16.4: + resolution: {integrity: sha512-pru6+yHANMTukMtEZGC4fs7XPwg35v8sj5CIEmE+gEkFljFiVJxEWxx/7ZDkTK+iZRYo1bFXBtfIN95+K3cJ5A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/helper-module-imports': 7.16.0 '@babel/helper-plugin-utils': 7.14.5 - babel-plugin-polyfill-corejs2: 0.2.3 - babel-plugin-polyfill-corejs3: 0.3.0 - babel-plugin-polyfill-regenerator: 0.2.3 + babel-plugin-polyfill-corejs2: 0.3.0 + babel-plugin-polyfill-corejs3: 0.4.0 + babel-plugin-polyfill-regenerator: 0.3.0 semver: 6.3.0 transitivePeerDependencies: - supports-color @@ -4741,6 +4814,90 @@ packages: - supports-color dev: true + /@babel/preset-env/7.16.4: + resolution: {integrity: sha512-v0QtNd81v/xKj4gNKeuAerQ/azeNn/G1B1qMLeXOcV8+4TWlD2j3NV1u8q29SDFBXx/NBq5kyEAO+0mpRgacjA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.16.4 + '@babel/helper-compilation-targets': 7.16.3 + '@babel/helper-plugin-utils': 7.14.5 + '@babel/helper-validator-option': 7.14.5 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.16.2 + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.16.0 + '@babel/plugin-proposal-async-generator-functions': 7.16.4 + '@babel/plugin-proposal-class-properties': 7.16.0 + '@babel/plugin-proposal-class-static-block': 7.16.0 + '@babel/plugin-proposal-dynamic-import': 7.16.0 + '@babel/plugin-proposal-export-namespace-from': 7.16.0 + '@babel/plugin-proposal-json-strings': 7.16.0 + '@babel/plugin-proposal-logical-assignment-operators': 7.16.0 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.16.0 + '@babel/plugin-proposal-numeric-separator': 7.16.0 + '@babel/plugin-proposal-object-rest-spread': 7.16.0 + '@babel/plugin-proposal-optional-catch-binding': 7.16.0 + '@babel/plugin-proposal-optional-chaining': 7.16.0 + '@babel/plugin-proposal-private-methods': 7.16.0 + '@babel/plugin-proposal-private-property-in-object': 7.16.0 + '@babel/plugin-proposal-unicode-property-regex': 7.16.0 + '@babel/plugin-syntax-async-generators': 7.8.4 + '@babel/plugin-syntax-class-properties': 7.12.13 + '@babel/plugin-syntax-class-static-block': 7.14.5 + '@babel/plugin-syntax-dynamic-import': 7.8.3 + '@babel/plugin-syntax-export-namespace-from': 7.8.3 + '@babel/plugin-syntax-json-strings': 7.8.3 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3 + '@babel/plugin-syntax-numeric-separator': 7.10.4 + '@babel/plugin-syntax-object-rest-spread': 7.8.3 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3 + '@babel/plugin-syntax-optional-chaining': 7.8.3 + '@babel/plugin-syntax-private-property-in-object': 7.14.5 + '@babel/plugin-syntax-top-level-await': 7.14.5 + '@babel/plugin-transform-arrow-functions': 7.16.0 + '@babel/plugin-transform-async-to-generator': 7.16.0 + '@babel/plugin-transform-block-scoped-functions': 7.16.0 + '@babel/plugin-transform-block-scoping': 7.16.0 + '@babel/plugin-transform-classes': 7.16.0 + '@babel/plugin-transform-computed-properties': 7.16.0 + '@babel/plugin-transform-destructuring': 7.16.0 + '@babel/plugin-transform-dotall-regex': 7.16.0 + '@babel/plugin-transform-duplicate-keys': 7.16.0 + '@babel/plugin-transform-exponentiation-operator': 7.16.0 + '@babel/plugin-transform-for-of': 7.16.0 + '@babel/plugin-transform-function-name': 7.16.0 + '@babel/plugin-transform-literals': 7.16.0 + '@babel/plugin-transform-member-expression-literals': 7.16.0 + '@babel/plugin-transform-modules-amd': 7.16.0 + '@babel/plugin-transform-modules-commonjs': 7.16.0 + '@babel/plugin-transform-modules-systemjs': 7.16.0 + '@babel/plugin-transform-modules-umd': 7.16.0 + '@babel/plugin-transform-named-capturing-groups-regex': 7.16.0 + '@babel/plugin-transform-new-target': 7.16.0 + '@babel/plugin-transform-object-super': 7.16.0 + '@babel/plugin-transform-parameters': 7.16.3 + '@babel/plugin-transform-property-literals': 7.16.0 + '@babel/plugin-transform-regenerator': 7.16.0 + '@babel/plugin-transform-reserved-words': 7.16.0 + '@babel/plugin-transform-shorthand-properties': 7.16.0 + '@babel/plugin-transform-spread': 7.16.0 + '@babel/plugin-transform-sticky-regex': 7.16.0 + '@babel/plugin-transform-template-literals': 7.16.0 + '@babel/plugin-transform-typeof-symbol': 7.16.0 + '@babel/plugin-transform-unicode-escapes': 7.16.0 + '@babel/plugin-transform-unicode-regex': 7.16.0 + '@babel/preset-modules': 0.1.5 + '@babel/types': 7.16.0 + babel-plugin-polyfill-corejs2: 0.3.0 + babel-plugin-polyfill-corejs3: 0.4.0 + babel-plugin-polyfill-regenerator: 0.3.0 + core-js-compat: 3.19.1 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/preset-modules/0.1.4_@babel+core@7.13.16: resolution: {integrity: sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==} peerDependencies: @@ -4919,6 +5076,13 @@ packages: regenerator-runtime: 0.13.9 dev: true + /@babel/runtime/7.16.3: + resolution: {integrity: sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.13.9 + dev: true + /@babel/template/7.14.5: resolution: {integrity: sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==} engines: {node: '>=6.9.0'} @@ -4971,6 +5135,23 @@ packages: - supports-color dev: true + /@babel/traverse/7.16.3: + resolution: {integrity: sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.16.0 + '@babel/generator': 7.16.0 + '@babel/helper-function-name': 7.16.0 + '@babel/helper-hoist-variables': 7.16.0 + '@babel/helper-split-export-declaration': 7.16.0 + '@babel/parser': 7.16.4 + '@babel/types': 7.16.0 + debug: 4.3.2 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/types/7.15.0: resolution: {integrity: sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==} engines: {node: '>=6.9.0'} @@ -5765,9 +5946,9 @@ packages: '@babel/core': '>=7' dependencies: '@babel/generator': 7.16.0 - '@babel/plugin-transform-runtime': 7.16.0 + '@babel/plugin-transform-runtime': 7.16.4 '@babel/plugin-transform-template-literals': 7.16.0 - '@babel/preset-env': 7.16.0 + '@babel/preset-env': 7.16.4 '@linaria/babel-preset': 3.0.0-beta.13 '@linaria/logger': 3.0.0-beta.3 '@linaria/preeval': 3.0.0-beta.13 @@ -5897,7 +6078,7 @@ packages: resolve: 1.17.0 semver: 7.3.4 source-map: 0.6.1 - typescript: 4.1.3 + typescript: 4.1.6 dev: true /@microsoft/tsdoc/0.12.24: @@ -10194,7 +10375,7 @@ packages: '@types/yargs-parser': 20.2.1 dev: true - /@typescript-eslint/eslint-plugin/4.14.0_980e7d90d2d08155204a38366bd3b934: + /@typescript-eslint/eslint-plugin/4.14.0_4f40ec8f9ae74407a8c29890901bb23f: resolution: {integrity: sha512-IJ5e2W7uFNfg4qh9eHkHRUCbgZ8VKtGwD07kannJvM5t/GU8P8+24NX8gi3Hf5jST5oWPY8kyV1s/WtfiZ4+Ww==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -10205,8 +10386,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/experimental-utils': 4.14.0_eslint@7.18.0+typescript@4.1.3 - '@typescript-eslint/parser': 4.14.0_eslint@7.18.0+typescript@4.1.3 + '@typescript-eslint/experimental-utils': 4.14.0_eslint@7.18.0+typescript@4.4.4 + '@typescript-eslint/parser': 4.14.0_eslint@7.18.0+typescript@4.4.4 '@typescript-eslint/scope-manager': 4.14.0 debug: 4.3.1 eslint: 7.18.0 @@ -10214,8 +10395,8 @@ packages: lodash: 4.17.20 regexpp: 3.1.0 semver: 7.3.4 - tsutils: 3.19.1_typescript@4.1.3 - typescript: 4.1.3 + tsutils: 3.19.1_typescript@4.4.4 + typescript: 4.4.4 transitivePeerDependencies: - supports-color dev: true @@ -10262,7 +10443,7 @@ packages: - typescript dev: true - /@typescript-eslint/experimental-utils/4.14.0_eslint@7.18.0+typescript@4.1.3: + /@typescript-eslint/experimental-utils/4.14.0_eslint@7.18.0+typescript@4.4.4: resolution: {integrity: sha512-6i6eAoiPlXMKRbXzvoQD5Yn9L7k9ezzGRvzC/x1V3650rUk3c3AOjQyGYyF9BDxQQDK2ElmKOZRD0CbtdkMzQQ==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -10271,7 +10452,7 @@ packages: '@types/json-schema': 7.0.7 '@typescript-eslint/scope-manager': 4.14.0 '@typescript-eslint/types': 4.14.0 - '@typescript-eslint/typescript-estree': 4.14.0_typescript@4.1.3 + '@typescript-eslint/typescript-estree': 4.14.0_typescript@4.4.4 eslint: 7.18.0 eslint-scope: 5.1.1 eslint-utils: 2.1.0 @@ -10298,7 +10479,7 @@ packages: - typescript dev: true - /@typescript-eslint/parser/4.14.0_eslint@7.18.0+typescript@4.1.3: + /@typescript-eslint/parser/4.14.0_eslint@7.18.0+typescript@4.4.4: resolution: {integrity: sha512-sUDeuCjBU+ZF3Lzw0hphTyScmDDJ5QVkyE21pRoBo8iDl7WBtVFS+WDN3blY1CH3SBt7EmYCw6wfmJjF0l/uYg==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -10310,15 +10491,15 @@ packages: dependencies: '@typescript-eslint/scope-manager': 4.14.0 '@typescript-eslint/types': 4.14.0 - '@typescript-eslint/typescript-estree': 4.14.0_typescript@4.1.3 + '@typescript-eslint/typescript-estree': 4.14.0_typescript@4.4.4 debug: 4.3.1 eslint: 7.18.0 - typescript: 4.1.3 + typescript: 4.4.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser/4.4.1_eslint@7.18.0+typescript@4.1.3: + /@typescript-eslint/parser/4.4.1_eslint@7.18.0+typescript@4.4.4: resolution: {integrity: sha512-S0fuX5lDku28Au9REYUsV+hdJpW/rNW0gWlc4SXzF/kdrRaAVX9YCxKpziH7djeWT/HFAjLZcnY7NJD8xTeUEg==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -10330,10 +10511,10 @@ packages: dependencies: '@typescript-eslint/scope-manager': 4.4.1 '@typescript-eslint/types': 4.4.1 - '@typescript-eslint/typescript-estree': 4.4.1_typescript@4.1.3 + '@typescript-eslint/typescript-estree': 4.4.1_typescript@4.4.4 debug: 4.3.1 eslint: 7.18.0 - typescript: 4.1.3 + typescript: 4.4.4 transitivePeerDependencies: - supports-color dev: true @@ -10418,7 +10599,7 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree/4.14.0_typescript@4.1.3: + /@typescript-eslint/typescript-estree/4.14.0_typescript@4.4.4: resolution: {integrity: sha512-wRjZ5qLao+bvS2F7pX4qi2oLcOONIB+ru8RGBieDptq/SudYwshveORwCVU4/yMAd4GK7Fsf8Uq1tjV838erag==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -10434,13 +10615,13 @@ packages: is-glob: 4.0.1 lodash: 4.17.20 semver: 7.3.4 - tsutils: 3.19.1_typescript@4.1.3 - typescript: 4.1.3 + tsutils: 3.19.1_typescript@4.4.4 + typescript: 4.4.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/typescript-estree/4.4.1_typescript@4.1.3: + /@typescript-eslint/typescript-estree/4.4.1_typescript@4.4.4: resolution: {integrity: sha512-wP/V7ScKzgSdtcY1a0pZYBoCxrCstLrgRQ2O9MmCUZDtmgxCO/TCqOTGRVwpP4/2hVfqMz/Vw1ZYrG8cVxvN3g==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -10456,8 +10637,8 @@ packages: is-glob: 4.0.1 lodash: 4.17.20 semver: 7.3.4 - tsutils: 3.19.1_typescript@4.1.3 - typescript: 4.1.3 + tsutils: 3.19.1_typescript@4.4.4 + typescript: 4.4.4 transitivePeerDependencies: - supports-color dev: true @@ -11644,6 +11825,18 @@ packages: - supports-color dev: true + /babel-plugin-polyfill-corejs2/0.3.0: + resolution: {integrity: sha512-wMDoBJ6uG4u4PNFh72Ty6t3EgfA91puCuAwKIazbQlci+ENb/UU9A3xG5lutjUIiXCIn1CY5L15r9LimiJyrSA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.16.4 + '@babel/helper-define-polyfill-provider': 0.3.0 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /babel-plugin-polyfill-corejs3/0.1.7_@babel+core@7.15.0: resolution: {integrity: sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw==} peerDependencies: @@ -11715,6 +11908,17 @@ packages: - supports-color dev: true + /babel-plugin-polyfill-corejs3/0.4.0: + resolution: {integrity: sha512-YxFreYwUfglYKdLUGvIF2nJEsGwj+RhWSX/ije3D2vQPOXuyMLMtg/cCGMDpOA7Nd+MwlNdnGODbd2EwUZPlsw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-define-polyfill-provider': 0.3.0 + core-js-compat: 3.19.1 + transitivePeerDependencies: + - supports-color + dev: true + /babel-plugin-polyfill-regenerator/0.2.2_@babel+core@7.13.16: resolution: {integrity: sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg==} peerDependencies: @@ -11758,6 +11962,16 @@ packages: - supports-color dev: true + /babel-plugin-polyfill-regenerator/0.3.0: + resolution: {integrity: sha512-dhAPTDLGoMW5/84wkgwiLRwMnio2i1fUe53EuvtKMv0pn2p3S8OCoV1xAzfJPl0KOX7IB89s2ib85vbYiea3jg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-define-polyfill-provider': 0.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /babel-plugin-syntax-jsx/6.18.0: resolution: {integrity: sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=} dev: true @@ -12156,6 +12370,18 @@ packages: picocolors: 1.0.0 dev: true + /browserslist/4.18.1: + resolution: {integrity: sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001280 + electron-to-chromium: 1.3.899 + escalade: 3.1.1 + node-releases: 2.0.1 + picocolors: 1.0.0 + dev: true + /bser/2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} dependencies: @@ -12394,6 +12620,10 @@ packages: resolution: {integrity: sha512-psUNoaG1ilknZPxi8HuhQWobuhLqtYSRUxplfVkEJdgZNB9TETVYGSBtv4YyfAdGvE6gn2eb0ztiXqHoWJcGnw==} dev: true + /caniuse-lite/1.0.30001280: + resolution: {integrity: sha512-kFXwYvHe5rix25uwueBxC569o53J6TpnGu0BEEn+6Lhl2vsnAumRFWEBhDft1fwyo6m1r4i+RqA4+163FpeFcA==} + dev: true + /capture-exit/2.0.0: resolution: {integrity: sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==} engines: {node: 6.* || 8.* || >= 10.*} @@ -13052,7 +13282,7 @@ packages: /core-js-compat/3.19.1: resolution: {integrity: sha512-Q/VJ7jAF/y68+aUsQJ/afPOewdsGkDtcMb40J8MbuWKlK3Y+wtHq8bTHKPj2WKWLIqmS5JhHs4CzHtz6pT2W6g==} dependencies: - browserslist: 4.17.6 + browserslist: 4.18.1 semver: 7.0.0 dev: true @@ -14141,6 +14371,10 @@ packages: resolution: {integrity: sha512-5iD1zgyPpFER4kJ716VsA4MxQ6x405dxdFNCEK2mITL075VHO5ResjY0xzQUZguCww/KlBxCA6JmBA9sDt1PRw==} dev: true + /electron-to-chromium/1.3.899: + resolution: {integrity: sha512-w16Dtd2zl7VZ4N4Db+FIa7n36sgPGCKjrKvUUmp5ialsikvcQLjcJR9RWnlYNxIyEHLdHaoIZEqKsPxU9MdyBg==} + dev: true + /element-resize-detector/1.2.3: resolution: {integrity: sha512-+dhNzUgLpq9ol5tyhoG7YLoXL3ssjfFW+0gpszXPwRU6NjGr1fVHMEAF8fVzIiRJq57Nre0RFeIjJwI8Nh2NmQ==} dependencies: @@ -14546,13 +14780,13 @@ packages: object.entries: 1.1.3 dev: true - /eslint-config-airbnb-typescript/12.0.0_aa91c0ea1e61103ae60b9cd49dfd9775: + /eslint-config-airbnb-typescript/12.0.0_b55a7168bd2ecdf8767ddb224d20fd7e: resolution: {integrity: sha512-TUCVru1Z09eKnVAX5i3XoNzjcCOU3nDQz2/jQGkg1jVYm+25fKClveziSl16celfCq+npU0MBPW/ZnXdGFZ9lw==} peerDependencies: '@typescript-eslint/eslint-plugin': ^4.4.1 dependencies: - '@typescript-eslint/eslint-plugin': 4.14.0_980e7d90d2d08155204a38366bd3b934 - '@typescript-eslint/parser': 4.4.1_eslint@7.18.0+typescript@4.1.3 + '@typescript-eslint/eslint-plugin': 4.14.0_4f40ec8f9ae74407a8c29890901bb23f + '@typescript-eslint/parser': 4.4.1_eslint@7.18.0+typescript@4.4.4 eslint-config-airbnb: 18.2.0_8b932c4aedefa0fbb298d8c6e2d8003e eslint-config-airbnb-base: 14.2.0_d4477e7d44043beb7952cd76bd313965 transitivePeerDependencies: @@ -16140,7 +16374,7 @@ packages: source-map: 0.6.1 wordwrap: 1.0.0 optionalDependencies: - uglify-js: 3.12.5 + uglify-js: 3.14.3 dev: true /har-schema/2.0.0: @@ -18774,7 +19008,7 @@ packages: /jsonfile/4.0.0: resolution: {integrity: sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=} optionalDependencies: - graceful-fs: 4.2.8 + graceful-fs: 4.2.4 dev: true /jsonfile/6.1.0: @@ -18949,7 +19183,7 @@ packages: resolution: {integrity: sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=} engines: {node: '>=4'} dependencies: - graceful-fs: 4.2.4 + graceful-fs: 4.2.8 parse-json: 2.2.0 pify: 2.3.0 strip-bom: 3.0.0 @@ -21794,6 +22028,12 @@ packages: hasBin: true dev: true + /prettier/2.4.1: + resolution: {integrity: sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + /pretty-bytes/4.0.2: resolution: {integrity: sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=} engines: {node: '>=4'} @@ -22643,7 +22883,7 @@ packages: /regenerator-transform/0.14.5: resolution: {integrity: sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==} dependencies: - '@babel/runtime': 7.16.0 + '@babel/runtime': 7.16.3 dev: true /regex-not/1.0.2: @@ -24827,14 +25067,14 @@ packages: /tslib/2.3.1: resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==} - /tsutils/3.19.1_typescript@4.1.3: + /tsutils/3.19.1_typescript@4.4.4: resolution: {integrity: sha512-GEdoBf5XI324lu7ycad7s6laADfnAqCw6wLGI+knxvw9vsIYBaJfYdmeCEG3FMMUiSm3OGgNb+m6utsWf5h9Vw==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 4.1.3 + typescript: 4.4.4 dev: true /tsutils/3.21.0_typescript@4.4.4: @@ -24959,12 +25199,39 @@ packages: typescript: 4.1.3 dev: true + /typedoc/0.20.16_typescript@4.4.4: + resolution: {integrity: sha512-xqIL8lT6ZE3QpP0GN30ckeTR05NSEkrP2pXQlNhC0OFkbvnjqJtDUcWSmCO15BuYyu4qsEbZT+tKYFEAt9Jxew==} + engines: {node: '>= 10.8.0'} + hasBin: true + peerDependencies: + typescript: 3.9.x || 4.0.x || 4.1.x + dependencies: + colors: 1.4.0 + fs-extra: 9.1.0 + handlebars: 4.7.6 + lodash: 4.17.20 + lunr: 2.3.9 + marked: 1.2.7 + minimatch: 3.0.4 + progress: 2.0.3 + shelljs: 0.8.4 + shiki: 0.2.7 + typedoc-default-themes: 0.12.4 + typescript: 4.4.4 + dev: true + /typescript/4.1.3: resolution: {integrity: sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==} engines: {node: '>=4.2.0'} hasBin: true dev: true + /typescript/4.1.6: + resolution: {integrity: sha512-pxnwLxeb/Z5SP80JDRzVjh58KsM6jZHRAOtTpS7sXLS4ogXNKC9ANxHHZqLLeVHZN35jCtI4JdmLLbLiC1kBow==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + /typescript/4.2.3: resolution: {integrity: sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==} engines: {node: '>=4.2.0'} @@ -24989,8 +25256,8 @@ packages: hasBin: true dev: true - /uglify-js/3.12.5: - resolution: {integrity: sha512-SgpgScL4T7Hj/w/GexjnBHi3Ien9WS1Rpfg5y91WXMj9SY997ZCQU76mH4TpLwwfmMvoOU8wiaRkIf6NaH3mtg==} + /uglify-js/3.14.3: + resolution: {integrity: sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==} engines: {node: '>=0.8.0'} hasBin: true dev: true