aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-03-23 21:24:23 +0100
committerFlorian Dold <florian@dold.me>2022-03-23 21:24:36 +0100
commitd881f4fd258a27cc765a25c24e5fef9f86b6226f (patch)
tree3254444f93ef552f4ac65f14e581ed761b9df79e /packages
parente21c1b31928cd6bfe90150ea2de19799b6359c40 (diff)
wallet: simplify crypto workers
Diffstat (limited to 'packages')
-rw-r--r--packages/taler-wallet-cli/src/bench2.ts5
-rw-r--r--packages/taler-wallet-cli/src/index.ts37
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts5
-rw-r--r--packages/taler-wallet-core/src/crypto/cryptoImplementation.ts (renamed from packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts)713
-rw-r--r--packages/taler-wallet-core/src/crypto/cryptoTypes.ts1
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/cryptoDispatcher.ts (renamed from packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts)216
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/nodeThreadWorker.ts19
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/rpcClient.ts90
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/synchronousWorker.ts49
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/synchronousWorkerFactory.ts123
-rw-r--r--packages/taler-wallet-core/src/dbless.ts8
-rw-r--r--packages/taler-wallet-core/src/headless/helpers.ts4
-rw-r--r--packages/taler-wallet-core/src/index.ts11
-rw-r--r--packages/taler-wallet-core/src/internal-wallet-state.ts5
-rw-r--r--packages/taler-wallet-core/src/operations/backup/index.ts23
-rw-r--r--packages/taler-wallet-core/src/operations/backup/state.ts2
-rw-r--r--packages/taler-wallet-core/src/operations/common.ts2
-rw-r--r--packages/taler-wallet-core/src/operations/deposits.ts14
-rw-r--r--packages/taler-wallet-core/src/operations/exchanges.ts24
-rw-r--r--packages/taler-wallet-core/src/operations/pay.ts33
-rw-r--r--packages/taler-wallet-core/src/operations/refresh.ts22
-rw-r--r--packages/taler-wallet-core/src/operations/reserves.ts2
-rw-r--r--packages/taler-wallet-core/src/operations/tip.ts22
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts29
-rw-r--r--packages/taler-wallet-core/src/wallet.ts22
-rw-r--r--packages/taler-wallet-webextension/src/browserWorkerEntry.ts6
-rw-r--r--packages/taler-wallet-webextension/src/wxApi.ts11
27 files changed, 784 insertions, 714 deletions
diff --git a/packages/taler-wallet-cli/src/bench2.ts b/packages/taler-wallet-cli/src/bench2.ts
index 43c28882e..c1fa674c4 100644
--- a/packages/taler-wallet-cli/src/bench2.ts
+++ b/packages/taler-wallet-cli/src/bench2.ts
@@ -27,7 +27,7 @@ import {
import {
checkReserve,
createFakebankReserve,
- CryptoApi,
+ CryptoDispatcher,
depositCoin,
downloadExchangeInfo,
findDenomOrThrow,
@@ -50,7 +50,8 @@ export async function runBench2(configJson: any): Promise<void> {
// Validate the configuration file for this benchmark.
const benchConf = codecForBench2Config().decode(configJson);
const curr = benchConf.currency;
- const cryptoApi = new CryptoApi(new SynchronousCryptoWorkerFactory());
+ const cryptoDisp = new CryptoDispatcher(new SynchronousCryptoWorkerFactory());
+ const cryptoApi = cryptoDisp.cryptoApi;
const http = new NodeHttpLib();
http.setThrottling(false);
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
index 0ed935a15..3b79f78b8 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -50,18 +50,21 @@ import {
NodeHttpLib,
getDefaultNodeWallet,
NodeThreadCryptoWorkerFactory,
- CryptoApi,
walletCoreDebugFlags,
WalletApiOperation,
WalletCoreApiClient,
Wallet,
getErrorDetailFromException,
+ CryptoDispatcher,
+ SynchronousCryptoWorkerFactory,
+ nativeCrypto,
} from "@gnu-taler/taler-wallet-core";
import { lintExchangeDeployment } from "./lint.js";
import { runBench1 } from "./bench1.js";
import { runEnv1 } from "./env1.js";
import { GlobalTestState, runTestWithState } from "./harness/harness.js";
import { runBench2 } from "./bench2.js";
+import { TalerCryptoInterface, TalerCryptoInterfaceR } from "@gnu-taler/taler-wallet-core/src/crypto/cryptoImplementation";
// This module also serves as the entry point for the crypto
// thread worker, and thus must expose these two handlers.
@@ -1121,14 +1124,30 @@ testCli.subcommand("tvgcheck", "tvgcheck").action(async (args) => {
console.log("check passed!");
});
-testCli.subcommand("cryptoworker", "cryptoworker").action(async (args) => {
- const workerFactory = new NodeThreadCryptoWorkerFactory();
- const cryptoApi = new CryptoApi(workerFactory);
- const input = "foo";
- console.log(`testing crypto worker by hashing string '${input}'`);
- const res = await cryptoApi.hashString(input);
- console.log(res);
-});
+testCli
+ .subcommand("cryptoworker", "cryptoworker")
+ .maybeOption("impl", ["--impl"], clk.STRING)
+ .action(async (args) => {
+ let cryptoApi: TalerCryptoInterface;
+ if (!args.cryptoworker.impl || args.cryptoworker.impl === "node") {
+ const workerFactory = new NodeThreadCryptoWorkerFactory();
+ const cryptoDisp = new CryptoDispatcher(workerFactory);
+ cryptoApi = cryptoDisp.cryptoApi;
+ } else if (args.cryptoworker.impl === "sync") {
+ const workerFactory = new SynchronousCryptoWorkerFactory();
+ const cryptoDisp = new CryptoDispatcher(workerFactory);
+ cryptoApi = cryptoDisp.cryptoApi;
+ } else if (args.cryptoworker.impl === "none") {
+ cryptoApi = nativeCrypto;
+ } else {
+ throw Error("invalid impl");
+ }
+
+ const input = "foo";
+ console.log(`testing crypto worker by hashing string '${input}'`);
+ const res = await cryptoApi.hashString({ str: input });
+ console.log(res);
+ });
export function main() {
if (process.env["TALER_WALLET_DEBUG_DENOMSEL_ALLOW_LATE"]) {
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts b/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts
index 146603f3a..d8abae136 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts
@@ -20,7 +20,7 @@
import { j2s } from "@gnu-taler/taler-util";
import {
checkReserve,
- CryptoApi,
+ CryptoDispatcher,
depositCoin,
downloadExchangeInfo,
findDenomOrThrow,
@@ -44,7 +44,8 @@ export async function runWalletDblessTest(t: GlobalTestState) {
const { bank, exchange } = await createSimpleTestkudosEnvironment(t);
const http = new NodeHttpLib();
- const cryptoApi = new CryptoApi(new SynchronousCryptoWorkerFactory());
+ const cryptiDisp = new CryptoDispatcher(new SynchronousCryptoWorkerFactory());
+ const cryptoApi = cryptiDisp.cryptoApi;
try {
// Withdraw digital cash into the wallet.
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
index b27067885..63b2687b6 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
@@ -44,7 +44,6 @@ import {
ExchangeProtocolVersion,
FreshCoin,
hash,
- HashCodeString,
hashCoinEv,
hashCoinEvInner,
hashDenomPub,
@@ -67,15 +66,13 @@ import {
setupWithdrawPlanchet,
stringToBytes,
TalerSignaturePurpose,
- AbsoluteTime,
BlindedDenominationSignature,
UnblindedSignature,
PlanchetUnblindInfo,
TalerProtocolTimestamp,
} from "@gnu-taler/taler-util";
import bigint from "big-integer";
-import { DenominationRecord, WireFee } from "../../db.js";
-import * as timer from "../../util/timer.js";
+import { DenominationRecord, WireFee } from "../db.js";
import {
CreateRecoupRefreshReqRequest,
CreateRecoupReqRequest,
@@ -84,90 +81,288 @@ import {
DeriveRefreshSessionRequest,
DeriveTipRequest,
SignTrackTransactionRequest,
-} from "../cryptoTypes.js";
+} 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);
- const dvbuf = new DataView(buffer);
- const u8buf = new Uint8Array(buffer);
- const curr = stringToBytes(amount.currency);
- if (typeof dvbuf.setBigUint64 !== "undefined") {
- dvbuf.setBigUint64(0, BigInt(amount.value));
- } else {
- const arr = bigint(amount.value).toArray(2 ** 8).value;
- let offset = 8 - arr.length;
- for (let i = 0; i < arr.length; i++) {
- dvbuf.setUint8(offset++, arr[i]);
- }
- }
- dvbuf.setUint32(8, amount.fraction);
- u8buf.set(curr, 8 + 4);
+/**
+ * Interface for (asynchronous) cryptographic operations that
+ * Taler uses.
+ */
+export interface TalerCryptoInterface {
+ /**
+ * Create a pre-coin of the given denomination to be withdrawn from then given
+ * reserve.
+ */
+ createPlanchet(req: PlanchetCreationRequest): Promise<WithdrawalPlanchet>;
- return u8buf;
+ eddsaSign(req: EddsaSignRequest): Promise<EddsaSignResponse>;
+
+ /**
+ * Create a planchet used for tipping, including the private keys.
+ */
+ createTipPlanchet(req: DeriveTipRequest): Promise<DerivedTipPlanchet>;
+
+ signTrackTransaction(
+ req: SignTrackTransactionRequest,
+ ): Promise<EddsaSigningResult>;
+
+ createRecoupRequest(req: CreateRecoupReqRequest): Promise<RecoupRequest>;
+
+ createRecoupRefreshRequest(
+ req: CreateRecoupRefreshReqRequest,
+ ): Promise<RecoupRefreshRequest>;
+
+ isValidPaymentSignature(
+ req: PaymentSignatureValidationRequest,
+ ): Promise<ValidationResult>;
+
+ isValidWireFee(req: WireFeeValidationRequest): Promise<ValidationResult>;
+
+ isValidDenom(req: DenominationValidationRequest): Promise<ValidationResult>;
+
+ isValidWireAccount(
+ req: WireAccountValidationRequest,
+ ): Promise<ValidationResult>;
+
+ isValidContractTermsSignature(
+ req: ContractTermsValidationRequest,
+ ): Promise<ValidationResult>;
+
+ createEddsaKeypair(req: {}): Promise<EddsaKeypair>;
+
+ eddsaGetPublic(req: EddsaGetPublicRequest): Promise<EddsaKeypair>;
+
+ unblindDenominationSignature(
+ req: UnblindDenominationSignatureRequest,
+ ): Promise<UnblindedSignature>;
+
+ rsaUnblind(req: RsaUnblindRequest): Promise<RsaUnblindResponse>;
+
+ rsaVerify(req: RsaVerificationRequest): Promise<ValidationResult>;
+
+ signDepositPermission(
+ depositInfo: DepositInfo,
+ ): Promise<CoinDepositPermission>;
+
+ deriveRefreshSession(
+ req: DeriveRefreshSessionRequest,
+ ): Promise<DerivedRefreshSession>;
+
+ hashString(req: HashStringRequest): Promise<HashStringResult>;
+
+ signCoinLink(req: SignCoinLinkRequest): Promise<EddsaSigningResult>;
+
+ makeSyncSignature(req: MakeSyncSignatureRequest): Promise<EddsaSigningResult>;
}
-function timestampRoundedToBuffer(ts: TalerProtocolTimestamp): Uint8Array {
- const b = new ArrayBuffer(8);
- const v = new DataView(b);
- // The buffer we sign over represents the timestamp in microseconds.
- if (typeof v.setBigUint64 !== "undefined") {
- const s = BigInt(ts.t_s) * BigInt(1000 * 1000);
- v.setBigUint64(0, s);
- } else {
- const s =
- ts.t_s === "never" ? bigint.zero : bigint(ts.t_s).multiply(1000 * 1000);
- const arr = s.toArray(2 ** 8).value;
- let offset = 8 - arr.length;
- for (let i = 0; i < arr.length; i++) {
- v.setUint8(offset++, arr[i]);
- }
- }
- return new Uint8Array(b);
+/**
+ * Implementation of the Taler crypto interface where every function
+ * always throws. Only useful in practice as a way to iterate through
+ * all possible crypto functions.
+ *
+ * (This list can be easily auto-generated by your favorite IDE).
+ */
+export const nullCrypto: TalerCryptoInterface = {
+ createPlanchet: function (
+ req: PlanchetCreationRequest,
+ ): Promise<WithdrawalPlanchet> {
+ throw new Error("Function not implemented.");
+ },
+ eddsaSign: function (req: EddsaSignRequest): Promise<EddsaSignResponse> {
+ throw new Error("Function not implemented.");
+ },
+ createTipPlanchet: function (
+ req: DeriveTipRequest,
+ ): Promise<DerivedTipPlanchet> {
+ throw new Error("Function not implemented.");
+ },
+ signTrackTransaction: function (
+ req: SignTrackTransactionRequest,
+ ): Promise<EddsaSigningResult> {
+ throw new Error("Function not implemented.");
+ },
+ createRecoupRequest: function (
+ req: CreateRecoupReqRequest,
+ ): Promise<RecoupRequest> {
+ throw new Error("Function not implemented.");
+ },
+ createRecoupRefreshRequest: function (
+ req: CreateRecoupRefreshReqRequest,
+ ): Promise<RecoupRefreshRequest> {
+ throw new Error("Function not implemented.");
+ },
+ isValidPaymentSignature: function (
+ req: PaymentSignatureValidationRequest,
+ ): Promise<ValidationResult> {
+ throw new Error("Function not implemented.");
+ },
+ isValidWireFee: function (
+ req: WireFeeValidationRequest,
+ ): Promise<ValidationResult> {
+ throw new Error("Function not implemented.");
+ },
+ isValidDenom: function (
+ req: DenominationValidationRequest,
+ ): Promise<ValidationResult> {
+ throw new Error("Function not implemented.");
+ },
+ isValidWireAccount: function (
+ req: WireAccountValidationRequest,
+ ): Promise<ValidationResult> {
+ throw new Error("Function not implemented.");
+ },
+ isValidContractTermsSignature: function (
+ req: ContractTermsValidationRequest,
+ ): Promise<ValidationResult> {
+ throw new Error("Function not implemented.");
+ },
+ createEddsaKeypair: function (req: {}): Promise<EddsaKeypair> {
+ throw new Error("Function not implemented.");
+ },
+ eddsaGetPublic: function (req: EddsaGetPublicRequest): Promise<EddsaKeypair> {
+ throw new Error("Function not implemented.");
+ },
+ unblindDenominationSignature: function (
+ req: UnblindDenominationSignatureRequest,
+ ): Promise<UnblindedSignature> {
+ throw new Error("Function not implemented.");
+ },
+ rsaUnblind: function (req: RsaUnblindRequest): Promise<RsaUnblindResponse> {
+ throw new Error("Function not implemented.");
+ },
+ rsaVerify: function (req: RsaVerificationRequest): Promise<ValidationResult> {
+ throw new Error("Function not implemented.");
+ },
+ signDepositPermission: function (
+ depositInfo: DepositInfo,
+ ): Promise<CoinDepositPermission> {
+ throw new Error("Function not implemented.");
+ },
+ deriveRefreshSession: function (
+ req: DeriveRefreshSessionRequest,
+ ): Promise<DerivedRefreshSession> {
+ throw new Error("Function not implemented.");
+ },
+ hashString: function (req: HashStringRequest): Promise<HashStringResult> {
+ throw new Error("Function not implemented.");
+ },
+ signCoinLink: function (
+ req: SignCoinLinkRequest,
+ ): Promise<EddsaSigningResult> {
+ throw new Error("Function not implemented.");
+ },
+ makeSyncSignature: function (
+ req: MakeSyncSignatureRequest,
+ ): Promise<EddsaSigningResult> {
+ throw new Error("Function not implemented.");
+ },
+};
+
+export type WithArg<X> = X extends (req: infer T) => infer R
+ ? (tci: TalerCryptoInterfaceR, req: T) => R
+ : never;
+
+export type TalerCryptoInterfaceR = {
+ [x in keyof TalerCryptoInterface]: WithArg<TalerCryptoInterface[x]>;
+};
+
+export interface SignCoinLinkRequest {
+ oldCoinPriv: string;
+ newDenomHash: string;
+ oldCoinPub: string;
+ transferPub: string;
+ coinEv: CoinEnvelope;
}
-export interface PrimitiveWorker {
- setupRefreshPlanchet(arg0: {
- transfer_secret: string;
- coin_index: number;
- }): Promise<{
- coin_pub: string;
- coin_priv: string;
- blinding_key: string;
- }>;
- eddsaVerify(req: {
- msg: string;
- sig: string;
- pub: string;
- }): Promise<{ valid: boolean }>;
-
- eddsaSign(req: { msg: string; priv: string }): Promise<{ sig: string }>;
+export interface RsaVerificationRequest {
+ hm: string;
+ sig: string;
+ pk: string;
}
-async function myEddsaSign(
- primitiveWorker: PrimitiveWorker | undefined,
- req: { msg: string; priv: string },
-): Promise<{ sig: string }> {
- if (primitiveWorker) {
- return primitiveWorker.eddsaSign(req);
- }
- const sig = eddsaSign(decodeCrock(req.msg), decodeCrock(req.priv));
- return {
- sig: encodeCrock(sig),
- };
+export interface EddsaSigningResult {
+ sig: string;
}
-export class CryptoImplementation {
- static enableTracing = false;
+export interface ValidationResult {
+ valid: boolean;
+}
- constructor(private primitiveWorker?: PrimitiveWorker) {}
+export interface HashStringRequest {
+ str: string;
+}
+
+export interface HashStringResult {
+ h: string;
+}
+
+export interface WireFeeValidationRequest {
+ type: string;
+ wf: WireFee;
+ masterPub: string;
+}
+
+export interface DenominationValidationRequest {
+ denom: DenominationRecord;
+ masterPub: string;
+}
+
+export interface PaymentSignatureValidationRequest {
+ sig: string;
+ contractHash: string;
+ merchantPub: string;
+}
+
+export interface ContractTermsValidationRequest {
+ contractTermsHash: string;
+ sig: string;
+ merchantPub: string;
+}
+
+export interface WireAccountValidationRequest {
+ versionCurrent: ExchangeProtocolVersion;
+ paytoUri: string;
+ sig: string;
+ masterPub: string;
+}
+
+export interface EddsaKeypair {
+ priv: string;
+ pub: string;
+}
+
+export interface EddsaGetPublicRequest {
+ priv: string;
+}
+
+export interface UnblindDenominationSignatureRequest {
+ planchet: PlanchetUnblindInfo;
+ evSig: BlindedDenominationSignature;
+}
+
+export interface RsaUnblindRequest {
+ blindedSig: string;
+ bk: string;
+ pk: string;
+}
+
+export interface RsaUnblindResponse {
+ sig: string;
+}
+
+export const nativeCryptoR: TalerCryptoInterfaceR = {
+ async eddsaSign(
+ tci: TalerCryptoInterfaceR,
+ req: EddsaSignRequest,
+ ): Promise<EddsaSignResponse> {
+ return {
+ sig: encodeCrock(eddsaSign(decodeCrock(req.msg), decodeCrock(req.priv))),
+ };
+ },
- /**
- * Create a pre-coin of the given denomination to be withdrawn from then given
- * reserve.
- */
async createPlanchet(
+ tci: TalerCryptoInterfaceR,
req: PlanchetCreationRequest,
): Promise<WithdrawalPlanchet> {
const denomPub = req.denomPub;
@@ -195,7 +390,7 @@ export class CryptoImplementation {
.put(evHash)
.build();
- const sigResult = await myEddsaSign(this.primitiveWorker, {
+ const sigResult = await tci.eddsaSign(tci, {
msg: encodeCrock(withdrawRequest),
priv: req.reservePriv,
});
@@ -216,12 +411,12 @@ export class CryptoImplementation {
} else {
throw Error("unsupported cipher, unable to create planchet");
}
- }
+ },
- /**
- * Create a planchet used for tipping, including the private keys.
- */
- createTipPlanchet(req: DeriveTipRequest): DerivedTipPlanchet {
+ async createTipPlanchet(
+ tci: TalerCryptoInterfaceR,
+ req: DeriveTipRequest,
+ ): Promise<DerivedTipPlanchet> {
if (req.denomPub.cipher !== DenomKeyType.Rsa) {
throw Error(`unsupported cipher (${req.denomPub.cipher})`);
}
@@ -243,22 +438,28 @@ export class CryptoImplementation {
coinPub: encodeCrock(fc.coinPub),
};
return tipPlanchet;
- }
+ },
- signTrackTransaction(req: SignTrackTransactionRequest): string {
+ async signTrackTransaction(
+ tci: TalerCryptoInterfaceR,
+ req: SignTrackTransactionRequest,
+ ): Promise<EddsaSigningResult> {
const p = buildSigPS(TalerSignaturePurpose.MERCHANT_TRACK_TRANSACTION)
.put(decodeCrock(req.contractTermsHash))
.put(decodeCrock(req.wireHash))
.put(decodeCrock(req.merchantPub))
.put(decodeCrock(req.coinPub))
.build();
- return encodeCrock(eddsaSign(p, decodeCrock(req.merchantPriv)));
- }
+ return { sig: encodeCrock(eddsaSign(p, decodeCrock(req.merchantPriv))) };
+ },
/**
* Create and sign a message to recoup a coin.
*/
- createRecoupRequest(req: CreateRecoupReqRequest): RecoupRequest {
+ async createRecoupRequest(
+ tci: TalerCryptoInterfaceR,
+ req: CreateRecoupReqRequest,
+ ): Promise<RecoupRequest> {
const p = buildSigPS(TalerSignaturePurpose.WALLET_COIN_RECOUP)
.put(decodeCrock(req.denomPubHash))
.put(decodeCrock(req.blindingKey))
@@ -281,14 +482,15 @@ export class CryptoImplementation {
} else {
throw new Error();
}
- }
+ },
/**
* Create and sign a message to recoup a coin.
*/
- createRecoupRefreshRequest(
+ async createRecoupRefreshRequest(
+ tci: TalerCryptoInterfaceR,
req: CreateRecoupRefreshReqRequest,
- ): RecoupRefreshRequest {
+ ): Promise<RecoupRefreshRequest> {
const p = buildSigPS(TalerSignaturePurpose.WALLET_COIN_RECOUP_REFRESH)
.put(decodeCrock(req.denomPubHash))
.put(decodeCrock(req.blindingKey))
@@ -311,32 +513,32 @@ export class CryptoImplementation {
} else {
throw new Error();
}
- }
+ },
/**
* Check if a payment signature is valid.
*/
- isValidPaymentSignature(
- sig: string,
- contractHash: string,
- merchantPub: string,
- ): boolean {
+ async isValidPaymentSignature(
+ tci: TalerCryptoInterfaceR,
+ req: PaymentSignatureValidationRequest,
+ ): Promise<ValidationResult> {
+ const { contractHash, sig, merchantPub } = req;
const p = buildSigPS(TalerSignaturePurpose.MERCHANT_PAYMENT_OK)
.put(decodeCrock(contractHash))
.build();
const sigBytes = decodeCrock(sig);
const pubBytes = decodeCrock(merchantPub);
- return eddsaVerify(p, sigBytes, pubBytes);
- }
+ return { valid: eddsaVerify(p, sigBytes, pubBytes) };
+ },
/**
* Check if a wire fee is correctly signed.
*/
async isValidWireFee(
- type: string,
- wf: WireFee,
- masterPub: string,
- ): Promise<boolean> {
+ tci: TalerCryptoInterfaceR,
+ req: WireFeeValidationRequest,
+ ): Promise<ValidationResult> {
+ const { type, wf, masterPub } = req;
const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_FEES)
.put(hash(stringToBytes(type + "\0")))
.put(timestampRoundedToBuffer(wf.startStamp))
@@ -347,25 +549,17 @@ export class CryptoImplementation {
.build();
const sig = decodeCrock(wf.sig);
const pub = decodeCrock(masterPub);
- if (this.primitiveWorker) {
- return (
- await this.primitiveWorker.eddsaVerify({
- msg: encodeCrock(p),
- pub: masterPub,
- sig: encodeCrock(sig),
- })
- ).valid;
- }
- return eddsaVerify(p, sig, pub);
- }
+ return { valid: eddsaVerify(p, sig, pub) };
+ },
/**
* Check if the signature of a denomination is valid.
*/
async isValidDenom(
- denom: DenominationRecord,
- masterPub: string,
- ): Promise<boolean> {
+ tci: TalerCryptoInterfaceR,
+ req: DenominationValidationRequest,
+ ): Promise<ValidationResult> {
+ const { masterPub, denom } = req;
const p = buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY)
.put(decodeCrock(masterPub))
.put(timestampRoundedToBuffer(denom.stampStart))
@@ -382,56 +576,59 @@ export class CryptoImplementation {
const sig = decodeCrock(denom.masterSig);
const pub = decodeCrock(masterPub);
const res = eddsaVerify(p, sig, pub);
- return res;
- }
-
- isValidWireAccount(
- versionCurrent: ExchangeProtocolVersion,
- paytoUri: string,
- sig: string,
- masterPub: string,
- ): boolean {
+ return { valid: res };
+ },
+
+ async isValidWireAccount(
+ tci: TalerCryptoInterfaceR,
+ req: WireAccountValidationRequest,
+ ): Promise<ValidationResult> {
+ const { sig, masterPub, paytoUri } = req;
const paytoHash = hashTruncate32(stringToBytes(paytoUri + "\0"));
const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_DETAILS)
.put(paytoHash)
.build();
- return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub));
- }
-
- isValidContractTermsSignature(
- contractTermsHash: string,
- sig: string,
- merchantPub: string,
- ): boolean {
- const cthDec = decodeCrock(contractTermsHash);
+ return { valid: eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub)) };
+ },
+
+ async isValidContractTermsSignature(
+ tci: TalerCryptoInterfaceR,
+ req: ContractTermsValidationRequest,
+ ): Promise<ValidationResult> {
+ const cthDec = decodeCrock(req.contractTermsHash);
const p = buildSigPS(TalerSignaturePurpose.MERCHANT_CONTRACT)
.put(cthDec)
.build();
- return eddsaVerify(p, decodeCrock(sig), decodeCrock(merchantPub));
- }
+ return {
+ valid: eddsaVerify(p, decodeCrock(req.sig), decodeCrock(req.merchantPub)),
+ };
+ },
/**
* Create a new EdDSA key pair.
*/
- createEddsaKeypair(): { priv: string; pub: string } {
+ async createEddsaKeypair(tci: TalerCryptoInterfaceR): Promise<EddsaKeypair> {
const pair = createEddsaKeyPair();
return {
priv: encodeCrock(pair.eddsaPriv),
pub: encodeCrock(pair.eddsaPub),
};
- }
+ },
- eddsaGetPublic(key: string): { priv: string; pub: string } {
+ async eddsaGetPublic(
+ tci: TalerCryptoInterfaceR,
+ req: EddsaGetPublicRequest,
+ ): Promise<EddsaKeypair> {
return {
- priv: key,
- pub: encodeCrock(eddsaGetPublic(decodeCrock(key))),
+ priv: req.priv,
+ pub: encodeCrock(eddsaGetPublic(decodeCrock(req.priv))),
};
- }
+ },
- unblindDenominationSignature(req: {
- planchet: PlanchetUnblindInfo;
- evSig: BlindedDenominationSignature;
- }): UnblindedSignature {
+ async unblindDenominationSignature(
+ tci: TalerCryptoInterfaceR,
+ req: UnblindDenominationSignatureRequest,
+ ): Promise<UnblindedSignature> {
if (req.evSig.cipher === DenomKeyType.Rsa) {
if (req.planchet.denomPub.cipher !== DenomKeyType.Rsa) {
throw new Error(
@@ -450,32 +647,45 @@ export class CryptoImplementation {
} else {
throw Error(`unblinding for cipher ${req.evSig.cipher} not implemented`);
}
- }
+ },
/**
* Unblind a blindly signed value.
*/
- rsaUnblind(blindedSig: string, bk: string, pk: string): string {
+ async rsaUnblind(
+ tci: TalerCryptoInterfaceR,
+ req: RsaUnblindRequest,
+ ): Promise<RsaUnblindResponse> {
const denomSig = rsaUnblind(
- decodeCrock(blindedSig),
- decodeCrock(pk),
- decodeCrock(bk),
+ decodeCrock(req.blindedSig),
+ decodeCrock(req.pk),
+ decodeCrock(req.bk),
);
- return encodeCrock(denomSig);
- }
+ return { sig: encodeCrock(denomSig) };
+ },
/**
* Unblind a blindly signed value.
*/
- rsaVerify(hm: string, sig: string, pk: string): boolean {
- return rsaVerify(hash(decodeCrock(hm)), decodeCrock(sig), decodeCrock(pk));
- }
+ async rsaVerify(
+ tci: TalerCryptoInterfaceR,
+ req: RsaVerificationRequest,
+ ): Promise<ValidationResult> {
+ return {
+ valid: rsaVerify(
+ hash(decodeCrock(req.hm)),
+ decodeCrock(req.sig),
+ decodeCrock(req.pk),
+ ),
+ };
+ },
/**
* Generate updated coins (to store in the database)
* and deposit permissions for each given coin.
*/
async signDepositPermission(
+ tci: TalerCryptoInterfaceR,
depositInfo: DepositInfo,
): Promise<CoinDepositPermission> {
// FIXME: put extensions here if used
@@ -498,7 +708,7 @@ export class CryptoImplementation {
} else {
throw Error("unsupported exchange protocol version");
}
- const coinSigRes = await myEddsaSign(this.primitiveWorker, {
+ const coinSigRes = await this.eddsaSign(tci, {
msg: encodeCrock(d),
priv: depositInfo.coinPriv,
});
@@ -521,9 +731,10 @@ export class CryptoImplementation {
`unsupported denomination cipher (${depositInfo.denomKeyType})`,
);
}
- }
+ },
async deriveRefreshSession(
+ tci: TalerCryptoInterfaceR,
req: DeriveRefreshSessionRequest,
): Promise<DerivedRefreshSession> {
const {
@@ -596,24 +807,14 @@ export class CryptoImplementation {
let coinPub: Uint8Array;
let coinPriv: Uint8Array;
let blindingFactor: Uint8Array;
- // disabled while not implemented in the C code
- if (0 && this.primitiveWorker) {
- const r = await this.primitiveWorker.setupRefreshPlanchet({
- transfer_secret: encodeCrock(transferSecret),
- coin_index: coinIndex,
- });
- coinPub = decodeCrock(r.coin_pub);
- coinPriv = decodeCrock(r.coin_priv);
- blindingFactor = decodeCrock(r.blinding_key);
- } else {
- let fresh: FreshCoin = setupRefreshPlanchet(
- transferSecret,
- coinIndex,
- );
- coinPriv = fresh.coinPriv;
- coinPub = fresh.coinPub;
- blindingFactor = fresh.bks;
- }
+ // FIXME: make setupRefreshPlanchet a crypto api fn
+ let fresh: FreshCoin = setupRefreshPlanchet(
+ transferSecret,
+ coinIndex,
+ );
+ coinPriv = fresh.coinPriv;
+ coinPub = fresh.coinPub;
+ blindingFactor = fresh.bks;
const coinPubHash = hash(coinPub);
if (denomSel.denomPub.cipher !== DenomKeyType.Rsa) {
throw Error("unsupported cipher, can't create refresh session");
@@ -654,7 +855,7 @@ export class CryptoImplementation {
.put(amountToBuffer(meltFee))
.build();
- const confirmSigResp = await myEddsaSign(this.primitiveWorker, {
+ const confirmSigResp = await tci.eddsaSign(tci, {
msg: encodeCrock(confirmData),
priv: meltCoinPriv,
});
@@ -670,102 +871,42 @@ export class CryptoImplementation {
};
return refreshSession;
- }
+ },
/**
* Hash a string including the zero terminator.
*/
- hashString(str: string): string {
- const b = stringToBytes(str + "\0");
- return encodeCrock(hash(b));
- }
-
- /**
- * Hash a crockford encoded value.
- */
- hashEncoded(encodedBytes: string): string {
- return encodeCrock(hash(decodeCrock(encodedBytes)));
- }
+ async hashString(
+ tci: TalerCryptoInterfaceR,
+ req: HashStringRequest,
+ ): Promise<HashStringResult> {
+ const b = stringToBytes(req.str + "\0");
+ return { h: encodeCrock(hash(b)) };
+ },
async signCoinLink(
- oldCoinPriv: string,
- newDenomHash: string,
- oldCoinPub: string,
- transferPub: string,
- coinEv: CoinEnvelope,
- ): Promise<string> {
- const coinEvHash = hashCoinEv(coinEv, newDenomHash);
+ tci: TalerCryptoInterfaceR,
+ req: SignCoinLinkRequest,
+ ): Promise<EddsaSigningResult> {
+ const coinEvHash = hashCoinEv(req.coinEv, req.newDenomHash);
// FIXME: fill in
const hAgeCommitment = new Uint8Array(32);
const coinLink = buildSigPS(TalerSignaturePurpose.WALLET_COIN_LINK)
- .put(decodeCrock(newDenomHash))
- .put(decodeCrock(transferPub))
+ .put(decodeCrock(req.newDenomHash))
+ .put(decodeCrock(req.transferPub))
.put(hAgeCommitment)
.put(coinEvHash)
.build();
- const sig = await myEddsaSign(this.primitiveWorker, {
+ return tci.eddsaSign(tci, {
msg: encodeCrock(coinLink),
- priv: oldCoinPriv,
+ priv: req.oldCoinPriv,
});
- return sig.sig;
- }
-
- benchmark(repetitions: number): BenchmarkResult {
- let time_hash = BigInt(0);
- for (let i = 0; i < repetitions; i++) {
- const start = timer.performanceNow();
- this.hashString("hello world");
- time_hash += timer.performanceNow() - start;
- }
+ },
- let time_hash_big = BigInt(0);
- for (let i = 0; i < repetitions; i++) {
- const ba = randomBytes(4096);
- const start = timer.performanceNow();
- hash(ba);
- time_hash_big += timer.performanceNow() - start;
- }
-
- let time_eddsa_create = BigInt(0);
- for (let i = 0; i < repetitions; i++) {
- const start = timer.performanceNow();
- createEddsaKeyPair();
- time_eddsa_create += timer.performanceNow() - start;
- }
-
- let time_eddsa_sign = BigInt(0);
- const p = randomBytes(4096);
-
- const pair = createEddsaKeyPair();
-
- for (let i = 0; i < repetitions; i++) {
- const start = timer.performanceNow();
- eddsaSign(p, pair.eddsaPriv);
- time_eddsa_sign += timer.performanceNow() - start;
- }
-
- const sig = eddsaSign(p, pair.eddsaPriv);
-
- let time_eddsa_verify = BigInt(0);
- for (let i = 0; i < repetitions; i++) {
- const start = timer.performanceNow();
- eddsaVerify(p, sig, pair.eddsaPub);
- time_eddsa_verify += timer.performanceNow() - start;
- }
-
- return {
- repetitions,
- time: {
- hash_small: Number(time_hash),
- hash_big: Number(time_hash_big),
- eddsa_create: Number(time_eddsa_create),
- eddsa_sign: Number(time_eddsa_sign),
- eddsa_verify: Number(time_eddsa_verify),
- },
- };
- }
-
- makeSyncSignature(req: MakeSyncSignatureRequest): string {
+ async makeSyncSignature(
+ tci: TalerCryptoInterfaceR,
+ req: MakeSyncSignatureRequest,
+ ): Promise<EddsaSigningResult> {
const hNew = decodeCrock(req.newHash);
let hOld: Uint8Array;
if (req.oldHash) {
@@ -778,6 +919,64 @@ export class CryptoImplementation {
.put(hNew)
.build();
const uploadSig = eddsaSign(sigBlob, decodeCrock(req.accountPriv));
- return encodeCrock(uploadSig);
+ return { sig: encodeCrock(uploadSig) };
+ },
+};
+
+function amountToBuffer(amount: AmountJson): Uint8Array {
+ const buffer = new ArrayBuffer(8 + 4 + 12);
+ const dvbuf = new DataView(buffer);
+ const u8buf = new Uint8Array(buffer);
+ const curr = stringToBytes(amount.currency);
+ if (typeof dvbuf.setBigUint64 !== "undefined") {
+ dvbuf.setBigUint64(0, BigInt(amount.value));
+ } else {
+ const arr = bigint(amount.value).toArray(2 ** 8).value;
+ let offset = 8 - arr.length;
+ for (let i = 0; i < arr.length; i++) {
+ dvbuf.setUint8(offset++, arr[i]);
+ }
+ }
+ dvbuf.setUint32(8, amount.fraction);
+ u8buf.set(curr, 8 + 4);
+
+ return u8buf;
+}
+
+function timestampRoundedToBuffer(ts: TalerProtocolTimestamp): Uint8Array {
+ const b = new ArrayBuffer(8);
+ const v = new DataView(b);
+ // The buffer we sign over represents the timestamp in microseconds.
+ if (typeof v.setBigUint64 !== "undefined") {
+ const s = BigInt(ts.t_s) * BigInt(1000 * 1000);
+ v.setBigUint64(0, s);
+ } else {
+ const s =
+ ts.t_s === "never" ? bigint.zero : bigint(ts.t_s).multiply(1000 * 1000);
+ const arr = s.toArray(2 ** 8).value;
+ let offset = 8 - arr.length;
+ for (let i = 0; i < arr.length; i++) {
+ v.setUint8(offset++, arr[i]);
+ }
}
+ return new Uint8Array(b);
+}
+
+export interface EddsaSignRequest {
+ msg: string;
+ priv: string;
}
+
+export interface EddsaSignResponse {
+ sig: string;
+}
+
+export const nativeCrypto: TalerCryptoInterface = Object.fromEntries(
+ Object.keys(nativeCryptoR).map((name) => {
+ return [
+ name,
+ (req: any) =>
+ nativeCryptoR[name as keyof TalerCryptoInterfaceR](nativeCryptoR, req),
+ ];
+ }),
+) as any;
diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
index 3b3396046..deff15071 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
@@ -29,7 +29,6 @@
*/
import {
AmountJson,
- AmountString,
CoinEnvelope,
DenominationPubKey,
ExchangeProtocolVersion,
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoDispatcher.ts
index ca498bff1..810273cca 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoDispatcher.ts
@@ -22,39 +22,10 @@
/**
* Imports.
*/
-import { DenominationRecord, WireFee } from "../../db.js";
-
-import { CryptoWorker } from "./cryptoWorkerInterface.js";
-
-import {
- BlindedDenominationSignature,
- CoinDepositPermission,
- CoinEnvelope,
- PlanchetUnblindInfo,
- RecoupRefreshRequest,
- RecoupRequest,
- UnblindedSignature,
-} from "@gnu-taler/taler-util";
-
-import {
- BenchmarkResult,
- WithdrawalPlanchet,
- PlanchetCreationRequest,
- DepositInfo,
- MakeSyncSignatureRequest,
-} from "@gnu-taler/taler-util";
-
-import * as timer from "../../util/timer.js";
import { Logger } from "@gnu-taler/taler-util";
-import {
- CreateRecoupRefreshReqRequest,
- CreateRecoupReqRequest,
- DerivedRefreshSession,
- DerivedTipPlanchet,
- DeriveRefreshSessionRequest,
- DeriveTipRequest,
- SignTrackTransactionRequest,
-} from "../cryptoTypes.js";
+import * as timer from "../../util/timer.js";
+import { nullCrypto, TalerCryptoInterface } from "../cryptoImplementation.js";
+import { CryptoWorker } from "./cryptoWorkerInterface.js";
const logger = new Logger("cryptoApi.ts");
@@ -80,7 +51,7 @@ interface WorkerState {
interface WorkItem {
operation: string;
- args: any[];
+ req: unknown;
resolve: any;
reject: any;
@@ -122,10 +93,9 @@ export class CryptoApiStoppedError extends Error {
}
/**
- * Crypto API that interfaces manages a background crypto thread
- * for the execution of expensive operations.
+ * Dispatcher for cryptographic operations to underlying crypto workers.
*/
-export class CryptoApi {
+export class CryptoDispatcher {
private nextRpcId = 1;
private workers: WorkerState[];
private workQueues: WorkItem[][];
@@ -191,7 +161,7 @@ export class CryptoApi {
}
const msg: any = {
- args: work.args,
+ req: work.req,
id: work.rpcId,
operation: work.operation,
};
@@ -277,7 +247,16 @@ export class CryptoApi {
currentWorkItem.resolve(msg.data.result);
}
+ cryptoApi: TalerCryptoInterface;
+
constructor(workerFactory: CryptoWorkerFactory) {
+ const fns: any = {};
+ for (const name of Object.keys(nullCrypto)) {
+ fns[name] = (x: any) => this.doRpc(name, 0, x);
+ }
+
+ this.cryptoApi = fns;
+
this.workerFactory = workerFactory;
this.workers = new Array<WorkerState>(workerFactory.getConcurrency());
@@ -298,7 +277,7 @@ export class CryptoApi {
private doRpc<T>(
operation: string,
priority: number,
- ...args: any[]
+ req: unknown,
): Promise<T> {
if (this.stopped) {
throw new CryptoApiStoppedError();
@@ -307,7 +286,7 @@ export class CryptoApi {
const rpcId = this.nextRpcId++;
const workItem: WorkItem = {
operation,
- args,
+ req,
resolve,
reject,
rpcId,
@@ -362,163 +341,4 @@ export class CryptoApi {
});
});
}
-
- createPlanchet(req: PlanchetCreationRequest): Promise<WithdrawalPlanchet> {
- return this.doRpc<WithdrawalPlanchet>("createPlanchet", 1, req);
- }
-
- unblindDenominationSignature(req: {
- planchet: PlanchetUnblindInfo;
- evSig: BlindedDenominationSignature;
- }): Promise<UnblindedSignature> {
- return this.doRpc<UnblindedSignature>(
- "unblindDenominationSignature",
- 1,
- req,
- );
- }
-
- createTipPlanchet(req: DeriveTipRequest): Promise<DerivedTipPlanchet> {
- return this.doRpc<DerivedTipPlanchet>("createTipPlanchet", 1, req);
- }
-
- signTrackTransaction(req: SignTrackTransactionRequest): Promise<string> {
- return this.doRpc<string>("signTrackTransaction", 1, req);
- }
-
- hashString(str: string): Promise<string> {
- return this.doRpc<string>("hashString", 1, str);
- }
-
- hashEncoded(encodedBytes: string): Promise<string> {
- return this.doRpc<string>("hashEncoded", 1, encodedBytes);
- }
-
- isValidDenom(denom: DenominationRecord, masterPub: string): Promise<boolean> {
- return this.doRpc<boolean>("isValidDenom", 2, denom, masterPub);
- }
-
- isValidWireFee(
- type: string,
- wf: WireFee,
- masterPub: string,
- ): Promise<boolean> {
- return this.doRpc<boolean>("isValidWireFee", 2, type, wf, masterPub);
- }
-
- isValidPaymentSignature(
- sig: string,
- contractHash: string,
- merchantPub: string,
- ): Promise<boolean> {
- return this.doRpc<boolean>(
- "isValidPaymentSignature",
- 1,
- sig,
- contractHash,
- merchantPub,
- );
- }
-
- signDepositPermission(
- depositInfo: DepositInfo,
- ): Promise<CoinDepositPermission> {
- return this.doRpc<CoinDepositPermission>(
- "signDepositPermission",
- 3,
- depositInfo,
- );
- }
-
- createEddsaKeypair(): Promise<{ priv: string; pub: string }> {
- return this.doRpc<{ priv: string; pub: string }>("createEddsaKeypair", 1);
- }
-
- eddsaGetPublic(key: string): Promise<{ priv: string; pub: string }> {
- return this.doRpc<{ priv: string; pub: string }>("eddsaGetPublic", 1, key);
- }
-
- rsaUnblind(sig: string, bk: string, pk: string): Promise<string> {
- return this.doRpc<string>("rsaUnblind", 4, sig, bk, pk);
- }
-
- rsaVerify(hm: string, sig: string, pk: string): Promise<boolean> {
- return this.doRpc<boolean>("rsaVerify", 4, hm, sig, pk);
- }
-
- isValidWireAccount(
- versionCurrent: number,
- paytoUri: string,
- sig: string,
- masterPub: string,
- ): Promise<boolean> {
- return this.doRpc<boolean>(
- "isValidWireAccount",
- 4,
- versionCurrent,
- paytoUri,
- sig,
- masterPub,
- );
- }
-
- isValidContractTermsSignature(
- contractTermsHash: string,
- sig: string,
- merchantPub: string,
- ): Promise<boolean> {
- return this.doRpc<boolean>(
- "isValidContractTermsSignature",
- 4,
- contractTermsHash,
- sig,
- merchantPub,
- );
- }
-
- createRecoupRequest(req: CreateRecoupReqRequest): Promise<RecoupRequest> {
- return this.doRpc<RecoupRequest>("createRecoupRequest", 1, req);
- }
-
- createRecoupRefreshRequest(
- req: CreateRecoupRefreshReqRequest,
- ): Promise<RecoupRefreshRequest> {
- return this.doRpc<RecoupRefreshRequest>(
- "createRecoupRefreshRequest",
- 1,
- req,
- );
- }
-
- deriveRefreshSession(
- req: DeriveRefreshSessionRequest,
- ): Promise<DerivedRefreshSession> {
- return this.doRpc<DerivedRefreshSession>("deriveRefreshSession", 4, req);
- }
-
- signCoinLink(
- oldCoinPriv: string,
- newDenomHash: string,
- oldCoinPub: string,
- transferPub: string,
- coinEv: CoinEnvelope,
- ): Promise<string> {
- return this.doRpc<string>(
- "signCoinLink",
- 4,
- oldCoinPriv,
- newDenomHash,
- oldCoinPub,
- transferPub,
- coinEv,
- );
- }
-
- benchmark(repetitions: number): Promise<BenchmarkResult> {
- return this.doRpc<BenchmarkResult>("benchmark", 1, repetitions);
- }
-
- makeSyncSignature(req: MakeSyncSignatureRequest): Promise<string> {
- return this.doRpc<string>("makeSyncSignature", 3, req);
- }
}
diff --git a/packages/taler-wallet-core/src/crypto/workers/nodeThreadWorker.ts b/packages/taler-wallet-core/src/crypto/workers/nodeThreadWorker.ts
index df57635d1..42370fc1b 100644
--- a/packages/taler-wallet-core/src/crypto/workers/nodeThreadWorker.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/nodeThreadWorker.ts
@@ -17,11 +17,11 @@
/**
* Imports
*/
-import { CryptoWorkerFactory } from "./cryptoApi.js";
+import { CryptoWorkerFactory } from "./cryptoDispatcher.js";
import { CryptoWorker } from "./cryptoWorkerInterface.js";
import os from "os";
-import { CryptoImplementation } from "./cryptoImplementation.js";
import { Logger } from "@gnu-taler/taler-util";
+import { nativeCryptoR } from "../cryptoImplementation.js";
const logger = new Logger("nodeThreadWorker.ts");
@@ -69,9 +69,9 @@ const workerCode = `
* a message.
*/
export function handleWorkerMessage(msg: any): void {
- const args = msg.args;
- if (!Array.isArray(args)) {
- console.error("args must be array");
+ const req = msg.req;
+ if (typeof req !== "object") {
+ console.error("request must be an object");
return;
}
const id = msg.id;
@@ -86,7 +86,7 @@ export function handleWorkerMessage(msg: any): void {
}
const handleRequest = async (): Promise<void> => {
- const impl = new CryptoImplementation();
+ const impl = nativeCryptoR;
if (!(operation in impl)) {
console.error(`crypto operation '${operation}' not found`);
@@ -94,12 +94,11 @@ export function handleWorkerMessage(msg: any): void {
}
try {
- const result = await (impl as any)[operation](...args);
+ const result = await (impl as any)[operation](impl, req);
// eslint-disable-next-line @typescript-eslint/no-var-requires
const _r = "require";
- const worker_threads: typeof import("worker_threads") = module[_r](
- "worker_threads",
- );
+ const worker_threads: typeof import("worker_threads") =
+ module[_r]("worker_threads");
// const worker_threads = require("worker_threads");
const p = worker_threads.parentPort;
diff --git a/packages/taler-wallet-core/src/crypto/workers/rpcClient.ts b/packages/taler-wallet-core/src/crypto/workers/rpcClient.ts
new file mode 100644
index 000000000..a8df8b4c6
--- /dev/null
+++ b/packages/taler-wallet-core/src/crypto/workers/rpcClient.ts
@@ -0,0 +1,90 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import { Logger } from "@gnu-taler/taler-util";
+import child_process from "child_process";
+import type internal from "stream";
+import { OpenedPromise, openPromise } from "../../util/promiseUtils.js";
+
+const logger = new Logger("synchronousWorkerFactory.ts");
+
+export class CryptoRpcClient {
+ proc: child_process.ChildProcessByStdio<
+ internal.Writable,
+ internal.Readable,
+ null
+ >;
+ requests: Array<{
+ p: OpenedPromise<any>;
+ req: any;
+ }> = [];
+
+ constructor() {
+ const stdoutChunks: Buffer[] = [];
+ this.proc = child_process.spawn("taler-crypto-worker", {
+ //stdio: ["pipe", "pipe", "inherit"],
+ stdio: ["pipe", "pipe", "inherit"],
+ detached: true,
+ });
+ this.proc.on("close", (): void => {
+ logger.error("child process exited");
+ });
+ (this.proc.stdout as any).unref();
+ (this.proc.stdin as any).unref();
+ this.proc.unref();
+
+ this.proc.stdout.on("data", (x) => {
+ // console.log("got chunk", x.toString("utf-8"));
+ if (x instanceof Buffer) {
+ const nlIndex = x.indexOf("\n");
+ if (nlIndex >= 0) {
+ const before = x.slice(0, nlIndex);
+ const after = x.slice(nlIndex + 1);
+ stdoutChunks.push(after);
+ const str = Buffer.concat([...stdoutChunks, before]).toString(
+ "utf-8",
+ );
+ const req = this.requests.shift();
+ if (!req) {
+ throw Error("request was undefined");
+ }
+ if (this.requests.length === 0) {
+ this.proc.unref();
+ }
+ //logger.info(`got response: ${str}`);
+ req.p.resolve(JSON.parse(str));
+ } else {
+ stdoutChunks.push(x);
+ }
+ } else {
+ throw Error(`unexpected data chunk type (${typeof x})`);
+ }
+ });
+ }
+
+ async queueRequest(req: any): Promise<any> {
+ const p = openPromise<any>();
+ if (this.requests.length === 0) {
+ this.proc.ref();
+ }
+ this.requests.push({ req, p });
+ this.proc.stdin.write(`${JSON.stringify(req)}\n`);
+ return p.promise;
+ }
+}
diff --git a/packages/taler-wallet-core/src/crypto/workers/synchronousWorker.ts b/packages/taler-wallet-core/src/crypto/workers/synchronousWorker.ts
index 4d341718e..1d7539ed6 100644
--- a/packages/taler-wallet-core/src/crypto/workers/synchronousWorker.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/synchronousWorker.ts
@@ -16,11 +16,10 @@
import { Logger } from "@gnu-taler/taler-util";
import {
- CryptoImplementation,
- PrimitiveWorker
-} from "./cryptoImplementation.js";
-
-
+ nativeCryptoR,
+ TalerCryptoInterfaceR,
+} from "../cryptoImplementation.js";
+import { CryptoRpcClient } from "./rpcClient.js";
const logger = new Logger("synchronousWorker.ts");
@@ -38,9 +37,33 @@ export class SynchronousCryptoWorker {
*/
onerror: undefined | ((m: any) => void);
- constructor(private primitiveWorker?: PrimitiveWorker) {
+ cryptoImplR: TalerCryptoInterfaceR;
+
+ rpcClient: CryptoRpcClient | undefined;
+
+ constructor() {
this.onerror = undefined;
this.onmessage = undefined;
+
+ this.cryptoImplR = { ...nativeCryptoR };
+
+ if (
+ process.env["TALER_WALLET_RPC_CRYPRO"] ||
+ // Old name
+ process.env["TALER_WALLET_PRIMITIVE_WORKER"]
+ ) {
+ const rpc = (this.rpcClient = new CryptoRpcClient());
+ this.cryptoImplR.eddsaSign = async (_, req) => {
+ logger.trace("making RPC request");
+ return await rpc.queueRequest({
+ op: "eddsa_sign",
+ args: {
+ msg: req.msg,
+ priv: req.priv,
+ },
+ });
+ };
+ }
}
/**
@@ -66,9 +89,9 @@ export class SynchronousCryptoWorker {
private async handleRequest(
operation: string,
id: number,
- args: string[],
+ req: unknown,
): Promise<void> {
- const impl = new CryptoImplementation(this.primitiveWorker);
+ const impl = this.cryptoImplR;
if (!(operation in impl)) {
console.error(`crypto operation '${operation}' not found`);
@@ -77,7 +100,7 @@ export class SynchronousCryptoWorker {
let result: any;
try {
- result = await (impl as any)[operation](...args);
+ result = await (impl as any)[operation](impl, req);
} catch (e) {
logger.error("error during operation", e);
return;
@@ -94,9 +117,9 @@ export class SynchronousCryptoWorker {
* Send a message to the worker thread.
*/
postMessage(msg: any): void {
- const args = msg.args;
- if (!Array.isArray(args)) {
- console.error("args must be array");
+ const req = msg.req;
+ if (typeof req !== "object") {
+ console.error("request must be an object");
return;
}
const id = msg.id;
@@ -110,7 +133,7 @@ export class SynchronousCryptoWorker {
return;
}
- this.handleRequest(operation, id, args).catch((e) => {
+ this.handleRequest(operation, id, req).catch((e) => {
console.error("Error while handling crypto request:", e);
});
}
diff --git a/packages/taler-wallet-core/src/crypto/workers/synchronousWorkerFactory.ts b/packages/taler-wallet-core/src/crypto/workers/synchronousWorkerFactory.ts
index ca63c7687..47f58be13 100644
--- a/packages/taler-wallet-core/src/crypto/workers/synchronousWorkerFactory.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/synchronousWorkerFactory.ts
@@ -14,121 +14,13 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import {
- PrimitiveWorker,
-} from "./cryptoImplementation.js";
-
-import { CryptoWorkerFactory } from "./cryptoApi.js";
+/**
+ * Imports.
+ */
+import { CryptoWorkerFactory } from "./cryptoDispatcher.js";
import { CryptoWorker } from "./cryptoWorkerInterface.js";
-
-import child_process from "child_process";
-import type internal from "stream";
-import { OpenedPromise, openPromise } from "../../index.js";
-import { Logger } from "@gnu-taler/taler-util";
import { SynchronousCryptoWorker } from "./synchronousWorker.js";
-const logger = new Logger("synchronousWorkerFactory.ts");
-
-class MyPrimitiveWorker implements PrimitiveWorker {
- proc: child_process.ChildProcessByStdio<
- internal.Writable,
- internal.Readable,
- null
- >;
- requests: Array<{
- p: OpenedPromise<any>;
- req: any;
- }> = [];
-
- constructor() {
- const stdoutChunks: Buffer[] = [];
- this.proc = child_process.spawn("taler-crypto-worker", {
- //stdio: ["pipe", "pipe", "inherit"],
- stdio: ["pipe", "pipe", "inherit"],
- detached: true,
- });
- this.proc.on("close", (): void => {
- logger.error("child process exited");
- });
- (this.proc.stdout as any).unref();
- (this.proc.stdin as any).unref();
- this.proc.unref();
-
- this.proc.stdout.on("data", (x) => {
- // console.log("got chunk", x.toString("utf-8"));
- if (x instanceof Buffer) {
- const nlIndex = x.indexOf("\n");
- if (nlIndex >= 0) {
- const before = x.slice(0, nlIndex);
- const after = x.slice(nlIndex + 1);
- stdoutChunks.push(after);
- const str = Buffer.concat([...stdoutChunks, before]).toString(
- "utf-8",
- );
- const req = this.requests.shift();
- if (!req) {
- throw Error("request was undefined")
- }
- if (this.requests.length === 0) {
- this.proc.unref();
- }
- //logger.info(`got response: ${str}`);
- req.p.resolve(JSON.parse(str));
- } else {
- stdoutChunks.push(x);
- }
- } else {
- throw Error(`unexpected data chunk type (${typeof x})`);
- }
- });
- }
-
- async setupRefreshPlanchet(req: {
- transfer_secret: string;
- coin_index: number;
- }): Promise<{
- coin_pub: string;
- coin_priv: string;
- blinding_key: string;
- }> {
- return this.queueRequest({
- op: "setup_refresh_planchet",
- args: req,
- });
- }
-
- async queueRequest(req: any): Promise<any> {
- const p = openPromise<any>();
- if (this.requests.length === 0) {
- this.proc.ref();
- }
- this.requests.push({ req, p });
- this.proc.stdin.write(`${JSON.stringify(req)}\n`);
- return p.promise;
- }
-
- async eddsaVerify(req: {
- msg: string;
- sig: string;
- pub: string;
- }): Promise<{ valid: boolean }> {
- return this.queueRequest({
- op: "eddsa_verify",
- args: req,
- });
- }
-
- async eddsaSign(req: {
- msg: string;
- priv: string;
- }): Promise<{ sig: string }> {
- return this.queueRequest({
- op: "eddsa_sign",
- args: req,
- });
- }
-}
-
/**
* The synchronous crypto worker produced by this factory doesn't run in the
* background, but actually blocks the caller until the operation is done.
@@ -139,12 +31,7 @@ export class SynchronousCryptoWorkerFactory implements CryptoWorkerFactory {
throw Error("cannot make worker, require(...) not defined");
}
- let primitiveWorker;
- if (process.env["TALER_WALLET_PRIMITIVE_WORKER"]) {
- primitiveWorker = new MyPrimitiveWorker();
- }
-
- return new SynchronousCryptoWorker(primitiveWorker);
+ return new SynchronousCryptoWorker();
}
getConcurrency(): number {
diff --git a/packages/taler-wallet-core/src/dbless.ts b/packages/taler-wallet-core/src/dbless.ts
index 854a3ea09..ea5a42323 100644
--- a/packages/taler-wallet-core/src/dbless.ts
+++ b/packages/taler-wallet-core/src/dbless.ts
@@ -47,10 +47,10 @@ import {
AbsoluteTime,
UnblindedSignature,
} from "@gnu-taler/taler-util";
+import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
import { DenominationRecord } from "./db.js";
import {
assembleRefreshRevealRequest,
- CryptoApi,
ExchangeInfo,
getBankWithdrawalInfo,
HttpRequestLibrary,
@@ -149,7 +149,7 @@ export async function topupReserveWithDemobank(
export async function withdrawCoin(args: {
http: HttpRequestLibrary;
- cryptoApi: CryptoApi;
+ cryptoApi: TalerCryptoInterface;
reserveKeyPair: ReserveKeypair;
denom: DenominationRecord;
exchangeBaseUrl: string;
@@ -212,7 +212,7 @@ export function findDenomOrThrow(
export async function depositCoin(args: {
http: HttpRequestLibrary;
- cryptoApi: CryptoApi;
+ cryptoApi: TalerCryptoInterface;
exchangeBaseUrl: string;
coin: CoinInfo;
amount: AmountString;
@@ -263,7 +263,7 @@ export async function depositCoin(args: {
export async function refreshCoin(req: {
http: HttpRequestLibrary;
- cryptoApi: CryptoApi;
+ cryptoApi: TalerCryptoInterface;
oldCoin: CoinInfo;
newDenoms: DenominationRecord[];
}): Promise<void> {
diff --git a/packages/taler-wallet-core/src/headless/helpers.ts b/packages/taler-wallet-core/src/headless/helpers.ts
index 120c4cd46..7bc8235fd 100644
--- a/packages/taler-wallet-core/src/headless/helpers.ts
+++ b/packages/taler-wallet-core/src/headless/helpers.ts
@@ -25,7 +25,9 @@
import type { IDBFactory } from "@gnu-taler/idb-bridge";
// eslint-disable-next-line no-duplicate-imports
import {
- BridgeIDBFactory, MemoryBackend, shimIndexedDB
+ BridgeIDBFactory,
+ MemoryBackend,
+ shimIndexedDB,
} from "@gnu-taler/idb-bridge";
import { AccessStats } from "@gnu-taler/idb-bridge/src/MemoryBackend";
import { Logger, WalletNotification } from "@gnu-taler/taler-util";
diff --git a/packages/taler-wallet-core/src/index.ts b/packages/taler-wallet-core/src/index.ts
index ca701820e..979d631c0 100644
--- a/packages/taler-wallet-core/src/index.ts
+++ b/packages/taler-wallet-core/src/index.ts
@@ -33,9 +33,11 @@ export * from "./db-utils.js";
// Crypto and crypto workers
// export * from "./crypto/workers/nodeThreadWorker.js";
-export { CryptoImplementation } from "./crypto/workers/cryptoImplementation.js";
export type { CryptoWorker } from "./crypto/workers/cryptoWorkerInterface.js";
-export { CryptoWorkerFactory, CryptoApi } from "./crypto/workers/cryptoApi.js";
+export {
+ CryptoWorkerFactory,
+ CryptoDispatcher,
+} from "./crypto/workers/cryptoDispatcher.js";
export { SynchronousCryptoWorker } from "./crypto/workers/synchronousWorker.js";
export * from "./pending-types.js";
@@ -58,3 +60,8 @@ export * from "./operations/refresh.js";
export * from "./dbless.js";
+export {
+ nativeCryptoR,
+ nativeCrypto,
+ nullCrypto,
+} from "./crypto/cryptoImplementation.js";
diff --git a/packages/taler-wallet-core/src/internal-wallet-state.ts b/packages/taler-wallet-core/src/internal-wallet-state.ts
index 904398722..5ecf796ed 100644
--- a/packages/taler-wallet-core/src/internal-wallet-state.ts
+++ b/packages/taler-wallet-core/src/internal-wallet-state.ts
@@ -36,7 +36,8 @@ import {
DenominationPubKey,
TalerProtocolTimestamp,
} from "@gnu-taler/taler-util";
-import { CryptoApi } from "./crypto/workers/cryptoApi.js";
+import { CryptoDispatcher } from "./crypto/workers/cryptoDispatcher.js";
+import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
import { ExchangeDetailsRecord, ExchangeRecord, WalletStoresV1 } from "./db.js";
import { PendingOperationsResponse } from "./pending-types.js";
import { AsyncOpMemoMap, AsyncOpMemoSingle } from "./util/asyncMemo.js";
@@ -200,7 +201,7 @@ export interface InternalWalletState {
memoProcessRefresh: AsyncOpMemoMap<void>;
memoProcessRecoup: AsyncOpMemoMap<void>;
memoProcessDeposit: AsyncOpMemoMap<void>;
- cryptoApi: CryptoApi;
+ cryptoApi: TalerCryptoInterface;
timerGroup: TimerGroup;
stopped: boolean;
diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts
index 8ddc0c064..5013b9032 100644
--- a/packages/taler-wallet-core/src/operations/backup/index.ts
+++ b/packages/taler-wallet-core/src/operations/backup/index.ts
@@ -69,7 +69,7 @@ import {
rsaBlind,
stringToBytes,
} from "@gnu-taler/taler-util";
-import { CryptoApi } from "../../crypto/workers/cryptoApi.js";
+import { CryptoDispatcher } from "../../crypto/workers/cryptoDispatcher.js";
import {
BackupProviderRecord,
BackupProviderState,
@@ -99,6 +99,7 @@ import { exportBackup } from "./export.js";
import { BackupCryptoPrecomputedData, importBackup } from "./import.js";
import { getWalletBackupState, provideBackupState } from "./state.js";
import { guardOperationException } from "../common.js";
+import { TalerCryptoInterface } from "../../crypto/cryptoImplementation.js";
const logger = new Logger("operations/backup.ts");
@@ -154,7 +155,7 @@ export async function encryptBackup(
* FIXME: Move computations into crypto worker.
*/
async function computeBackupCryptoData(
- cryptoApi: CryptoApi,
+ cryptoApi: TalerCryptoInterface,
backupContent: WalletBackupContentV1,
): Promise<BackupCryptoPrecomputedData> {
const cryptoData: BackupCryptoPrecomputedData = {
@@ -193,18 +194,18 @@ async function computeBackupCryptoData(
}
}
for (const prop of backupContent.proposals) {
- const contractTermsHash = await cryptoApi.hashString(
- canonicalJson(prop.contract_terms_raw),
- );
+ const { h: contractTermsHash } = await cryptoApi.hashString({
+ str: canonicalJson(prop.contract_terms_raw),
+ });
const noncePub = encodeCrock(eddsaGetPublic(decodeCrock(prop.nonce_priv)));
cryptoData.proposalNoncePrivToPub[prop.nonce_priv] = noncePub;
cryptoData.proposalIdToContractTermsHash[prop.proposal_id] =
contractTermsHash;
}
for (const purch of backupContent.purchases) {
- const contractTermsHash = await cryptoApi.hashString(
- canonicalJson(purch.contract_terms_raw),
- );
+ const { h: contractTermsHash } = await cryptoApi.hashString({
+ str: canonicalJson(purch.contract_terms_raw),
+ });
const noncePub = encodeCrock(eddsaGetPublic(decodeCrock(purch.nonce_priv)));
cryptoData.proposalNoncePrivToPub[purch.nonce_priv] = noncePub;
cryptoData.proposalIdToContractTermsHash[purch.proposal_id] =
@@ -286,13 +287,13 @@ async function runBackupCycleForProvider(
logger.trace(`trying to upload backup to ${provider.baseUrl}`);
logger.trace(`old hash ${oldHash}, new hash ${newHash}`);
- const syncSig = await ws.cryptoApi.makeSyncSignature({
+ const syncSigResp = await ws.cryptoApi.makeSyncSignature({
newHash: encodeCrock(currentBackupHash),
oldHash: provider.lastBackupHash,
accountPriv: encodeCrock(accountKeyPair.eddsaPriv),
});
- logger.trace(`sync signature is ${syncSig}`);
+ logger.trace(`sync signature is ${syncSigResp}`);
const accountBackupUrl = new URL(
`/backups/${encodeCrock(accountKeyPair.eddsaPub)}`,
@@ -304,7 +305,7 @@ async function runBackupCycleForProvider(
body: encBackup,
headers: {
"content-type": "application/octet-stream",
- "sync-signature": syncSig,
+ "sync-signature": syncSigResp.sig,
"if-none-match": newHash,
...(provider.lastBackupHash
? {
diff --git a/packages/taler-wallet-core/src/operations/backup/state.ts b/packages/taler-wallet-core/src/operations/backup/state.ts
index f25cc170a..293f56137 100644
--- a/packages/taler-wallet-core/src/operations/backup/state.ts
+++ b/packages/taler-wallet-core/src/operations/backup/state.ts
@@ -41,7 +41,7 @@ export async function provideBackupState(
}
// We need to generate the key outside of the transaction
// due to how IndexedDB works.
- const k = await ws.cryptoApi.createEddsaKeypair();
+ const k = await ws.cryptoApi.createEddsaKeypair({});
const d = getRandomBytes(5);
// FIXME: device ID should be configured when wallet is initialized
// and be based on hostname
diff --git a/packages/taler-wallet-core/src/operations/common.ts b/packages/taler-wallet-core/src/operations/common.ts
index 5525b4deb..5261b114d 100644
--- a/packages/taler-wallet-core/src/operations/common.ts
+++ b/packages/taler-wallet-core/src/operations/common.ts
@@ -15,7 +15,7 @@
*/
import { TalerErrorDetail, TalerErrorCode } from "@gnu-taler/taler-util";
-import { CryptoApiStoppedError } from "../crypto/workers/cryptoApi.js";
+import { CryptoApiStoppedError } from "../crypto/workers/cryptoDispatcher.js";
import { TalerError, getErrorDetailFromException } from "../errors.js";
/**
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts
index ad3f614f3..2e14afdf1 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -254,14 +254,14 @@ export async function trackDepositGroup(
`deposits/${wireHash}/${depositGroup.merchantPub}/${depositGroup.contractTermsHash}/${dp.coin_pub}`,
dp.exchange_url,
);
- const sig = await ws.cryptoApi.signTrackTransaction({
+ const sigResp = await ws.cryptoApi.signTrackTransaction({
coinPub: dp.coin_pub,
contractTermsHash: depositGroup.contractTermsHash,
merchantPriv: depositGroup.merchantPriv,
merchantPub: depositGroup.merchantPub,
wireHash,
});
- url.searchParams.set("merchant_sig", sig);
+ url.searchParams.set("merchant_sig", sigResp.sig);
const httpResp = await ws.http.get(url.href);
const body = await httpResp.json();
responses.push({
@@ -391,8 +391,8 @@ export async function createDepositGroup(
const now = AbsoluteTime.now();
const nowRounded = AbsoluteTime.toTimestamp(now);
- const noncePair = await ws.cryptoApi.createEddsaKeypair();
- const merchantPair = await ws.cryptoApi.createEddsaKeypair();
+ const noncePair = await ws.cryptoApi.createEddsaKeypair({});
+ const merchantPair = await ws.cryptoApi.createEddsaKeypair({});
const wireSalt = encodeCrock(getRandomBytes(16));
const wireHash = hashWire(req.depositPaytoUri, wireSalt);
const contractTerms: ContractTerms = {
@@ -421,9 +421,9 @@ export async function createDepositGroup(
refund_deadline: TalerProtocolTimestamp.zero(),
};
- const contractTermsHash = await ws.cryptoApi.hashString(
- canonicalJson(contractTerms),
- );
+ const { h: contractTermsHash } = await ws.cryptoApi.hashString({
+ str: canonicalJson(contractTerms),
+ });
const contractData = extractContractData(
contractTerms,
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts
index 94159369b..51b5c7806 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -46,7 +46,7 @@ import {
TalerProtocolDuration,
} from "@gnu-taler/taler-util";
import { decodeCrock, encodeCrock, hash } from "@gnu-taler/taler-util";
-import { CryptoApi } from "../crypto/workers/cryptoApi.js";
+import { CryptoDispatcher } from "../crypto/workers/cryptoDispatcher.js";
import {
DenominationRecord,
DenominationVerificationStatus,
@@ -243,12 +243,13 @@ async function validateWireInfo(
if (ws.insecureTrustExchange) {
isValid = true;
} else {
- isValid = await ws.cryptoApi.isValidWireAccount(
+ const { valid: v } = await ws.cryptoApi.isValidWireAccount({
+ masterPub: masterPublicKey,
+ paytoUri: a.payto_uri,
+ sig: a.master_sig,
versionCurrent,
- a.payto_uri,
- a.master_sig,
- masterPublicKey,
- );
+ });
+ isValid = v;
}
if (!isValid) {
throw Error("exchange acct signature invalid");
@@ -272,11 +273,12 @@ async function validateWireInfo(
if (ws.insecureTrustExchange) {
isValid = true;
} else {
- isValid = await ws.cryptoApi.isValidWireFee(
- wireMethod,
- fee,
- masterPublicKey,
- );
+ const { valid: v } = await ws.cryptoApi.isValidWireFee({
+ masterPub: masterPublicKey,
+ type: wireMethod,
+ wf: fee,
+ });
+ isValid = v;
}
if (!isValid) {
throw Error("exchange wire fee signature invalid");
diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts
index 1c1a0f506..97f38bae6 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -55,7 +55,10 @@ import {
TransactionType,
URL,
} from "@gnu-taler/taler-util";
-import { EXCHANGE_COINS_LOCK, InternalWalletState } from "../internal-wallet-state.js";
+import {
+ EXCHANGE_COINS_LOCK,
+ InternalWalletState,
+} from "../internal-wallet-state.js";
import {
AbortStatus,
AllowedAuditorInfo,
@@ -100,6 +103,7 @@ import {
import { getExchangeDetails } from "./exchanges.js";
import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js";
import { guardOperationException } from "./common.js";
+import { EddsaKeypair } from "../crypto/cryptoImplementation.js";
/**
* Logger.
@@ -795,11 +799,11 @@ async function processDownloadProposalImpl(
);
}
- const sigValid = await ws.cryptoApi.isValidContractTermsSignature(
+ const sigValid = await ws.cryptoApi.isValidContractTermsSignature({
contractTermsHash,
- proposalResp.sig,
- parsedContractTerms.merchant_pub,
- );
+ merchantPub: parsedContractTerms.merchant_pub,
+ sig: proposalResp.sig,
+ });
if (!sigValid) {
const err = makeErrorDetail(
@@ -921,9 +925,14 @@ async function startDownloadProposal(
return oldProposal.proposalId;
}
- const { priv, pub } = await (noncePriv
- ? ws.cryptoApi.eddsaGetPublic(noncePriv)
- : ws.cryptoApi.createEddsaKeypair());
+ let noncePair: EddsaKeypair;
+ if (noncePriv) {
+ noncePair = await ws.cryptoApi.eddsaGetPublic({ priv: noncePriv });
+ } else {
+ noncePair = await ws.cryptoApi.createEddsaKeypair({});
+ }
+
+ const { priv, pub } = noncePair;
const proposalId = encodeCrock(getRandomBytes(32));
const proposalRecord: ProposalRecord = {
@@ -1673,11 +1682,11 @@ async function processPurchasePayImpl(
logger.trace("got success from pay URL", merchantResp);
const merchantPub = purchase.download.contractData.merchantPub;
- const valid: boolean = await ws.cryptoApi.isValidPaymentSignature(
- merchantResp.sig,
- purchase.download.contractData.contractTermsHash,
+ const { valid } = await ws.cryptoApi.isValidPaymentSignature({
+ contractHash: purchase.download.contractData.contractTermsHash,
merchantPub,
- );
+ sig: merchantResp.sig,
+ });
if (!valid) {
logger.error("merchant payment signature invalid");
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts
index c422674a9..a77738262 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -76,9 +76,9 @@ import {
RefreshNewDenomInfo,
} from "../crypto/cryptoTypes.js";
import { GetReadWriteAccess } from "../util/query.js";
-import { CryptoApi } from "../index.browser.js";
import { guardOperationException } from "./common.js";
-import { CryptoApiStoppedError } from "../crypto/workers/cryptoApi.js";
+import { CryptoApiStoppedError } from "../crypto/workers/cryptoDispatcher.js";
+import { TalerCryptoInterface } from "../crypto/cryptoImplementation.js";
const logger = new Logger("refresh.ts");
@@ -461,7 +461,7 @@ async function refreshMelt(
}
export async function assembleRefreshRevealRequest(args: {
- cryptoApi: CryptoApi;
+ cryptoApi: TalerCryptoInterface;
derived: DerivedRefreshSession;
norevealIndex: number;
oldCoinPub: CoinPublicKeyString;
@@ -494,14 +494,14 @@ export async function assembleRefreshRevealRequest(args: {
const dsel = newDenoms[i];
for (let j = 0; j < dsel.count; j++) {
const newCoinIndex = linkSigs.length;
- const linkSig = await cryptoApi.signCoinLink(
- oldCoinPriv,
- dsel.denomPubHash,
- oldCoinPub,
- derived.transferPubs[norevealIndex],
- planchets[newCoinIndex].coinEv,
- );
- linkSigs.push(linkSig);
+ const linkSig = await cryptoApi.signCoinLink({
+ coinEv: planchets[newCoinIndex].coinEv,
+ newDenomHash: dsel.denomPubHash,
+ oldCoinPriv: oldCoinPriv,
+ oldCoinPub: oldCoinPub,
+ transferPub: derived.transferPubs[norevealIndex],
+ });
+ linkSigs.push(linkSig.sig);
newDenomsFlat.push(dsel.denomPubHash);
}
}
diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts
index dd0fa5423..9cbd63c45 100644
--- a/packages/taler-wallet-core/src/operations/reserves.ts
+++ b/packages/taler-wallet-core/src/operations/reserves.ts
@@ -170,7 +170,7 @@ export async function createReserve(
ws: InternalWalletState,
req: CreateReserveRequest,
): Promise<CreateReserveResponse> {
- const keypair = await ws.cryptoApi.createEddsaKeypair();
+ const keypair = await ws.cryptoApi.createEddsaKeypair({});
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
const canonExchange = canonicalizeBaseUrl(req.exchange);
diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts
index 7bd81b825..cd29f8a86 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -336,17 +336,17 @@ async function processTipImpl(
throw Error("unsupported cipher");
}
- const denomSigRsa = await ws.cryptoApi.rsaUnblind(
- blindedSig.blinded_rsa_signature,
- planchet.blindingKey,
- denom.denomPub.rsa_public_key,
- );
+ const denomSigRsa = await ws.cryptoApi.rsaUnblind({
+ bk: planchet.blindingKey,
+ blindedSig: blindedSig.blinded_rsa_signature,
+ pk: denom.denomPub.rsa_public_key,
+ });
- const isValid = await ws.cryptoApi.rsaVerify(
- planchet.coinPub,
- denomSigRsa,
- denom.denomPub.rsa_public_key,
- );
+ const isValid = await ws.cryptoApi.rsaVerify({
+ hm: planchet.coinPub,
+ pk: denom.denomPub.rsa_public_key,
+ sig: denomSigRsa.sig,
+ });
if (!isValid) {
await ws.db
@@ -377,7 +377,7 @@ async function processTipImpl(
},
currentAmount: denom.value,
denomPubHash: denom.denomPubHash,
- denomSig: { cipher: DenomKeyType.Rsa, rsa_signature: denomSigRsa },
+ denomSig: { cipher: DenomKeyType.Rsa, rsa_signature: denomSigRsa.sig },
exchangeBaseUrl: tipRecord.exchangeBaseUrl,
status: CoinStatus.Fresh,
suspended: false,
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index b7feae06a..7685ede73 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -603,17 +603,17 @@ async function processPlanchetVerifyAndStoreCoin(
throw Error("unsupported cipher");
}
- const denomSigRsa = await ws.cryptoApi.rsaUnblind(
- evSig.blinded_rsa_signature,
- planchet.blindingKey,
- planchetDenomPub.rsa_public_key,
- );
+ const denomSigRsa = await ws.cryptoApi.rsaUnblind({
+ bk: planchet.blindingKey,
+ blindedSig: evSig.blinded_rsa_signature,
+ pk: planchetDenomPub.rsa_public_key,
+ });
- const isValid = await ws.cryptoApi.rsaVerify(
- planchet.coinPub,
- denomSigRsa,
- planchetDenomPub.rsa_public_key,
- );
+ const isValid = await ws.cryptoApi.rsaVerify({
+ hm: planchet.coinPub,
+ pk: planchetDenomPub.rsa_public_key,
+ sig: denomSigRsa.sig,
+ });
if (!isValid) {
await ws.db
@@ -640,7 +640,7 @@ async function processPlanchetVerifyAndStoreCoin(
if (planchetDenomPub.cipher === DenomKeyType.Rsa) {
denomSig = {
cipher: planchetDenomPub.cipher,
- rsa_signature: denomSigRsa,
+ rsa_signature: denomSigRsa.sig,
};
} else {
throw Error("unsupported cipher");
@@ -759,10 +759,11 @@ export async function updateWithdrawalDenoms(
if (ws.insecureTrustExchange) {
valid = true;
} else {
- valid = await ws.cryptoApi.isValidDenom(
+ const res = await ws.cryptoApi.isValidDenom({
denom,
- exchangeDetails.masterPublicKey,
- );
+ masterPub: exchangeDetails.masterPublicKey,
+ });
+ valid = res.valid;
}
logger.trace(`Done validating ${denom.denomPubHash}`);
if (!valid) {
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index 5e71cfbc7..d1e9aa646 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -90,7 +90,10 @@ import {
RecoupOperations,
ReserveOperations,
} from "./internal-wallet-state.js";
-import { CryptoApi, CryptoWorkerFactory } from "./crypto/workers/cryptoApi.js";
+import {
+ CryptoDispatcher,
+ CryptoWorkerFactory,
+} from "./crypto/workers/cryptoDispatcher.js";
import {
AuditorTrustRecord,
CoinSourceType,
@@ -99,10 +102,7 @@ import {
ReserveRecordStatus,
WalletStoresV1,
} from "./db.js";
-import {
- getErrorDetailFromException,
- TalerError,
-} from "./errors.js";
+import { getErrorDetailFromException, TalerError } from "./errors.js";
import { exportBackup } from "./operations/backup/export.js";
import {
addBackupProvider,
@@ -193,6 +193,10 @@ import {
import { DbAccess, GetReadWriteAccess } from "./util/query.js";
import { TimerGroup } from "./util/timer.js";
import { WalletCoreApiClient } from "./wallet-api-types.js";
+import {
+ TalerCryptoInterface,
+ TalerCryptoInterfaceR,
+} from "./crypto/cryptoImplementation.js";
const builtinAuditors: AuditorTrustRecord[] = [
{
@@ -1158,7 +1162,8 @@ class InternalWalletStateImpl implements InternalWalletState {
memoProcessRefresh: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
memoProcessRecoup: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
memoProcessDeposit: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
- cryptoApi: CryptoApi;
+ cryptoApi: TalerCryptoInterface;
+ cryptoDispatcher: CryptoDispatcher;
merchantInfoCache: Record<string, MerchantInfo> = {};
@@ -1213,7 +1218,8 @@ class InternalWalletStateImpl implements InternalWalletState {
public http: HttpRequestLibrary,
cryptoWorkerFactory: CryptoWorkerFactory,
) {
- this.cryptoApi = new CryptoApi(cryptoWorkerFactory);
+ this.cryptoDispatcher = new CryptoDispatcher(cryptoWorkerFactory);
+ this.cryptoApi = this.cryptoDispatcher.cryptoApi;
}
async getDenomInfo(
@@ -1258,7 +1264,7 @@ class InternalWalletStateImpl implements InternalWalletState {
stop(): void {
this.stopped = true;
this.timerGroup.stopCurrentAndFutureTimers();
- this.cryptoApi.stop();
+ this.cryptoDispatcher.stop();
}
async runUntilDone(
diff --git a/packages/taler-wallet-webextension/src/browserWorkerEntry.ts b/packages/taler-wallet-webextension/src/browserWorkerEntry.ts
index 7829e6d65..4494b9af8 100644
--- a/packages/taler-wallet-webextension/src/browserWorkerEntry.ts
+++ b/packages/taler-wallet-webextension/src/browserWorkerEntry.ts
@@ -23,18 +23,18 @@
*/
import { Logger } from "@gnu-taler/taler-util";
-import { CryptoImplementation } from "@gnu-taler/taler-wallet-core";
+import { nativeCrypto } from "@gnu-taler/taler-wallet-core";
const logger = new Logger("browserWorkerEntry.ts");
-const worker: Worker = (self as any) as Worker;
+const worker: Worker = self as any as Worker;
async function handleRequest(
operation: string,
id: number,
args: string[],
): Promise<void> {
- const impl = new CryptoImplementation();
+ const impl = nativeCrypto;
if (!(operation in impl)) {
console.error(`crypto operation '${operation}' not found`);
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts
index 4b0383f88..dd53a4feb 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -97,10 +97,10 @@ export interface UpgradeResponse {
async function callBackend(operation: string, payload: any): Promise<any> {
let response: CoreApiResponse;
try {
- response = await platform.setMessageToWalletBackground(operation, payload)
+ response = await platform.setMessageToWalletBackground(operation, payload);
} catch (e) {
console.log("Error calling backend");
- throw new Error(`Error contacting backend: ${e}`)
+ throw new Error(`Error contacting backend: ${e}`);
}
console.log("got response", response);
if (response.type === "error") {
@@ -413,12 +413,15 @@ export function importDB(dump: any): Promise<void> {
return callBackend("importDb", { dump });
}
-export function onUpdateNotification(messageTypes: Array<NotificationType>, doCallback: () => void): () => void {
+export function onUpdateNotification(
+ messageTypes: Array<NotificationType>,
+ doCallback: () => void,
+): () => void {
const listener = (message: MessageFromBackend): void => {
const shouldNotify = messageTypes.includes(message.type);
if (shouldNotify) {
doCallback();
}
};
- return platform.listenToWalletNotifications(listener)
+ return platform.listenToWalletNotifications(listener);
}