aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-02-21 12:40:51 +0100
committerFlorian Dold <florian@dold.me>2022-02-21 12:40:57 +0100
commit5c93f15157b4fc9d0fefb6bb2a9956592ebb1ec9 (patch)
treecd7e7500376f0b0ee560348e792ac4cbbb576925
parent606be7577be2bd249f19204d0c80b3b48e3065ca (diff)
downloadwallet-core-5c93f15157b4fc9d0fefb6bb2a9956592ebb1ec9.tar.xz
towards implementing breaking exchange protocol changes
-rw-r--r--packages/taler-util/src/talerCrypto.ts115
-rw-r--r--packages/taler-util/src/talerTypes.ts174
-rw-r--r--packages/taler-util/src/walletTypes.ts313
-rw-r--r--packages/taler-wallet-cli/src/harness/harness.ts2
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts4
-rw-r--r--packages/taler-wallet-core/src/crypto/cryptoTypes.ts29
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts3
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts237
-rw-r--r--packages/taler-wallet-core/src/db.ts47
-rw-r--r--packages/taler-wallet-core/src/operations/backup/import.ts3
-rw-r--r--packages/taler-wallet-core/src/operations/backup/index.ts62
-rw-r--r--packages/taler-wallet-core/src/operations/deposits.ts66
-rw-r--r--packages/taler-wallet-core/src/operations/exchanges.ts10
-rw-r--r--packages/taler-wallet-core/src/operations/pay.ts10
-rw-r--r--packages/taler-wallet-core/src/operations/refresh.ts43
-rw-r--r--packages/taler-wallet-core/src/operations/tip.ts44
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.test.ts14
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts29
-rw-r--r--packages/taler-wallet-core/src/util/coinSelection.test.ts14
19 files changed, 560 insertions, 659 deletions
diff --git a/packages/taler-util/src/talerCrypto.ts b/packages/taler-util/src/talerCrypto.ts
index c2640317a..40d2e55da 100644
--- a/packages/taler-util/src/talerCrypto.ts
+++ b/packages/taler-util/src/talerCrypto.ts
@@ -22,10 +22,15 @@
* Imports.
*/
import * as nacl from "./nacl-fast.js";
-import { kdf } from "./kdf.js";
+import { kdf, kdfKw } from "./kdf.js";
import bigint from "big-integer";
-import { DenominationPubKey, DenomKeyType } from "./talerTypes.js";
-import { AssertionError, equal } from "assert";
+import {
+ CoinEnvelope,
+ DenominationPubKey,
+ DenomKeyType,
+ HashCodeString,
+} from "./talerTypes.js";
+import { Logger } from "./logging.js";
export function getRandomBytes(n: number): Uint8Array {
return nacl.randomBytes(n);
@@ -365,7 +370,7 @@ export type CsBlindingSecrets = {
beta: [Uint8Array, Uint8Array];
};
-function typedArrayConcat(chunks: Uint8Array[]): Uint8Array {
+export function typedArrayConcat(chunks: Uint8Array[]): Uint8Array {
let payloadLen = 0;
for (const c of chunks) {
payloadLen += c.byteLength;
@@ -490,9 +495,7 @@ export function deriveBSeed(
* @param coinPriv coin private key
* @returns nonce
*/
-export function deriveWithdrawNonce(
- coinPriv: Uint8Array,
-): Uint8Array {
+export function deriveWithdrawNonce(coinPriv: Uint8Array): Uint8Array {
const outLen = 32;
const salt = stringToBytes("n");
return kdf(outLen, coinPriv, salt);
@@ -539,7 +542,7 @@ export async function csUnblind(
csSig: CsBlindSignature,
): Promise<CsSignature> {
if (b != 0 && b != 1) {
- throw new AssertionError();
+ throw new Error();
}
const secrets = deriveSecrets(bseed);
const rPubDash = (await calcRBlind(csPub, secrets, rPub))[b];
@@ -595,9 +598,38 @@ export function hash(d: Uint8Array): Uint8Array {
return nacl.hash(d);
}
+export function hashCoinEv(
+ coinEv: CoinEnvelope,
+ denomPubHash: HashCodeString,
+): Uint8Array {
+ const hashContext = createHashContext();
+ hashContext.update(decodeCrock(denomPubHash));
+ hashCoinEvInner(coinEv, hashContext);
+ return hashContext.finish();
+}
+
+const logger = new Logger("talerCrypto.ts");
+
+export function hashCoinEvInner(
+ coinEv: CoinEnvelope,
+ hashState: nacl.HashState,
+): void {
+ const hashInputBuf = new ArrayBuffer(4);
+ const uint8ArrayBuf = new Uint8Array(hashInputBuf);
+ const dv = new DataView(hashInputBuf);
+ dv.setUint32(0, DenomKeyType.toIntTag(coinEv.cipher));
+ hashState.update(uint8ArrayBuf);
+ switch (coinEv.cipher) {
+ case DenomKeyType.Rsa:
+ hashState.update(decodeCrock(coinEv.rsa_blinded_planchet));
+ return;
+ default:
+ throw new Error();
+ }
+}
+
/**
- * Hash a denomination public key according to the
- * algorithm of exchange protocol v10.
+ * Hash a denomination public key.
*/
export function hashDenomPub(pub: DenominationPubKey): Uint8Array {
if (pub.cipher === DenomKeyType.Rsa) {
@@ -606,18 +638,16 @@ export function hashDenomPub(pub: DenominationPubKey): Uint8Array {
const uint8ArrayBuf = new Uint8Array(hashInputBuf);
const dv = new DataView(hashInputBuf);
dv.setUint32(0, pub.age_mask ?? 0);
- dv.setUint32(4, pub.cipher);
+ dv.setUint32(4, DenomKeyType.toIntTag(pub.cipher));
uint8ArrayBuf.set(pubBuf, 8);
return nacl.hash(uint8ArrayBuf);
- } else if (pub.cipher === DenomKeyType.LegacyRsa) {
- return hash(decodeCrock(pub.rsa_public_key));
} else if (pub.cipher === DenomKeyType.ClauseSchnorr) {
const pubBuf = decodeCrock(pub.cs_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);
+ dv.setUint32(4, DenomKeyType.toIntTag(pub.cipher));
uint8ArrayBuf.set(pubBuf, 8);
return nacl.hash(uint8ArrayBuf);
} else {
@@ -652,18 +682,57 @@ export interface FreshCoin {
bks: Uint8Array;
}
+// export function setupRefreshPlanchet(
+// secretSeed: Uint8Array,
+// coinNumber: number,
+// ): FreshCoin {
+// const info = stringToBytes("taler-coin-derivation");
+// const saltArrBuf = new ArrayBuffer(4);
+// const salt = new Uint8Array(saltArrBuf);
+// const saltDataView = new DataView(saltArrBuf);
+// saltDataView.setUint32(0, coinNumber);
+// const out = kdf(64, secretSeed, salt, info);
+// const coinPriv = out.slice(0, 32);
+// const bks = out.slice(32, 64);
+// return {
+// bks,
+// coinPriv,
+// coinPub: eddsaGetPublic(coinPriv),
+// };
+// }
+
+function bufferForUint32(n: number): Uint8Array {
+ const arrBuf = new ArrayBuffer(4);
+ const buf = new Uint8Array(arrBuf);
+ const dv = new DataView(arrBuf);
+ dv.setUint32(0, n);
+ return buf;
+}
+
export function setupRefreshPlanchet(
- secretSeed: Uint8Array,
+ transferSecret: Uint8Array,
coinNumber: number,
): FreshCoin {
- const info = stringToBytes("taler-coin-derivation");
- const saltArrBuf = new ArrayBuffer(4);
- const salt = new Uint8Array(saltArrBuf);
- const saltDataView = new DataView(saltArrBuf);
- saltDataView.setUint32(0, coinNumber);
- const out = kdf(64, secretSeed, salt, info);
- const coinPriv = out.slice(0, 32);
- const bks = out.slice(32, 64);
+ // See TALER_transfer_secret_to_planchet_secret in C impl
+ const planchetMasterSecret = kdfKw({
+ ikm: transferSecret,
+ outputLength: 32,
+ salt: bufferForUint32(coinNumber),
+ info: stringToBytes("taler-coin-derivation"),
+ });
+
+ const coinPriv = kdfKw({
+ ikm: planchetMasterSecret,
+ outputLength: 32,
+ salt: stringToBytes("coin"),
+ });
+
+ const bks = kdfKw({
+ ikm: planchetMasterSecret,
+ outputLength: 32,
+ salt: stringToBytes("bks"),
+ });
+
return {
bks,
coinPriv,
diff --git a/packages/taler-util/src/talerTypes.ts b/packages/taler-util/src/talerTypes.ts
index 7305122bd..59d37dece 100644
--- a/packages/taler-util/src/talerTypes.ts
+++ b/packages/taler-util/src/talerTypes.ts
@@ -60,11 +60,8 @@ export class ExchangeDenomination {
/**
* Public signing key of the denomination.
- *
- * The "string" alternative is for the old exchange protocol (v9) that
- * only supports RSA keys.
*/
- denom_pub: DenominationPubKey | string;
+ denom_pub: DenominationPubKey;
/**
* Fee for withdrawing.
@@ -162,7 +159,7 @@ export interface RecoupRequest {
*
* The string variant is for the legacy exchange protocol.
*/
- denom_sig: UnblindedSignature | string;
+ denom_sig: UnblindedSignature;
/**
* Blinding key that was used during withdraw,
@@ -188,7 +185,7 @@ export interface RecoupRefreshRequest {
*
* The string variant is for the legacy exchange protocol.
*/
- denom_sig: UnblindedSignature | string;
+ denom_sig: UnblindedSignature;
/**
* Coin's blinding factor.
@@ -218,20 +215,13 @@ export interface RecoupConfirmation {
old_coin_pub?: string;
}
-export type UnblindedSignature =
- | RsaUnblindedSignature
- | LegacyRsaUnblindedSignature;
+export type UnblindedSignature = RsaUnblindedSignature;
export interface RsaUnblindedSignature {
cipher: DenomKeyType.Rsa;
rsa_signature: string;
}
-export interface LegacyRsaUnblindedSignature {
- cipher: DenomKeyType.LegacyRsa;
- rsa_signature: string;
-}
-
/**
* Deposit permission for a single coin.
*/
@@ -252,7 +242,7 @@ export interface CoinDepositPermission {
* The string variant is for legacy protocol support.
*/
- ub_sig: UnblindedSignature | string;
+ ub_sig: UnblindedSignature;
/**
* The denomination public key associated with this coin.
@@ -841,18 +831,23 @@ export class TipPickupGetResponse {
}
export enum DenomKeyType {
- Rsa = 1,
- ClauseSchnorr = 2,
- LegacyRsa = 3,
+ Rsa = "RSA",
+ ClauseSchnorr = "CS",
}
-export interface RsaBlindedDenominationSignature {
- cipher: DenomKeyType.Rsa;
- blinded_rsa_signature: string;
+export namespace DenomKeyType {
+ export function toIntTag(t: DenomKeyType): number {
+ switch (t) {
+ case DenomKeyType.Rsa:
+ return 1;
+ case DenomKeyType.ClauseSchnorr:
+ return 2;
+ }
+ }
}
-export interface LegacyRsaBlindedDenominationSignature {
- cipher: DenomKeyType.LegacyRsa;
+export interface RsaBlindedDenominationSignature {
+ cipher: DenomKeyType.Rsa;
blinded_rsa_signature: string;
}
@@ -862,33 +857,22 @@ export interface CSBlindedDenominationSignature {
export type BlindedDenominationSignature =
| RsaBlindedDenominationSignature
- | CSBlindedDenominationSignature
- | LegacyRsaBlindedDenominationSignature;
+ | CSBlindedDenominationSignature;
export const codecForBlindedDenominationSignature = () =>
buildCodecForUnion<BlindedDenominationSignature>()
.discriminateOn("cipher")
- .alternative(1, codecForRsaBlindedDenominationSignature())
- .alternative(3, codecForLegacyRsaBlindedDenominationSignature())
+ .alternative(DenomKeyType.Rsa, codecForRsaBlindedDenominationSignature())
.build("BlindedDenominationSignature");
export const codecForRsaBlindedDenominationSignature = () =>
buildCodecForObject<RsaBlindedDenominationSignature>()
- .property("cipher", codecForConstNumber(1))
+ .property("cipher", codecForConstString(DenomKeyType.Rsa))
.property("blinded_rsa_signature", codecForString())
.build("RsaBlindedDenominationSignature");
-export const codecForLegacyRsaBlindedDenominationSignature = () =>
- buildCodecForObject<LegacyRsaBlindedDenominationSignature>()
- .property("cipher", codecForConstNumber(1))
- .property("blinded_rsa_signature", codecForString())
- .build("LegacyRsaBlindedDenominationSignature");
-
export class WithdrawResponse {
- /**
- * The string variant is for legacy protocol support.
- */
- ev_sig: BlindedDenominationSignature | string;
+ ev_sig: BlindedDenominationSignature;
}
/**
@@ -983,10 +967,7 @@ export interface ExchangeMeltResponse {
}
export interface ExchangeRevealItem {
- /**
- * The string variant is for the legacy v9 protocol.
- */
- ev_sig: BlindedDenominationSignature | string;
+ ev_sig: BlindedDenominationSignature;
}
export interface ExchangeRevealResponse {
@@ -1105,26 +1086,18 @@ export interface BankWithdrawalOperationPostResponse {
transfer_done: boolean;
}
-export type DenominationPubKey =
- | RsaDenominationPubKey
- | CsDenominationPubKey
- | LegacyRsaDenominationPubKey;
-
-export interface LegacyRsaDenominationPubKey {
- cipher: DenomKeyType.LegacyRsa;
- rsa_public_key: string;
-}
+export type DenominationPubKey = RsaDenominationPubKey | CsDenominationPubKey;
export interface RsaDenominationPubKey {
- cipher: DenomKeyType.Rsa;
- rsa_public_key: string;
- age_mask?: number;
+ readonly cipher: DenomKeyType.Rsa;
+ readonly rsa_public_key: string;
+ readonly age_mask?: number;
}
export interface CsDenominationPubKey {
- cipher: DenomKeyType.ClauseSchnorr;
- age_mask: number;
- cs_public_key: string;
+ readonly cipher: DenomKeyType.ClauseSchnorr;
+ readonly age_mask: number;
+ readonly cs_public_key: string;
}
export namespace DenominationPubKey {
@@ -1136,12 +1109,6 @@ export namespace DenominationPubKey {
return -1;
} else if (p1.cipher > p2.cipher) {
return +1;
- }
- if (
- p1.cipher === DenomKeyType.LegacyRsa &&
- p2.cipher === DenomKeyType.LegacyRsa
- ) {
- return strcmp(p1.rsa_public_key, p2.rsa_public_key);
} else if (
p1.cipher === DenomKeyType.Rsa &&
p2.cipher === DenomKeyType.Rsa
@@ -1166,41 +1133,24 @@ export namespace DenominationPubKey {
throw Error("unsupported cipher");
}
}
-
- export function lift(p1: DenominationPubKey | string): DenominationPubKey {
- if (typeof p1 === "string") {
- return {
- cipher: DenomKeyType.LegacyRsa,
- rsa_public_key: p1,
- };
- }
- return p1;
- }
}
export const codecForDenominationPubKey = () =>
buildCodecForUnion<DenominationPubKey>()
.discriminateOn("cipher")
- .alternative(1, codecForRsaDenominationPubKey())
- .alternative(2, codecForCsDenominationPubKey())
- .alternative(3, codecForLegacyRsaDenominationPubKey())
+ .alternative(DenomKeyType.Rsa, codecForRsaDenominationPubKey())
+ .alternative(DenomKeyType.ClauseSchnorr, codecForCsDenominationPubKey())
.build("DenominationPubKey");
export const codecForRsaDenominationPubKey = () =>
buildCodecForObject<RsaDenominationPubKey>()
- .property("cipher", codecForConstNumber(1))
+ .property("cipher", codecForConstString(DenomKeyType.Rsa))
.property("rsa_public_key", codecForString())
.build("DenominationPubKey");
-export const codecForLegacyRsaDenominationPubKey = () =>
- buildCodecForObject<LegacyRsaDenominationPubKey>()
- .property("cipher", codecForConstNumber(3))
- .property("rsa_public_key", codecForString())
- .build("LegacyRsaDenominationPubKey");
-
export const codecForCsDenominationPubKey = () =>
buildCodecForObject<CsDenominationPubKey>()
- .property("cipher", codecForConstNumber(2))
+ .property("cipher", codecForConstString(DenomKeyType.ClauseSchnorr))
.property("cs_public_key", codecForString())
.build("CsDenominationPubKey");
@@ -1219,10 +1169,7 @@ export type CoinPublicKeyString = string;
export const codecForDenomination = (): Codec<ExchangeDenomination> =>
buildCodecForObject<ExchangeDenomination>()
.property("value", codecForString())
- .property(
- "denom_pub",
- codecForEither(codecForDenominationPubKey(), codecForString()),
- )
+ .property("denom_pub", codecForDenominationPubKey())
.property("fee_withdraw", codecForString())
.property("fee_deposit", codecForString())
.property("fee_refresh", codecForString())
@@ -1470,10 +1417,7 @@ export const codecForRecoupConfirmation = (): Codec<RecoupConfirmation> =>
export const codecForWithdrawResponse = (): Codec<WithdrawResponse> =>
buildCodecForObject<WithdrawResponse>()
- .property(
- "ev_sig",
- codecForEither(codecForBlindedDenominationSignature(), codecForString()),
- )
+ .property("ev_sig", codecForBlindedDenominationSignature())
.build("WithdrawResponse");
export const codecForMerchantPayResponse = (): Codec<MerchantPayResponse> =>
@@ -1491,10 +1435,7 @@ export const codecForExchangeMeltResponse = (): Codec<ExchangeMeltResponse> =>
export const codecForExchangeRevealItem = (): Codec<ExchangeRevealItem> =>
buildCodecForObject<ExchangeRevealItem>()
- .property(
- "ev_sig",
- codecForEither(codecForBlindedDenominationSignature(), codecForString()),
- )
+ .property("ev_sig", codecForBlindedDenominationSignature())
.build("ExchangeRevealItem");
export const codecForExchangeRevealResponse =
@@ -1711,17 +1652,48 @@ export const codecForMerchantConfigResponse =
.build("MerchantConfigResponse");
export enum ExchangeProtocolVersion {
- V9 = 9,
+ /**
+ * Current version supported by the wallet.
+ */
V12 = 12,
}
export enum MerchantProtocolVersion {
/**
- * Legacy version that is still supported.
- */
- V1 = 1,
- /**
* Current version supported by the wallet.
*/
V3 = 3,
}
+
+export type CoinEnvelope = CoinEnvelopeRsa | CoinEnvelopeCs;
+
+export interface CoinEnvelopeRsa {
+ cipher: DenomKeyType.Rsa;
+ rsa_blinded_planchet: string;
+}
+
+export interface CoinEnvelopeCs {
+ cipher: DenomKeyType.ClauseSchnorr;
+ // FIXME: add remaining fields
+}
+
+export type HashCodeString = string;
+
+export interface ExchangeWithdrawRequest {
+ denom_pub_hash: HashCodeString;
+ reserve_sig: EddsaSignatureString;
+ coin_ev: CoinEnvelope;
+}
+
+export interface ExchangeRefreshRevealRequest {
+ new_denoms_h: HashCodeString[];
+ coin_evs: CoinEnvelope[];
+ /**
+ * kappa - 1 transfer private keys (ephemeral ECDHE keys).
+ */
+ transfer_privs: string[];
+
+ transfer_pub: EddsaPublicKeyString;
+
+ link_sigs: EddsaSignatureString[];
+}
diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts
index 4a871e743..2219316b3 100644
--- a/packages/taler-util/src/walletTypes.ts
+++ b/packages/taler-util/src/walletTypes.ts
@@ -47,6 +47,7 @@ import {
import {
AmountString,
codecForContractTerms,
+ CoinEnvelope,
ContractTerms,
DenominationPubKey,
DenomKeyType,
@@ -136,11 +137,12 @@ export interface ConfirmPayResultPending {
export type ConfirmPayResult = ConfirmPayResultDone | ConfirmPayResultPending;
-export const codecForConfirmPayResultPending = (): Codec<ConfirmPayResultPending> =>
- buildCodecForObject<ConfirmPayResultPending>()
- .property("lastError", codecForAny())
- .property("type", codecForConstString(ConfirmPayResultType.Pending))
- .build("ConfirmPayResultPending");
+export const codecForConfirmPayResultPending =
+ (): Codec<ConfirmPayResultPending> =>
+ buildCodecForObject<ConfirmPayResultPending>()
+ .property("lastError", codecForAny())
+ .property("type", codecForConstString(ConfirmPayResultType.Pending))
+ .build("ConfirmPayResultPending");
export const codecForConfirmPayResultDone = (): Codec<ConfirmPayResultDone> =>
buildCodecForObject<ConfirmPayResultDone>()
@@ -322,45 +324,48 @@ export enum PreparePayResultType {
AlreadyConfirmed = "already-confirmed",
}
-export const codecForPreparePayResultPaymentPossible = (): Codec<PreparePayResultPaymentPossible> =>
- buildCodecForObject<PreparePayResultPaymentPossible>()
- .property("amountEffective", codecForAmountString())
- .property("amountRaw", codecForAmountString())
- .property("contractTerms", codecForContractTerms())
- .property("proposalId", codecForString())
- .property("contractTermsHash", codecForString())
- .property("noncePriv", codecForString())
- .property(
- "status",
- codecForConstString(PreparePayResultType.PaymentPossible),
- )
- .build("PreparePayResultPaymentPossible");
-
-export const codecForPreparePayResultInsufficientBalance = (): Codec<PreparePayResultInsufficientBalance> =>
- buildCodecForObject<PreparePayResultInsufficientBalance>()
- .property("amountRaw", codecForAmountString())
- .property("contractTerms", codecForAny())
- .property("proposalId", codecForString())
- .property("noncePriv", codecForString())
- .property(
- "status",
- codecForConstString(PreparePayResultType.InsufficientBalance),
- )
- .build("PreparePayResultInsufficientBalance");
-
-export const codecForPreparePayResultAlreadyConfirmed = (): Codec<PreparePayResultAlreadyConfirmed> =>
- buildCodecForObject<PreparePayResultAlreadyConfirmed>()
- .property(
- "status",
- codecForConstString(PreparePayResultType.AlreadyConfirmed),
- )
- .property("amountEffective", codecForAmountString())
- .property("amountRaw", codecForAmountString())
- .property("paid", codecForBoolean())
- .property("contractTerms", codecForAny())
- .property("contractTermsHash", codecForString())
- .property("proposalId", codecForString())
- .build("PreparePayResultAlreadyConfirmed");
+export const codecForPreparePayResultPaymentPossible =
+ (): Codec<PreparePayResultPaymentPossible> =>
+ buildCodecForObject<PreparePayResultPaymentPossible>()
+ .property("amountEffective", codecForAmountString())
+ .property("amountRaw", codecForAmountString())
+ .property("contractTerms", codecForContractTerms())
+ .property("proposalId", codecForString())
+ .property("contractTermsHash", codecForString())
+ .property("noncePriv", codecForString())
+ .property(
+ "status",
+ codecForConstString(PreparePayResultType.PaymentPossible),
+ )
+ .build("PreparePayResultPaymentPossible");
+
+export const codecForPreparePayResultInsufficientBalance =
+ (): Codec<PreparePayResultInsufficientBalance> =>
+ buildCodecForObject<PreparePayResultInsufficientBalance>()
+ .property("amountRaw", codecForAmountString())
+ .property("contractTerms", codecForAny())
+ .property("proposalId", codecForString())
+ .property("noncePriv", codecForString())
+ .property(
+ "status",
+ codecForConstString(PreparePayResultType.InsufficientBalance),
+ )
+ .build("PreparePayResultInsufficientBalance");
+
+export const codecForPreparePayResultAlreadyConfirmed =
+ (): Codec<PreparePayResultAlreadyConfirmed> =>
+ buildCodecForObject<PreparePayResultAlreadyConfirmed>()
+ .property(
+ "status",
+ codecForConstString(PreparePayResultType.AlreadyConfirmed),
+ )
+ .property("amountEffective", codecForAmountString())
+ .property("amountRaw", codecForAmountString())
+ .property("paid", codecForBoolean())
+ .property("contractTerms", codecForAny())
+ .property("contractTermsHash", codecForString())
+ .property("proposalId", codecForString())
+ .build("PreparePayResultAlreadyConfirmed");
export const codecForPreparePayResult = (): Codec<PreparePayResult> =>
buildCodecForUnion<PreparePayResult>()
@@ -461,7 +466,7 @@ export interface PlanchetCreationResult {
denomPub: DenominationPubKey;
blindingKey: string;
withdrawSig: string;
- coinEv: string;
+ coinEv: CoinEnvelope;
coinValue: AmountJson;
coinEvHash: string;
}
@@ -543,12 +548,13 @@ export interface ExchangeListItem {
tos: ExchangeTos;
}
-const codecForExchangeTos = (): Codec<ExchangeTos> => buildCodecForObject<ExchangeTos>()
- .property("acceptedVersion", codecOptional(codecForString()))
- .property("currentVersion", codecOptional(codecForString()))
- .property("contentType", codecOptional(codecForString()))
- .property("content", codecOptional(codecForString()))
- .build("ExchangeTos")
+const codecForExchangeTos = (): Codec<ExchangeTos> =>
+ buildCodecForObject<ExchangeTos>()
+ .property("acceptedVersion", codecOptional(codecForString()))
+ .property("currentVersion", codecOptional(codecForString()))
+ .property("contentType", codecOptional(codecForString()))
+ .property("content", codecOptional(codecForString()))
+ .build("ExchangeTos");
export const codecForExchangeListItem = (): Codec<ExchangeListItem> =>
buildCodecForObject<ExchangeListItem>()
@@ -670,10 +676,11 @@ export interface ForceExchangeUpdateRequest {
exchangeBaseUrl: string;
}
-export const codecForForceExchangeUpdateRequest = (): Codec<AddExchangeRequest> =>
- buildCodecForObject<AddExchangeRequest>()
- .property("exchangeBaseUrl", codecForString())
- .build("AddExchangeRequest");
+export const codecForForceExchangeUpdateRequest =
+ (): Codec<AddExchangeRequest> =>
+ buildCodecForObject<AddExchangeRequest>()
+ .property("exchangeBaseUrl", codecForString())
+ .build("AddExchangeRequest");
export interface GetExchangeTosRequest {
exchangeBaseUrl: string;
@@ -691,11 +698,12 @@ export interface AcceptManualWithdrawalRequest {
amount: string;
}
-export const codecForAcceptManualWithdrawalRequet = (): Codec<AcceptManualWithdrawalRequest> =>
- buildCodecForObject<AcceptManualWithdrawalRequest>()
- .property("exchangeBaseUrl", codecForString())
- .property("amount", codecForString())
- .build("AcceptManualWithdrawalRequest");
+export const codecForAcceptManualWithdrawalRequet =
+ (): Codec<AcceptManualWithdrawalRequest> =>
+ buildCodecForObject<AcceptManualWithdrawalRequest>()
+ .property("exchangeBaseUrl", codecForString())
+ .property("amount", codecForString())
+ .build("AcceptManualWithdrawalRequest");
export interface GetWithdrawalDetailsForAmountRequest {
exchangeBaseUrl: string;
@@ -707,28 +715,31 @@ export interface AcceptBankIntegratedWithdrawalRequest {
exchangeBaseUrl: string;
}
-export const codecForAcceptBankIntegratedWithdrawalRequest = (): Codec<AcceptBankIntegratedWithdrawalRequest> =>
- buildCodecForObject<AcceptBankIntegratedWithdrawalRequest>()
- .property("exchangeBaseUrl", codecForString())
- .property("talerWithdrawUri", codecForString())
- .build("AcceptBankIntegratedWithdrawalRequest");
+export const codecForAcceptBankIntegratedWithdrawalRequest =
+ (): Codec<AcceptBankIntegratedWithdrawalRequest> =>
+ buildCodecForObject<AcceptBankIntegratedWithdrawalRequest>()
+ .property("exchangeBaseUrl", codecForString())
+ .property("talerWithdrawUri", codecForString())
+ .build("AcceptBankIntegratedWithdrawalRequest");
-export const codecForGetWithdrawalDetailsForAmountRequest = (): Codec<GetWithdrawalDetailsForAmountRequest> =>
- buildCodecForObject<GetWithdrawalDetailsForAmountRequest>()
- .property("exchangeBaseUrl", codecForString())
- .property("amount", codecForString())
- .build("GetWithdrawalDetailsForAmountRequest");
+export const codecForGetWithdrawalDetailsForAmountRequest =
+ (): Codec<GetWithdrawalDetailsForAmountRequest> =>
+ buildCodecForObject<GetWithdrawalDetailsForAmountRequest>()
+ .property("exchangeBaseUrl", codecForString())
+ .property("amount", codecForString())
+ .build("GetWithdrawalDetailsForAmountRequest");
export interface AcceptExchangeTosRequest {
exchangeBaseUrl: string;
etag: string;
}
-export const codecForAcceptExchangeTosRequest = (): Codec<AcceptExchangeTosRequest> =>
- buildCodecForObject<AcceptExchangeTosRequest>()
- .property("exchangeBaseUrl", codecForString())
- .property("etag", codecForString())
- .build("AcceptExchangeTosRequest");
+export const codecForAcceptExchangeTosRequest =
+ (): Codec<AcceptExchangeTosRequest> =>
+ buildCodecForObject<AcceptExchangeTosRequest>()
+ .property("exchangeBaseUrl", codecForString())
+ .property("etag", codecForString())
+ .build("AcceptExchangeTosRequest");
export interface ApplyRefundRequest {
talerRefundUri: string;
@@ -742,18 +753,20 @@ export const codecForApplyRefundRequest = (): Codec<ApplyRefundRequest> =>
export interface GetWithdrawalDetailsForUriRequest {
talerWithdrawUri: string;
}
-export const codecForGetWithdrawalDetailsForUri = (): Codec<GetWithdrawalDetailsForUriRequest> =>
- buildCodecForObject<GetWithdrawalDetailsForUriRequest>()
- .property("talerWithdrawUri", codecForString())
- .build("GetWithdrawalDetailsForUriRequest");
+export const codecForGetWithdrawalDetailsForUri =
+ (): Codec<GetWithdrawalDetailsForUriRequest> =>
+ buildCodecForObject<GetWithdrawalDetailsForUriRequest>()
+ .property("talerWithdrawUri", codecForString())
+ .build("GetWithdrawalDetailsForUriRequest");
export interface ListKnownBankAccountsRequest {
currency?: string;
}
-export const codecForListKnownBankAccounts = (): Codec<ListKnownBankAccountsRequest> =>
- buildCodecForObject<ListKnownBankAccountsRequest>()
- .property("currency", codecOptional(codecForString()))
- .build("ListKnownBankAccountsRequest");
+export const codecForListKnownBankAccounts =
+ (): Codec<ListKnownBankAccountsRequest> =>
+ buildCodecForObject<ListKnownBankAccountsRequest>()
+ .property("currency", codecOptional(codecForString()))
+ .build("ListKnownBankAccountsRequest");
export interface GetExchangeWithdrawalInfo {
exchangeBaseUrl: string;
@@ -761,15 +774,16 @@ export interface GetExchangeWithdrawalInfo {
tosAcceptedFormat?: string[];
}
-export const codecForGetExchangeWithdrawalInfo = (): Codec<GetExchangeWithdrawalInfo> =>
- buildCodecForObject<GetExchangeWithdrawalInfo>()
- .property("exchangeBaseUrl", codecForString())
- .property("amount", codecForAmountJson())
- .property(
- "tosAcceptedFormat",
- codecOptional(codecForList(codecForString())),
- )
- .build("GetExchangeWithdrawalInfo");
+export const codecForGetExchangeWithdrawalInfo =
+ (): Codec<GetExchangeWithdrawalInfo> =>
+ buildCodecForObject<GetExchangeWithdrawalInfo>()
+ .property("exchangeBaseUrl", codecForString())
+ .property("amount", codecForAmountJson())
+ .property(
+ "tosAcceptedFormat",
+ codecOptional(codecForList(codecForString())),
+ )
+ .build("GetExchangeWithdrawalInfo");
export interface AbortProposalRequest {
proposalId: string;
@@ -853,17 +867,17 @@ export interface RefreshPlanchetInfo {
/**
* Public key for the coin.
*/
- publicKey: string;
+ coinPub: string;
/**
* Private key for the coin.
*/
- privateKey: string;
+ coinPriv: string;
/**
* Blinded public key.
*/
- coinEv: string;
+ coinEv: CoinEnvelope;
coinEvHash: string;
@@ -896,12 +910,13 @@ export interface RecoveryLoadRequest {
strategy?: RecoveryMergeStrategy;
}
-export const codecForWithdrawTestBalance = (): Codec<WithdrawTestBalanceRequest> =>
- buildCodecForObject<WithdrawTestBalanceRequest>()
- .property("amount", codecForString())
- .property("bankBaseUrl", codecForString())
- .property("exchangeBaseUrl", codecForString())
- .build("WithdrawTestBalanceRequest");
+export const codecForWithdrawTestBalance =
+ (): Codec<WithdrawTestBalanceRequest> =>
+ buildCodecForObject<WithdrawTestBalanceRequest>()
+ .property("amount", codecForString())
+ .property("bankBaseUrl", codecForString())
+ .property("exchangeBaseUrl", codecForString())
+ .build("WithdrawTestBalanceRequest");
export interface ApplyRefundResponse {
contractTermsHash: string;
@@ -935,11 +950,12 @@ export interface SetCoinSuspendedRequest {
suspended: boolean;
}
-export const codecForSetCoinSuspendedRequest = (): Codec<SetCoinSuspendedRequest> =>
- buildCodecForObject<SetCoinSuspendedRequest>()
- .property("coinPub", codecForString())
- .property("suspended", codecForBoolean())
- .build("SetCoinSuspendedRequest");
+export const codecForSetCoinSuspendedRequest =
+ (): Codec<SetCoinSuspendedRequest> =>
+ buildCodecForObject<SetCoinSuspendedRequest>()
+ .property("coinPub", codecForString())
+ .property("suspended", codecForBoolean())
+ .build("SetCoinSuspendedRequest");
export interface ForceRefreshRequest {
coinPubList: string[];
@@ -972,10 +988,11 @@ export interface AbortPayWithRefundRequest {
proposalId: string;
}
-export const codecForAbortPayWithRefundRequest = (): Codec<AbortPayWithRefundRequest> =>
- buildCodecForObject<AbortPayWithRefundRequest>()
- .property("proposalId", codecForString())
- .build("AbortPayWithRefundRequest");
+export const codecForAbortPayWithRefundRequest =
+ (): Codec<AbortPayWithRefundRequest> =>
+ buildCodecForObject<AbortPayWithRefundRequest>()
+ .property("proposalId", codecForString())
+ .build("AbortPayWithRefundRequest");
export interface GetFeeForDepositRequest {
depositPaytoUri: string;
@@ -987,18 +1004,18 @@ export interface CreateDepositGroupRequest {
amount: AmountString;
}
-
export const codecForGetFeeForDeposit = (): Codec<GetFeeForDepositRequest> =>
buildCodecForObject<GetFeeForDepositRequest>()
.property("amount", codecForAmountString())
.property("depositPaytoUri", codecForString())
.build("GetFeeForDepositRequest");
-export const codecForCreateDepositGroupRequest = (): Codec<CreateDepositGroupRequest> =>
- buildCodecForObject<CreateDepositGroupRequest>()
- .property("amount", codecForAmountString())
- .property("depositPaytoUri", codecForString())
- .build("CreateDepositGroupRequest");
+export const codecForCreateDepositGroupRequest =
+ (): Codec<CreateDepositGroupRequest> =>
+ buildCodecForObject<CreateDepositGroupRequest>()
+ .property("amount", codecForAmountString())
+ .property("depositPaytoUri", codecForString())
+ .build("CreateDepositGroupRequest");
export interface CreateDepositGroupResponse {
depositGroupId: string;
@@ -1015,10 +1032,11 @@ export interface TrackDepositGroupResponse {
}[];
}
-export const codecForTrackDepositGroupRequest = (): Codec<TrackDepositGroupRequest> =>
- buildCodecForObject<TrackDepositGroupRequest>()
- .property("depositGroupId", codecForAmountString())
- .build("TrackDepositGroupRequest");
+export const codecForTrackDepositGroupRequest =
+ (): Codec<TrackDepositGroupRequest> =>
+ buildCodecForObject<TrackDepositGroupRequest>()
+ .property("depositGroupId", codecForAmountString())
+ .build("TrackDepositGroupRequest");
export interface WithdrawUriInfoResponse {
amount: AmountString;
@@ -1026,12 +1044,13 @@ export interface WithdrawUriInfoResponse {
possibleExchanges: ExchangeListItem[];
}
-export const codecForWithdrawUriInfoResponse = (): Codec<WithdrawUriInfoResponse> =>
- buildCodecForObject<WithdrawUriInfoResponse>()
- .property("amount", codecForAmountString())
- .property("defaultExchangeBaseUrl", codecOptional(codecForString()))
- .property("possibleExchanges", codecForList(codecForExchangeListItem()))
- .build("WithdrawUriInfoResponse");
+export const codecForWithdrawUriInfoResponse =
+ (): Codec<WithdrawUriInfoResponse> =>
+ buildCodecForObject<WithdrawUriInfoResponse>()
+ .property("amount", codecForAmountString())
+ .property("defaultExchangeBaseUrl", codecOptional(codecForString()))
+ .property("possibleExchanges", codecForList(codecForExchangeListItem()))
+ .build("WithdrawUriInfoResponse");
export interface WalletCurrencyInfo {
trustedAuditors: {
@@ -1054,15 +1073,17 @@ export interface RetryTransactionRequest {
transactionId: string;
}
-export const codecForDeleteTransactionRequest = (): Codec<DeleteTransactionRequest> =>
- buildCodecForObject<DeleteTransactionRequest>()
- .property("transactionId", codecForString())
- .build("DeleteTransactionRequest");
+export const codecForDeleteTransactionRequest =
+ (): Codec<DeleteTransactionRequest> =>
+ buildCodecForObject<DeleteTransactionRequest>()
+ .property("transactionId", codecForString())
+ .build("DeleteTransactionRequest");
-export const codecForRetryTransactionRequest = (): Codec<RetryTransactionRequest> =>
- buildCodecForObject<RetryTransactionRequest>()
- .property("transactionId", codecForString())
- .build("RetryTransactionRequest");
+export const codecForRetryTransactionRequest =
+ (): Codec<RetryTransactionRequest> =>
+ buildCodecForObject<RetryTransactionRequest>()
+ .property("transactionId", codecForString())
+ .build("RetryTransactionRequest");
export interface SetWalletDeviceIdRequest {
/**
@@ -1071,10 +1092,11 @@ export interface SetWalletDeviceIdRequest {
walletDeviceId: string;
}
-export const codecForSetWalletDeviceIdRequest = (): Codec<SetWalletDeviceIdRequest> =>
- buildCodecForObject<SetWalletDeviceIdRequest>()
- .property("walletDeviceId", codecForString())
- .build("SetWalletDeviceIdRequest");
+export const codecForSetWalletDeviceIdRequest =
+ (): Codec<SetWalletDeviceIdRequest> =>
+ buildCodecForObject<SetWalletDeviceIdRequest>()
+ .property("walletDeviceId", codecForString())
+ .build("SetWalletDeviceIdRequest");
export interface WithdrawFakebankRequest {
amount: AmountString;
@@ -1082,12 +1104,13 @@ export interface WithdrawFakebankRequest {
bank: string;
}
-export const codecForWithdrawFakebankRequest = (): Codec<WithdrawFakebankRequest> =>
- buildCodecForObject<WithdrawFakebankRequest>()
- .property("amount", codecForAmountString())
- .property("bank", codecForString())
- .property("exchange", codecForString())
- .build("WithdrawFakebankRequest");
+export const codecForWithdrawFakebankRequest =
+ (): Codec<WithdrawFakebankRequest> =>
+ buildCodecForObject<WithdrawFakebankRequest>()
+ .property("amount", codecForAmountString())
+ .property("bank", codecForString())
+ .property("exchange", codecForString())
+ .build("WithdrawFakebankRequest");
export interface ImportDb {
dump: any;
@@ -1095,4 +1118,4 @@ export interface ImportDb {
export const codecForImportDbRequest = (): Codec<ImportDb> =>
buildCodecForObject<ImportDb>()
.property("dump", codecForAny())
- .build("ImportDbRequest")
+ .build("ImportDbRequest");
diff --git a/packages/taler-wallet-cli/src/harness/harness.ts b/packages/taler-wallet-cli/src/harness/harness.ts
index fc489327f..b4d2884d1 100644
--- a/packages/taler-wallet-cli/src/harness/harness.ts
+++ b/packages/taler-wallet-cli/src/harness/harness.ts
@@ -1525,7 +1525,7 @@ export class ExchangeService implements ExchangeServiceInterface {
this.exchangeHttpProc = this.globalState.spawnService(
"taler-exchange-httpd",
- ["-c", this.configFilename, ...this.timetravelArgArr],
+ ["-LINFO", "-c", this.configFilename, ...this.timetravelArgArr],
`exchange-httpd-${this.name}`,
);
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts b/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts
index eceb26d79..ed07114eb 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts
@@ -201,8 +201,8 @@ export async function runExchangeTimetravelTest(t: GlobalTestState) {
for (const da of denomPubs1) {
let found = false;
for (const db of denomPubs2) {
- const d1 = DenominationPubKey.lift(da.denomPub);
- const d2 = DenominationPubKey.lift(db.denomPub);
+ const d1 = da.denomPub;
+ const d2 = db.denomPub;
if (DenominationPubKey.cmp(d1, d2) === 0) {
found = true;
break;
diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
index 93a7cd1c4..94abb8f7c 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
@@ -30,8 +30,10 @@
import {
AmountJson,
AmountString,
+ CoinEnvelope,
DenominationPubKey,
ExchangeProtocolVersion,
+ RefreshPlanchetInfo,
UnblindedSignature,
} from "@gnu-taler/taler-util";
@@ -74,32 +76,7 @@ export interface DerivedRefreshSession {
/**
* Planchets for each cut-and-choose instance.
*/
- planchetsForGammas: {
- /**
- * Public key for the coin.
- */
- publicKey: string;
-
- /**
- * Private key for the coin.
- */
- privateKey: string;
-
- /**
- * Blinded public key.
- */
- coinEv: string;
-
- /**
- * Hash of the blinded public key.
- */
- coinEvHash: string;
-
- /**
- * Blinding key used.
- */
- blindingKey: string;
- }[][];
+ planchetsForGammas: RefreshPlanchetInfo[][];
/**
* The transfer keys, kappa of them.
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
index 29c2553a5..16446bb9e 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
@@ -28,6 +28,7 @@ import { CryptoWorker } from "./cryptoWorkerInterface.js";
import {
CoinDepositPermission,
+ CoinEnvelope,
RecoupRefreshRequest,
RecoupRequest,
} from "@gnu-taler/taler-util";
@@ -452,7 +453,7 @@ export class CryptoApi {
newDenomHash: string,
oldCoinPub: string,
transferPub: string,
- coinEv: string,
+ coinEv: CoinEnvelope,
): Promise<string> {
return this.doRpc<string>(
"signCoinLink",
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
index bff2e0eb5..9f6d82348 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
@@ -26,19 +26,49 @@
// FIXME: Crypto should not use DB Types!
import {
- AmountJson, Amounts, BenchmarkResult, buildSigPS,
- CoinDepositPermission, createEddsaKeyPair, createHashContext, decodeCrock,
- DenomKeyType, DepositInfo, eddsaGetPublic, eddsaSign, eddsaVerify,
- encodeCrock, ExchangeProtocolVersion,
- FreshCoin, hash, hashDenomPub, kdf, keyExchangeEcdheEddsa,
- // Logger,
- MakeSyncSignatureRequest, PlanchetCreationRequest, PlanchetCreationResult,
- randomBytes, RecoupRefreshRequest,
+ AmountJson,
+ Amounts,
+ BenchmarkResult,
+ buildSigPS,
+ CoinDepositPermission,
+ CoinEnvelope,
+ createEddsaKeyPair,
+ createHashContext,
+ decodeCrock,
+ DenomKeyType,
+ DepositInfo,
+ eddsaGetPublic,
+ eddsaSign,
+ eddsaVerify,
+ encodeCrock,
+ ExchangeProtocolVersion,
+ FreshCoin,
+ hash,
+ HashCodeString,
+ hashCoinEv,
+ hashCoinEvInner,
+ hashDenomPub,
+ keyExchangeEcdheEddsa,
+ Logger,
+ MakeSyncSignatureRequest,
+ PlanchetCreationRequest,
+ PlanchetCreationResult,
+ randomBytes,
+ RecoupRefreshRequest,
RecoupRequest,
- RefreshPlanchetInfo, rsaBlind, rsaUnblind, rsaVerify, setupRefreshPlanchet,
+ RefreshPlanchetInfo,
+ rsaBlind,
+ rsaUnblind,
+ rsaVerify,
+ setupRefreshPlanchet,
setupRefreshTransferPub,
setupTipPlanchet,
- setupWithdrawPlanchet, stringToBytes, TalerSignaturePurpose, Timestamp, timestampTruncateToSecond
+ setupWithdrawPlanchet,
+ stringToBytes,
+ TalerSignaturePurpose,
+ Timestamp,
+ timestampTruncateToSecond,
+ typedArrayConcat,
} from "@gnu-taler/taler-util";
import bigint from "big-integer";
import { DenominationRecord, WireFee } from "../../db.js";
@@ -50,10 +80,10 @@ import {
DerivedTipPlanchet,
DeriveRefreshSessionRequest,
DeriveTipRequest,
- SignTrackTransactionRequest
+ SignTrackTransactionRequest,
} from "../cryptoTypes.js";
-// const logger = new Logger("cryptoImplementation.ts");
+const logger = new Logger("cryptoImplementation.ts");
function amountToBuffer(amount: AmountJson): Uint8Array {
const buffer = new ArrayBuffer(8 + 4 + 12);
@@ -130,7 +160,7 @@ async function myEddsaSign(
export class CryptoImplementation {
static enableTracing = false;
- constructor(private primitiveWorker?: PrimitiveWorker) { }
+ constructor(private primitiveWorker?: PrimitiveWorker) {}
/**
* Create a pre-coin of the given denomination to be withdrawn from then given
@@ -139,26 +169,26 @@ export class CryptoImplementation {
async createPlanchet(
req: PlanchetCreationRequest,
): Promise<PlanchetCreationResult> {
- if (
- req.denomPub.cipher === DenomKeyType.Rsa ||
- req.denomPub.cipher === DenomKeyType.LegacyRsa
- ) {
+ const denomPub = req.denomPub;
+ if (denomPub.cipher === DenomKeyType.Rsa) {
const reservePub = decodeCrock(req.reservePub);
- const denomPubRsa = decodeCrock(req.denomPub.rsa_public_key);
+ const denomPubRsa = decodeCrock(denomPub.rsa_public_key);
const derivedPlanchet = setupWithdrawPlanchet(
decodeCrock(req.secretSeed),
req.coinIndex,
);
const coinPubHash = hash(derivedPlanchet.coinPub);
const ev = rsaBlind(coinPubHash, derivedPlanchet.bks, denomPubRsa);
+ const coinEv: CoinEnvelope = {
+ cipher: DenomKeyType.Rsa,
+ rsa_blinded_planchet: encodeCrock(ev),
+ };
const amountWithFee = Amounts.add(req.value, req.feeWithdraw).amount;
const denomPubHash = hashDenomPub(req.denomPub);
- const evHash = hash(ev);
-
+ const evHash = hashCoinEv(coinEv, encodeCrock(denomPubHash));
const withdrawRequest = buildSigPS(
TalerSignaturePurpose.WALLET_RESERVE_WITHDRAW,
)
- .put(reservePub)
.put(amountToBuffer(amountWithFee))
.put(denomPubHash)
.put(evHash)
@@ -171,14 +201,11 @@ export class CryptoImplementation {
const planchet: PlanchetCreationResult = {
blindingKey: encodeCrock(derivedPlanchet.bks),
- coinEv: encodeCrock(ev),
+ coinEv,
coinPriv: encodeCrock(derivedPlanchet.coinPriv),
coinPub: encodeCrock(derivedPlanchet.coinPub),
coinValue: req.value,
- denomPub: {
- cipher: req.denomPub.cipher,
- rsa_public_key: encodeCrock(denomPubRsa),
- },
+ denomPub,
denomPubHash: encodeCrock(denomPubHash),
reservePub: encodeCrock(reservePub),
withdrawSig: sigResult.sig,
@@ -194,11 +221,8 @@ export class CryptoImplementation {
* Create a planchet used for tipping, including the private keys.
*/
createTipPlanchet(req: DeriveTipRequest): DerivedTipPlanchet {
- if (
- req.denomPub.cipher !== DenomKeyType.Rsa &&
- req.denomPub.cipher !== DenomKeyType.LegacyRsa
- ) {
- throw Error("unsupported cipher");
+ if (req.denomPub.cipher !== DenomKeyType.Rsa) {
+ throw Error(`unsupported cipher (${req.denomPub.cipher})`);
}
const fc = setupTipPlanchet(decodeCrock(req.secretSeed), req.planchetIndex);
const denomPub = decodeCrock(req.denomPub.rsa_public_key);
@@ -236,15 +260,7 @@ export class CryptoImplementation {
const coinPriv = decodeCrock(req.coinPriv);
const coinSig = eddsaSign(p, coinPriv);
- if (req.denomPub.cipher === DenomKeyType.LegacyRsa) {
- const paybackRequest: RecoupRequest = {
- coin_blind_key_secret: req.blindingKey,
- coin_sig: encodeCrock(coinSig),
- denom_pub_hash: req.denomPubHash,
- denom_sig: req.denomSig.rsa_signature,
- };
- return paybackRequest;
- } else {
+ if (req.denomPub.cipher === DenomKeyType.Rsa) {
const paybackRequest: RecoupRequest = {
coin_blind_key_secret: req.blindingKey,
coin_sig: encodeCrock(coinSig),
@@ -252,6 +268,8 @@ export class CryptoImplementation {
denom_sig: req.denomSig,
};
return paybackRequest;
+ } else {
+ throw new Error();
}
}
@@ -268,15 +286,7 @@ export class CryptoImplementation {
const coinPriv = decodeCrock(req.coinPriv);
const coinSig = eddsaSign(p, coinPriv);
- if (req.denomPub.cipher === DenomKeyType.LegacyRsa) {
- const recoupRequest: RecoupRefreshRequest = {
- coin_blind_key_secret: req.blindingKey,
- coin_sig: encodeCrock(coinSig),
- denom_pub_hash: req.denomPubHash,
- denom_sig: req.denomSig.rsa_signature,
- };
- return recoupRequest;
- } else {
+ if (req.denomPub.cipher === DenomKeyType.Rsa) {
const recoupRequest: RecoupRefreshRequest = {
coin_blind_key_secret: req.blindingKey,
coin_sig: encodeCrock(coinSig),
@@ -284,6 +294,8 @@ export class CryptoImplementation {
denom_sig: req.denomSig,
};
return recoupRequest;
+ } else {
+ throw new Error();
}
}
@@ -364,26 +376,11 @@ export class CryptoImplementation {
sig: string,
masterPub: string,
): boolean {
- if (versionCurrent === ExchangeProtocolVersion.V12) {
- const paytoHash = hash(stringToBytes(paytoUri + "\0"));
- const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_DETAILS)
- .put(paytoHash)
- .build();
- return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub));
- } else if (versionCurrent === ExchangeProtocolVersion.V9) {
- const h = kdf(
- 64,
- stringToBytes("exchange-wire-signature"),
- stringToBytes(paytoUri + "\0"),
- new Uint8Array(0),
- );
- const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_DETAILS)
- .put(h)
- .build();
- return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub));
- } else {
- throw Error(`unsupported version (${versionCurrent})`);
- }
+ const paytoHash = hash(stringToBytes(paytoUri + "\0"));
+ const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_DETAILS)
+ .put(paytoHash)
+ .build();
+ return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub));
}
isValidContractTermsSignature(
@@ -444,10 +441,12 @@ export class CryptoImplementation {
): Promise<CoinDepositPermission> {
// FIXME: put extensions here if used
const hExt = new Uint8Array(64);
+ const hAgeCommitment = new Uint8Array(32);
let d: Uint8Array;
if (depositInfo.denomKeyType === DenomKeyType.Rsa) {
d = buildSigPS(TalerSignaturePurpose.WALLET_COIN_DEPOSIT)
.put(decodeCrock(depositInfo.contractTermsHash))
+ .put(hAgeCommitment)
.put(hExt)
.put(decodeCrock(depositInfo.wireInfoHash))
.put(decodeCrock(depositInfo.denomPubHash))
@@ -457,18 +456,6 @@ export class CryptoImplementation {
.put(amountToBuffer(depositInfo.feeDeposit))
.put(decodeCrock(depositInfo.merchantPub))
.build();
- } else if (depositInfo.denomKeyType === DenomKeyType.LegacyRsa) {
- d = buildSigPS(TalerSignaturePurpose.WALLET_COIN_DEPOSIT)
- .put(decodeCrock(depositInfo.contractTermsHash))
- .put(decodeCrock(depositInfo.wireInfoHash))
- .put(decodeCrock(depositInfo.denomPubHash))
- .put(timestampRoundedToBuffer(depositInfo.timestamp))
- .put(timestampRoundedToBuffer(depositInfo.refundDeadline))
- .put(amountToBuffer(depositInfo.spendAmount))
- .put(amountToBuffer(depositInfo.feeDeposit))
- .put(decodeCrock(depositInfo.merchantPub))
- .put(decodeCrock(depositInfo.coinPub))
- .build();
} else {
throw Error("unsupported exchange protocol version");
}
@@ -490,18 +477,10 @@ export class CryptoImplementation {
},
};
return s;
- } else if (depositInfo.denomKeyType === DenomKeyType.LegacyRsa) {
- const s: CoinDepositPermission = {
- coin_pub: depositInfo.coinPub,
- coin_sig: coinSigRes.sig,
- contribution: Amounts.stringify(depositInfo.spendAmount),
- h_denom: depositInfo.denomPubHash,
- exchange_url: depositInfo.exchangeBaseUrl,
- ub_sig: depositInfo.denomSig.rsa_signature,
- };
- return s;
} else {
- throw Error("unsupported merchant protocol version");
+ throw Error(
+ `unsupported denomination cipher (${depositInfo.denomKeyType})`,
+ );
}
}
@@ -551,17 +530,18 @@ export class CryptoImplementation {
for (const denomSel of newCoinDenoms) {
for (let i = 0; i < denomSel.count; i++) {
- if (denomSel.denomPub.cipher === DenomKeyType.LegacyRsa) {
- const r = decodeCrock(denomSel.denomPub.rsa_public_key);
- sessionHc.update(r);
+ if (denomSel.denomPub.cipher === DenomKeyType.Rsa) {
+ const denomPubHash = hashDenomPub(denomSel.denomPub);
+ sessionHc.update(denomPubHash);
} else {
- sessionHc.update(hashDenomPub(denomSel.denomPub));
+ throw new Error();
}
}
}
sessionHc.update(decodeCrock(meltCoinPub));
sessionHc.update(amountToBuffer(valueWithFee));
+
for (let i = 0; i < kappa; i++) {
const planchets: RefreshPlanchetInfo[] = [];
for (let j = 0; j < newCoinDenoms.length; j++) {
@@ -594,24 +574,29 @@ export class CryptoImplementation {
coinPub = fresh.coinPub;
blindingFactor = fresh.bks;
}
- const pubHash = hash(coinPub);
- if (
- denomSel.denomPub.cipher !== DenomKeyType.Rsa &&
- denomSel.denomPub.cipher !== DenomKeyType.LegacyRsa
- ) {
+ const coinPubHash = hash(coinPub);
+ if (denomSel.denomPub.cipher !== DenomKeyType.Rsa) {
throw Error("unsupported cipher, can't create refresh session");
}
- const denomPub = decodeCrock(denomSel.denomPub.rsa_public_key);
- const ev = rsaBlind(pubHash, blindingFactor, denomPub);
+ const rsaDenomPub = decodeCrock(denomSel.denomPub.rsa_public_key);
+ const ev = rsaBlind(coinPubHash, blindingFactor, rsaDenomPub);
+ const coinEv: CoinEnvelope = {
+ cipher: DenomKeyType.Rsa,
+ rsa_blinded_planchet: encodeCrock(ev),
+ };
+ const coinEvHash = hashCoinEv(
+ coinEv,
+ encodeCrock(hashDenomPub(denomSel.denomPub)),
+ );
const planchet: RefreshPlanchetInfo = {
blindingKey: encodeCrock(blindingFactor),
- coinEv: encodeCrock(ev),
- privateKey: encodeCrock(coinPriv),
- publicKey: encodeCrock(coinPub),
- coinEvHash: encodeCrock(hash(ev)),
+ coinEv,
+ coinPriv: encodeCrock(coinPriv),
+ coinPub: encodeCrock(coinPub),
+ coinEvHash: encodeCrock(coinEvHash),
};
planchets.push(planchet);
- sessionHc.update(ev);
+ hashCoinEvInner(coinEv, sessionHc);
}
}
planchetsForGammas.push(planchets);
@@ -619,26 +604,15 @@ export class CryptoImplementation {
const sessionHash = sessionHc.finish();
let confirmData: Uint8Array;
- if (req.exchangeProtocolVersion === ExchangeProtocolVersion.V9) {
- confirmData = buildSigPS(TalerSignaturePurpose.WALLET_COIN_MELT)
- .put(sessionHash)
- .put(decodeCrock(meltCoinDenomPubHash))
- .put(amountToBuffer(valueWithFee))
- .put(amountToBuffer(meltFee))
- .put(decodeCrock(meltCoinPub))
- .build();
- } else if (req.exchangeProtocolVersion === ExchangeProtocolVersion.V12) {
- confirmData = buildSigPS(TalerSignaturePurpose.WALLET_COIN_MELT)
- .put(sessionHash)
- .put(decodeCrock(meltCoinDenomPubHash))
- .put(amountToBuffer(valueWithFee))
- .put(amountToBuffer(meltFee))
- .build();
- } else {
- throw Error(
- `Exchange protocol version (${req.exchangeProtocolVersion}) not supported`,
- );
- }
+ // FIXME: fill in age commitment
+ const hAgeCommitment = new Uint8Array(32);
+ confirmData = buildSigPS(TalerSignaturePurpose.WALLET_COIN_MELT)
+ .put(sessionHash)
+ .put(decodeCrock(meltCoinDenomPubHash))
+ .put(hAgeCommitment)
+ .put(amountToBuffer(valueWithFee))
+ .put(amountToBuffer(meltFee))
+ .build();
const confirmSigResp = await myEddsaSign(this.primitiveWorker, {
msg: encodeCrock(confirmData),
@@ -678,12 +652,15 @@ export class CryptoImplementation {
newDenomHash: string,
oldCoinPub: string,
transferPub: string,
- coinEv: string,
+ coinEv: CoinEnvelope,
): Promise<string> {
- const coinEvHash = hash(decodeCrock(coinEv));
+ const coinEvHash = hashCoinEv(coinEv, newDenomHash);
+ // FIXME: fill in
+ const hAgeCommitment = new Uint8Array(32);
const coinLink = buildSigPS(TalerSignaturePurpose.WALLET_COIN_LINK)
.put(decodeCrock(newDenomHash))
.put(decodeCrock(transferPub))
+ .put(hAgeCommitment)
.put(coinEvHash)
.build();
const sig = await myEddsaSign(this.primitiveWorker, {
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index 7f7dd10ff..410311530 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -38,6 +38,7 @@ import {
TalerErrorDetails,
Timestamp,
UnblindedSignature,
+ CoinEnvelope,
} from "@gnu-taler/taler-util";
import { RetryInfo } from "./util/retries.js";
import { PayCoinSelection } from "./util/coinSelection.js";
@@ -602,7 +603,7 @@ export interface PlanchetRecord {
withdrawSig: string;
- coinEv: string;
+ coinEv: CoinEnvelope;
coinEvHash: string;
@@ -1154,7 +1155,6 @@ export interface WalletContractData {
timestamp: Timestamp;
wireMethod: string;
wireInfoHash: string;
- wireInfoLegacyHash?: string;
maxDepositFee: AmountJson;
}
@@ -1294,9 +1294,9 @@ export const WALLET_BACKUP_STATE_KEY = "walletBackupState";
*/
export type ConfigRecord =
| {
- key: typeof WALLET_BACKUP_STATE_KEY;
- value: WalletBackupConfState;
- }
+ key: typeof WALLET_BACKUP_STATE_KEY;
+ value: WalletBackupConfState;
+ }
| { key: "currencyDefaultsApplied"; value: boolean };
export interface WalletBackupConfState {
@@ -1392,9 +1392,9 @@ export interface WithdrawalGroupRecord {
/**
* UID of the denomination selection.
- *
+ *
* Used for merging backups.
- *
+ *
* FIXME: Should this not also include a timestamp for more logical merging?
*/
denomSelUid: string;
@@ -1480,17 +1480,17 @@ export enum BackupProviderStateTag {
export type BackupProviderState =
| {
- tag: BackupProviderStateTag.Provisional;
- }
+ tag: BackupProviderStateTag.Provisional;
+ }
| {
- tag: BackupProviderStateTag.Ready;
- nextBackupTimestamp: Timestamp;
- }
+ tag: BackupProviderStateTag.Ready;
+ nextBackupTimestamp: Timestamp;
+ }
| {
- tag: BackupProviderStateTag.Retrying;
- retryInfo: RetryInfo;
- lastError?: TalerErrorDetails;
- };
+ tag: BackupProviderStateTag.Retrying;
+ retryInfo: RetryInfo;
+ lastError?: TalerErrorDetails;
+ };
export interface BackupProviderTerms {
supportedProtocolVersion: string;
@@ -1875,9 +1875,9 @@ export function exportDb(db: IDBDatabase): Promise<any> {
}
export interface DatabaseDump {
- name: string,
- stores: { [s: string]: any },
- version: string,
+ name: string;
+ stores: { [s: string]: any };
+ version: string;
}
export function importDb(db: IDBDatabase, dump: DatabaseDump): Promise<any> {
@@ -1891,12 +1891,11 @@ export function importDb(db: IDBDatabase, dump: DatabaseDump): Promise<any> {
const name = db.objectStoreNames[i];
const storeDump = dump.stores[name];
if (!storeDump) continue;
- Object.keys(storeDump).forEach(async key => {
- const value = storeDump[key]
+ Object.keys(storeDump).forEach(async (key) => {
+ const value = storeDump[key];
if (!value) return;
- tx.objectStore(name).put(value)
- })
-
+ tx.objectStore(name).put(value);
+ });
}
});
}
diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts
index 9f63441dd..21b10a945 100644
--- a/packages/taler-wallet-core/src/operations/backup/import.ts
+++ b/packages/taler-wallet-core/src/operations/backup/import.ts
@@ -337,8 +337,7 @@ export async function importBackup(
for (const backupDenomination of backupExchangeDetails.denominations) {
if (
- backupDenomination.denom_pub.cipher !== DenomKeyType.Rsa &&
- backupDenomination.denom_pub.cipher !== DenomKeyType.LegacyRsa
+ backupDenomination.denom_pub.cipher !== DenomKeyType.Rsa
) {
throw Error("unsupported cipher");
}
diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts
index e3950ef90..5eb248611 100644
--- a/packages/taler-wallet-core/src/operations/backup/index.ts
+++ b/packages/taler-wallet-core/src/operations/backup/index.ts
@@ -168,10 +168,7 @@ async function computeBackupCryptoData(
};
for (const backupExchangeDetails of backupContent.exchange_details) {
for (const backupDenom of backupExchangeDetails.denominations) {
- if (
- backupDenom.denom_pub.cipher !== DenomKeyType.Rsa &&
- backupDenom.denom_pub.cipher !== DenomKeyType.LegacyRsa
- ) {
+ if (backupDenom.denom_pub.cipher !== DenomKeyType.Rsa) {
throw Error("unsupported cipher");
}
for (const backupCoin of backupDenom.coins) {
@@ -192,18 +189,14 @@ async function computeBackupCryptoData(
LibtoolVersion.compare(backupExchangeDetails.protocol_version, "9")
?.compatible
) {
- cryptoData.rsaDenomPubToHash[
- backupDenom.denom_pub.rsa_public_key
- ] = encodeCrock(
- hash(decodeCrock(backupDenom.denom_pub.rsa_public_key)),
- );
+ 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));
+ cryptoData.rsaDenomPubToHash[backupDenom.denom_pub.rsa_public_key] =
+ encodeCrock(hashDenomPub(backupDenom.denom_pub));
} else {
throw Error("unsupported exchange protocol version");
}
@@ -220,9 +213,8 @@ async function computeBackupCryptoData(
);
const noncePub = encodeCrock(eddsaGetPublic(decodeCrock(prop.nonce_priv)));
cryptoData.proposalNoncePrivToPub[prop.nonce_priv] = noncePub;
- cryptoData.proposalIdToContractTermsHash[
- prop.proposal_id
- ] = contractTermsHash;
+ cryptoData.proposalIdToContractTermsHash[prop.proposal_id] =
+ contractTermsHash;
}
for (const purch of backupContent.purchases) {
const contractTermsHash = await cryptoApi.hashString(
@@ -230,9 +222,8 @@ async function computeBackupCryptoData(
);
const noncePub = encodeCrock(eddsaGetPublic(decodeCrock(purch.nonce_priv)));
cryptoData.proposalNoncePrivToPub[purch.nonce_priv] = noncePub;
- cryptoData.proposalIdToContractTermsHash[
- purch.proposal_id
- ] = contractTermsHash;
+ cryptoData.proposalIdToContractTermsHash[purch.proposal_id] =
+ contractTermsHash;
}
return cryptoData;
}
@@ -548,10 +539,11 @@ export interface RemoveBackupProviderRequest {
provider: string;
}
-export const codecForRemoveBackupProvider = (): Codec<RemoveBackupProviderRequest> =>
- buildCodecForObject<RemoveBackupProviderRequest>()
- .property("provider", codecForString())
- .build("RemoveBackupProviderRequest");
+export const codecForRemoveBackupProvider =
+ (): Codec<RemoveBackupProviderRequest> =>
+ buildCodecForObject<RemoveBackupProviderRequest>()
+ .property("provider", codecForString())
+ .build("RemoveBackupProviderRequest");
export async function removeBackupProvider(
ws: InternalWalletState,
@@ -619,12 +611,13 @@ interface SyncTermsOfServiceResponse {
version: string;
}
-const codecForSyncTermsOfServiceResponse = (): Codec<SyncTermsOfServiceResponse> =>
- buildCodecForObject<SyncTermsOfServiceResponse>()
- .property("storage_limit_in_megabytes", codecForNumber())
- .property("annual_fee", codecForAmountString())
- .property("version", codecForString())
- .build("SyncTermsOfServiceResponse");
+const codecForSyncTermsOfServiceResponse =
+ (): Codec<SyncTermsOfServiceResponse> =>
+ buildCodecForObject<SyncTermsOfServiceResponse>()
+ .property("storage_limit_in_megabytes", codecForNumber())
+ .property("annual_fee", codecForAmountString())
+ .property("version", codecForString())
+ .build("SyncTermsOfServiceResponse");
export interface AddBackupProviderRequest {
backupProviderBaseUrl: string;
@@ -637,12 +630,13 @@ export interface AddBackupProviderRequest {
activate?: boolean;
}
-export const codecForAddBackupProviderRequest = (): Codec<AddBackupProviderRequest> =>
- buildCodecForObject<AddBackupProviderRequest>()
- .property("backupProviderBaseUrl", codecForString())
- .property("name", codecForString())
- .property("activate", codecOptional(codecForBoolean()))
- .build("AddBackupProviderRequest");
+export const codecForAddBackupProviderRequest =
+ (): Codec<AddBackupProviderRequest> =>
+ buildCodecForObject<AddBackupProviderRequest>()
+ .property("backupProviderBaseUrl", codecForString())
+ .property("name", codecForString())
+ .property("activate", codecOptional(codecForBoolean()))
+ .build("AddBackupProviderRequest");
export async function addBackupProvider(
ws: InternalWalletState,
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts
index 8a5b35732..25b9cb92d 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -27,7 +27,11 @@ import {
CreateDepositGroupRequest,
CreateDepositGroupResponse,
DenomKeyType,
- durationFromSpec, encodeCrock, GetFeeForDepositRequest, getRandomBytes, getTimestampNow,
+ durationFromSpec,
+ encodeCrock,
+ GetFeeForDepositRequest,
+ getRandomBytes,
+ getTimestampNow,
Logger,
NotificationType,
parsePaytoUri,
@@ -38,7 +42,7 @@ import {
timestampTruncateToSecond,
TrackDepositGroupRequest,
TrackDepositGroupResponse,
- URL
+ URL,
} from "@gnu-taler/taler-util";
import { InternalWalletState } from "../common.js";
import { DepositGroupRecord, OperationStatus } from "../db.js";
@@ -54,7 +58,7 @@ import {
getCandidatePayCoins,
getTotalPaymentCost,
hashWire,
- hashWireLegacy
+ hashWireLegacy,
} from "./pay.js";
import { getTotalRefreshCost } from "./refresh.js";
@@ -199,47 +203,21 @@ async function processDepositGroupImpl(
}
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,
- };
- }
+ 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);
logger.info(`depositing to ${url}`);
const httpResp = await ws.http.postJson(url.href, requestBody);
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts
index 87200c2f9..c50afc215 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -83,15 +83,7 @@ function denominationRecordFromKeys(
denomIn: ExchangeDenomination,
): DenominationRecord {
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;
- }
+ denomPub = denomIn.denom_pub;
const denomPubHash = encodeCrock(hashDenomPub(denomPub));
const d: DenominationRecord = {
denomPub,
diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts
index 8f0727c8b..4870d446a 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -606,7 +606,6 @@ 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,
@@ -1515,14 +1514,7 @@ 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;
- }
+ wireInfoHash = contractData.wireInfoHash;
const dp = await ws.cryptoApi.signDepositPermission({
coinPriv: coin.coinPriv,
coinPub: coin.coinPub,
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts
index 1e5dd68a8..ba4cb697d 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -18,8 +18,10 @@ import {
DenomKeyType,
encodeCrock,
ExchangeProtocolVersion,
+ ExchangeRefreshRevealRequest,
getRandomBytes,
HttpStatusCode,
+ j2s,
} from "@gnu-taler/taler-util";
import {
CoinRecord,
@@ -369,10 +371,6 @@ async function refreshMelt(
let exchangeProtocolVersion: ExchangeProtocolVersion;
switch (d.oldDenom.denomPub.cipher) {
- case DenomKeyType.LegacyRsa: {
- exchangeProtocolVersion = ExchangeProtocolVersion.V9;
- break;
- }
case DenomKeyType.Rsa: {
exchangeProtocolVersion = ExchangeProtocolVersion.V12;
break;
@@ -397,16 +395,7 @@ async function refreshMelt(
oldCoin.exchangeBaseUrl,
);
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 {
+ if (oldCoin.denomPub.cipher === DenomKeyType.Rsa) {
meltReqBody = {
coin_pub: oldCoin.coinPub,
confirm_sig: derived.confirmSig,
@@ -569,10 +558,6 @@ async function refreshReveal(
let exchangeProtocolVersion: ExchangeProtocolVersion;
switch (d.oldDenom.denomPub.cipher) {
- case DenomKeyType.LegacyRsa: {
- exchangeProtocolVersion = ExchangeProtocolVersion.V9;
- break;
- }
case DenomKeyType.Rsa: {
exchangeProtocolVersion = ExchangeProtocolVersion.V12;
break;
@@ -600,7 +585,6 @@ async function refreshReveal(
throw Error("refresh index error");
}
- const evs = planchets.map((x: RefreshPlanchetInfo) => x.coinEv);
const newDenomsFlat: string[] = [];
const linkSigs: string[] = [];
@@ -620,10 +604,9 @@ async function refreshReveal(
}
}
- const req = {
- coin_evs: evs,
+ const req: ExchangeRefreshRevealRequest = {
+ coin_evs: planchets.map((x) => x.coinEv),
new_denoms_h: newDenomsFlat,
- rc: derived.hash,
transfer_privs: privs,
transfer_pub: derived.transferPubs[norevealIndex],
link_sigs: linkSigs,
@@ -666,20 +649,14 @@ async function refreshReveal(
continue;
}
const pc = derived.planchetsForGammas[norevealIndex][newCoinIndex];
- if (
- denom.denomPub.cipher !== DenomKeyType.Rsa &&
- denom.denomPub.cipher !== DenomKeyType.LegacyRsa
- ) {
+ if (denom.denomPub.cipher !== DenomKeyType.Rsa) {
throw Error("cipher unsupported");
}
const evSig = reveal.ev_sigs[newCoinIndex].ev_sig;
let rsaSig: string;
if (typeof evSig === "string") {
rsaSig = evSig;
- } else if (
- evSig.cipher === DenomKeyType.Rsa ||
- evSig.cipher === DenomKeyType.LegacyRsa
- ) {
+ } else if (evSig.cipher === DenomKeyType.Rsa) {
rsaSig = evSig.blinded_rsa_signature;
} else {
throw Error("unsupported cipher");
@@ -691,8 +668,8 @@ async function refreshReveal(
);
const coin: CoinRecord = {
blindingKey: pc.blindingKey,
- coinPriv: pc.privateKey,
- coinPub: pc.publicKey,
+ coinPriv: pc.coinPriv,
+ coinPub: pc.coinPub,
currentAmount: denom.value,
denomPub: denom.denomPub,
denomPubHash: denom.denomPubHash,
@@ -707,7 +684,7 @@ async function refreshReveal(
oldCoinPub: refreshGroup.oldCoinPubs[coinIndex],
},
suspended: false,
- coinEvHash: pc.coinEv,
+ coinEvHash: pc.coinEvHash,
};
coins.push(coin);
diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts
index f985d8aad..039fb64a1 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -306,37 +306,13 @@ async function processTipImpl(
// FIXME: Maybe we want to signal to the caller that the transient error happened?
return;
}
-
- // FIXME: Do this earlier?
- const merchantInfo = await ws.merchantOps.getMerchantInfo(
- ws,
- tipRecord.merchantBaseUrl,
- );
-
let blindedSigs: BlindedDenominationSignature[] = [];
- if (merchantInfo.protocolVersionCurrent === MerchantProtocolVersion.V3) {
- const response = await readSuccessResponseJsonOrThrow(
- merchantResp,
- codecForMerchantTipResponseV2(),
- );
- blindedSigs = response.blind_sigs.map((x) => x.blind_sig);
- } else if (
- merchantInfo.protocolVersionCurrent === MerchantProtocolVersion.V1
- ) {
- 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 (${merchantInfo.protocolVersionCurrent})`,
- );
- }
+ const response = await readSuccessResponseJsonOrThrow(
+ merchantResp,
+ codecForMerchantTipResponseV2(),
+ );
+ blindedSigs = response.blind_sigs.map((x) => x.blind_sig);
if (blindedSigs.length !== planchets.length) {
throw Error("number of tip responses does not match requested planchets");
@@ -352,17 +328,11 @@ async function processTipImpl(
const planchet = planchets[i];
checkLogicInvariant(!!planchet);
- if (
- denom.denomPub.cipher !== DenomKeyType.Rsa &&
- denom.denomPub.cipher !== DenomKeyType.LegacyRsa
- ) {
+ if (denom.denomPub.cipher !== DenomKeyType.Rsa) {
throw Error("unsupported cipher");
}
- if (
- blindedSig.cipher !== DenomKeyType.Rsa &&
- blindedSig.cipher !== DenomKeyType.LegacyRsa
- ) {
+ if (blindedSig.cipher !== DenomKeyType.Rsa) {
throw Error("unsupported cipher");
}
diff --git a/packages/taler-wallet-core/src/operations/withdraw.test.ts b/packages/taler-wallet-core/src/operations/withdraw.test.ts
index 2c890a121..02540848a 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.test.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.test.ts
@@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { Amounts } from "@gnu-taler/taler-util";
+import { Amounts, DenomKeyType } from "@gnu-taler/taler-util";
import test from "ava";
import { DenominationRecord, DenominationVerificationStatus } from "../db.js";
import { selectWithdrawalDenominations } from "./withdraw.js";
@@ -29,7 +29,7 @@ test("withdrawal selection bug repro", (t) => {
const denoms: DenominationRecord[] = [
{
denomPub: {
- cipher: 1,
+ cipher: DenomKeyType.Rsa,
rsa_public_key:
"040000XT67C8KBD6B75TTQ3SK8FWXMNQW4372T3BDDGPAMB9RFCA03638W8T3F71WFEFK9NP32VKYVNFXPYRWQ1N1HDKV5J0DFEKHBPJCYSWCBJDRNWD7G8BN8PT97FA9AMV75MYEK4X54D1HGJ207JSVJBGFCATSPNTEYNHEQF1F220W00TBZR1HNPDQFD56FG0DJQ9KGHM8EC33H6AY9YN9CNX5R3Z4TZ4Q23W47SBHB13H6W74FQJG1F50X38VRSC4SR8RWBAFB7S4K8D2H4NMRFSQT892A3T0BTBW7HM5C0H2CK6FRKG31F7W9WP1S29013K5CXYE55CT8TH6N8J9B780R42Y5S3ZB6J6E9H76XBPSGH4TGYSR2VZRB98J417KCQMZKX1BB67E7W5KVE37TC9SJ904002",
},
@@ -83,7 +83,7 @@ test("withdrawal selection bug repro", (t) => {
},
{
denomPub: {
- cipher: 1,
+ cipher: DenomKeyType.Rsa,
rsa_public_key:
"040000Y63CF78QFPKRY77BRK9P557Q1GQWX3NCZ3HSYSK0Z7TT0KGRA7N4SKBKEHSTVHX1Z9DNXMJR4EXSY1TXCKV0GJ3T3YYC6Z0JNMJFVYQAV4FX5J90NZH1N33MZTV8HS9SMNAA9S6K73G4P99GYBB01B0P6M1KXZ5JRDR7VWBR3MEJHHGJ6QBMCJR3NWJRE3WJW9PRY8QPQ2S7KFWTWRESH2DBXCXWBD2SRN6P9YX8GRAEMFEGXC9V5GVJTEMH6ZDGNXFPWZE3JVJ2Q4N9GDYKBCHZCJ7M7M2RJ9ZV4Y64NAN9BT6XDC68215GKKRHTW1BBF1MYY6AR3JCTT9HYAM923RMVQR3TAEB7SDX8J76XRZWYH3AGJCZAQGMN5C8SSH9AHQ9RNQJQ15CN45R37X4YNFJV904002",
},
@@ -138,7 +138,7 @@ test("withdrawal selection bug repro", (t) => {
},
{
denomPub: {
- cipher: 1,
+ cipher: DenomKeyType.Rsa,
rsa_public_key:
"040000YDESWC2B962DA4WK356SC50MA3N9KV0ZSGY3RC48JCTY258W909C7EEMT5BTC5KZ5T4CERCZ141P9QF87EK2BD1XEEM5GB07MB3H19WE4CQGAS8X84JBWN83PQGQXVMWE5HFA992KMGHC566GT9ZS2QPHZB6X89C4A80Z663PYAAPXP728VHAKATGNNBQ01ZZ2XD1CH9Y38YZBSPJ4K7GB2J76GBCYAVD9ENHDVWXJAXYRPBX4KSS5TXRR3K5NEN9ZV3AJD2V65K7ABRZDF5D5V1FJZZMNJ5XZ4FEREEKEBV9TDFPGJTKDEHEC60K3DN24DAATRESDJ1ZYYSYSRCAT4BT2B62ARGVMJTT5N2R126DRW9TGRWCW0ZAF2N2WET1H4NJEW77X0QT46Z5R3MZ0XPHD04002",
},
@@ -192,7 +192,7 @@ test("withdrawal selection bug repro", (t) => {
},
{
denomPub: {
- cipher: 1,
+ cipher: DenomKeyType.Rsa,
rsa_public_key:
"040000YG3T1ADB8DVA6BD3EPV6ZHSHTDW35DEN4VH1AE6CSB7P1PSDTNTJG866PHF6QB1CCWYCVRGA0FVBJ9Q0G7KV7AD9010GDYBQH0NNPHW744MTNXVXWBGGGRGQGYK4DTYN1DSWQ1FZNDSZZPB5BEKG2PDJ93NX2JTN06Y8QMS2G734Z9XHC10EENBG2KVB7EJ3CM8PV1T32RC7AY62F3496E8D8KRHJQQTT67DSGMNKK86QXVDTYW677FG27DP20E8XY3M6FQD53NDJ1WWES91401MV1A3VXVPGC76GZVDD62W3WTJ1YMKHTTA3MRXX3VEAAH3XTKDN1ER7X6CZPMYTF8VK735VP2B2TZGTF28TTW4FZS32SBS64APCDF6SZQ427N5538TJC7SRE71YSP5ET8GS904002",
},
@@ -247,7 +247,7 @@ test("withdrawal selection bug repro", (t) => {
},
{
denomPub: {
- cipher: 1,
+ cipher: DenomKeyType.Rsa,
rsa_public_key:
"040000ZC0G60E9QQ5PD81TSDWD9GV5Y6P8Z05NSPA696DP07NGQQVSRQXBA76Q6PRB0YFX295RG4MTQJXAZZ860ET307HSC2X37XAVGQXRVB8Q4F1V7NP5ZEVKTX75DZK1QRAVHEZGQYKSSH6DBCJNQF6V9WNQF3GEYVA4KCBHA7JF772KHXM9642C28Z0AS4XXXV2PABAN5C8CHYD5H7JDFNK3920W5Q69X0BS84XZ4RE2PW6HM1WZ6KGZ3MKWWWCPKQ1FSFABRBWKAB09PF563BEBXKY6M38QETPH5EDWGANHD0SC3QV0WXYVB7BNHNNQ0J5BNV56K563SYHM4E5ND260YRJSYA1GN5YSW2B1J5T1A1EBNYF2DN6JNJKWXWEQ42G5YS17ZSZ5EWDRA9QKV8EGTCNAD04002",
},
@@ -301,7 +301,7 @@ test("withdrawal selection bug repro", (t) => {
},
{
denomPub: {
- cipher: 1,
+ cipher: DenomKeyType.Rsa,
rsa_public_key:
"040000ZSK2PMVY6E3NBQ52KXMW029M60F4BWYTDS0FZSD0PE53CNZ9H6TM3GQK1WRTEKQ5GRWJ1J9DY6Y42SP47QVT1XD1G0W05SQ5F3F7P5KSWR0FJBJ9NZBXQEVN8Q4JRC94X3JJ3XV3KBYTZ2HTDFV28C3H2SRR0XGNZB4FY85NDZF1G4AEYJJ9QB3C0V8H70YB8RV3FKTNH7XS4K4HFNZHJ5H9VMX5SM9Z2DX37HA5WFH0E2MJBVVF2BWWA5M0HPPSB365RAE2AMD42Q65A96WD80X27SB2ZNQZ8WX0K13FWF85GZ6YNYAJGE1KGN06JDEKE9QD68Z651D7XE8V6664TVVC8M68S7WD0DSXMJQKQ0BNJXNDE29Q7MRX6DA3RW0PZ44B3TKRK0294FPVZTNSTA6XF04002",
},
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index 79220089b..731e9b3aa 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -43,6 +43,7 @@ import {
DenomKeyType,
LibtoolVersion,
UnblindedSignature,
+ ExchangeWithdrawRequest,
} from "@gnu-taler/taler-util";
import {
CoinRecord,
@@ -497,9 +498,8 @@ async function processPlanchetExchangeRequest(
`processing planchet #${coinIdx} in withdrawal ${withdrawalGroup.withdrawalGroupId}`,
);
- const reqBody: any = {
+ const reqBody: ExchangeWithdrawRequest = {
denom_pub_hash: planchet.denomPubHash,
- reserve_pub: planchet.reservePub,
reserve_sig: planchet.withdrawSig,
coin_ev: planchet.coinEv,
};
@@ -580,28 +580,12 @@ async function processPlanchetVerifyAndStoreCoin(
const { planchet, exchangeBaseUrl } = d;
const planchetDenomPub = planchet.denomPub;
- if (
- planchetDenomPub.cipher !== DenomKeyType.Rsa &&
- planchetDenomPub.cipher !== DenomKeyType.LegacyRsa
- ) {
+ if (planchetDenomPub.cipher !== DenomKeyType.Rsa) {
throw Error(`cipher (${planchetDenomPub.cipher}) not supported`);
}
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
- )
- ) {
+ if (!(evSig.cipher === DenomKeyType.Rsa)) {
throw Error("unsupported cipher");
}
@@ -639,10 +623,7 @@ async function processPlanchetVerifyAndStoreCoin(
}
let denomSig: UnblindedSignature;
- if (
- planchet.denomPub.cipher === DenomKeyType.LegacyRsa ||
- planchet.denomPub.cipher === DenomKeyType.Rsa
- ) {
+ if (planchet.denomPub.cipher === DenomKeyType.Rsa) {
denomSig = {
cipher: planchet.denomPub.cipher,
rsa_signature: denomSigRsa,
diff --git a/packages/taler-wallet-core/src/util/coinSelection.test.ts b/packages/taler-wallet-core/src/util/coinSelection.test.ts
index 49f8d1635..1675a9a35 100644
--- a/packages/taler-wallet-core/src/util/coinSelection.test.ts
+++ b/packages/taler-wallet-core/src/util/coinSelection.test.ts
@@ -18,7 +18,7 @@
* Imports.
*/
import test from "ava";
-import { AmountJson, Amounts } from "@gnu-taler/taler-util";
+import { AmountJson, Amounts, DenomKeyType } from "@gnu-taler/taler-util";
import { AvailableCoinInfo, selectPayCoins } from "./coinSelection.js";
function a(x: string): AmountJson {
@@ -34,7 +34,7 @@ function fakeAci(current: string, feeDeposit: string): AvailableCoinInfo {
availableAmount: a(current),
coinPub: "foobar",
denomPub: {
- cipher: 1,
+ cipher: DenomKeyType.Rsa,
rsa_public_key: "foobar",
},
feeDeposit: a(feeDeposit),
@@ -47,7 +47,7 @@ test("it should be able to pay if merchant takes the fees", (t) => {
fakeAci("EUR:1.0", "EUR:0.1"),
fakeAci("EUR:1.0", "EUR:0.0"),
];
- acis.forEach((x, i) => x.coinPub = String(i));
+ acis.forEach((x, i) => (x.coinPub = String(i)));
const res = selectPayCoins({
candidates: {
@@ -75,7 +75,7 @@ test("it should take the last two coins if it pays less fees", (t) => {
// Merchant covers the fee, this one shouldn't be used
fakeAci("EUR:1.0", "EUR:0.0"),
];
- acis.forEach((x, i) => x.coinPub = String(i));
+ acis.forEach((x, i) => (x.coinPub = String(i)));
const res = selectPayCoins({
candidates: {
@@ -102,8 +102,8 @@ test("it should take the last coins if the merchant doest not take all the fee",
fakeAci("EUR:1.0", "EUR:0.5"),
// this coin should be selected instead of previous one with fee
fakeAci("EUR:1.0", "EUR:0.0"),
- ]
- acis.forEach((x, i) => x.coinPub = String(i));
+ ];
+ acis.forEach((x, i) => (x.coinPub = String(i)));
const res = selectPayCoins({
candidates: {
@@ -221,7 +221,7 @@ test("it should use the coins that spent less relative fee", (t) => {
fakeAci("EUR:0.05", "EUR:0.05"),
fakeAci("EUR:0.05", "EUR:0.05"),
];
- acis.forEach((x, i) => x.coinPub = String(i));
+ acis.forEach((x, i) => (x.coinPub = String(i)));
const res = selectPayCoins({
candidates: {