gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: wallet: simplify crypto worke


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet: simplify crypto workers
Date: Wed, 23 Mar 2022 21:24:46 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new d881f4fd wallet: simplify crypto workers
d881f4fd is described below

commit d881f4fd258a27cc765a25c24e5fef9f86b6226f
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Mar 23 21:24:23 2022 +0100

    wallet: simplify crypto workers
---
 packages/taler-wallet-cli/src/bench2.ts            |   5 +-
 packages/taler-wallet-cli/src/index.ts             |  37 +-
 .../src/integrationtests/test-wallet-dbless.ts     |   5 +-
 .../crypto/{workers => }/cryptoImplementation.ts   | 713 +++++++++++++--------
 .../taler-wallet-core/src/crypto/cryptoTypes.ts    |   1 -
 .../workers/{cryptoApi.ts => cryptoDispatcher.ts}  | 216 +------
 .../src/crypto/workers/nodeThreadWorker.ts         |  19 +-
 .../{synchronousWorkerFactory.ts => rpcClient.ts}  |  79 +--
 .../src/crypto/workers/synchronousWorker.ts        |  49 +-
 .../src/crypto/workers/synchronousWorkerFactory.ts | 123 +---
 packages/taler-wallet-core/src/dbless.ts           |   8 +-
 packages/taler-wallet-core/src/headless/helpers.ts |   4 +-
 packages/taler-wallet-core/src/index.ts            |  11 +-
 .../taler-wallet-core/src/internal-wallet-state.ts |   5 +-
 .../src/operations/backup/index.ts                 |  23 +-
 .../src/operations/backup/state.ts                 |   2 +-
 .../taler-wallet-core/src/operations/common.ts     |   2 +-
 .../taler-wallet-core/src/operations/deposits.ts   |  14 +-
 .../taler-wallet-core/src/operations/exchanges.ts  |  24 +-
 packages/taler-wallet-core/src/operations/pay.ts   |  33 +-
 .../taler-wallet-core/src/operations/refresh.ts    |  22 +-
 .../taler-wallet-core/src/operations/reserves.ts   |   2 +-
 packages/taler-wallet-core/src/operations/tip.ts   |  22 +-
 .../taler-wallet-core/src/operations/withdraw.ts   |  29 +-
 packages/taler-wallet-core/src/wallet.ts           |  22 +-
 .../src/browserWorkerEntry.ts                      |   6 +-
 packages/taler-wallet-webextension/src/wxApi.ts    |  11 +-
 27 files changed, 702 insertions(+), 785 deletions(-)

diff --git a/packages/taler-wallet-cli/src/bench2.ts 
b/packages/taler-wallet-cli/src/bench2.ts
index 43c28882..c1fa674c 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 0ed935a1..3b79f78b 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 146603f3..d8abae13 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
similarity index 59%
rename from 
packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
rename to packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
index b2706788..63b2687b 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 3b339604..deff1507 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
similarity index 62%
rename from packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
rename to packages/taler-wallet-core/src/crypto/workers/cryptoDispatcher.ts
index ca498bff..810273cc 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 df57635d..42370fc1 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/synchronousWorkerFactory.ts 
b/packages/taler-wallet-core/src/crypto/workers/rpcClient.ts
similarity index 60%
copy from 
packages/taler-wallet-core/src/crypto/workers/synchronousWorkerFactory.ts
copy to packages/taler-wallet-core/src/crypto/workers/rpcClient.ts
index ca63c768..a8df8b4c 100644
--- a/packages/taler-wallet-core/src/crypto/workers/synchronousWorkerFactory.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/rpcClient.ts
@@ -1,6 +1,6 @@
 /*
  This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
+ (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
@@ -14,22 +14,17 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import {
-  PrimitiveWorker,
-} from "./cryptoImplementation.js";
-
-import { CryptoWorkerFactory } from "./cryptoApi.js";
-import { CryptoWorker } from "./cryptoWorkerInterface.js";
-
+/**
+ * Imports.
+ */
+import { Logger } from "@gnu-taler/taler-util";
 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";
+import { OpenedPromise, openPromise } from "../../util/promiseUtils.js";
 
 const logger = new Logger("synchronousWorkerFactory.ts");
 
-class MyPrimitiveWorker implements PrimitiveWorker {
+export class CryptoRpcClient {
   proc: child_process.ChildProcessByStdio<
     internal.Writable,
     internal.Readable,
@@ -67,7 +62,7 @@ class MyPrimitiveWorker implements PrimitiveWorker {
           );
           const req = this.requests.shift();
           if (!req) {
-            throw Error("request was undefined")
+            throw Error("request was undefined");
           }
           if (this.requests.length === 0) {
             this.proc.unref();
@@ -83,20 +78,6 @@ class MyPrimitiveWorker implements PrimitiveWorker {
     });
   }
 
-  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) {
@@ -106,48 +87,4 @@ class MyPrimitiveWorker implements PrimitiveWorker {
     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.
- */
-export class SynchronousCryptoWorkerFactory implements CryptoWorkerFactory {
-  startWorker(): CryptoWorker {
-    if (typeof require === "undefined") {
-      throw Error("cannot make worker, require(...) not defined");
-    }
-
-    let primitiveWorker;
-    if (process.env["TALER_WALLET_PRIMITIVE_WORKER"]) {
-      primitiveWorker = new MyPrimitiveWorker();
-    }
-
-    return new SynchronousCryptoWorker(primitiveWorker);
-  }
-
-  getConcurrency(): number {
-    return 1;
-  }
 }
diff --git a/packages/taler-wallet-core/src/crypto/workers/synchronousWorker.ts 
b/packages/taler-wallet-core/src/crypto/workers/synchronousWorker.ts
index 4d341718..1d7539ed 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 ca63c768..47f58be1 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 854a3ea0..ea5a4232 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 120c4cd4..7bc8235f 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 ca701820..979d631c 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 90439872..5ecf796e 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 8ddc0c06..5013b903 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 f25cc170..293f5613 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 5525b4de..5261b114 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 ad3f614f..2e14afdf 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 94159369..51b5c780 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 1c1a0f50..97f38bae 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 c422674a..a7773826 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 dd0fa542..9cbd63c4 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 7bd81b82..cd29f8a8 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 b7feae06..7685ede7 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 5e71cfbc..d1e9aa64 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 7829e6d6..4494b9af 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 4b0383f8..dd53a4fe 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);
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]