diff options
5 files changed, 333 insertions, 1 deletions
diff --git a/packages/taler-wallet-core/src/crypto/workers/synchronousWorkerNode.ts b/packages/taler-wallet-core/src/crypto/workers/synchronousWorkerNode.ts new file mode 100644 index 000000000..c1620574f --- /dev/null +++ b/packages/taler-wallet-core/src/crypto/workers/synchronousWorkerNode.ts @@ -0,0 +1,204 @@ +/* + This file is part of GNU Taler + (C) 2019 GNUnet e.V. + + 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/> + */ + +import { Logger } from "@gnu-taler/taler-util"; +import { + nativeCryptoR, + TalerCryptoInterfaceR, +} from "../cryptoImplementation.js"; +import { CryptoRpcClient } from "./rpcClient.js"; + +const logger = new Logger("synchronousWorker.ts"); + +/** + * Worker implementation that uses node subprocesses. + */ +export class SynchronousCryptoWorker { + /** + * Function to be called when we receive a message from the worker thread. + */ + onmessage: undefined | ((m: any) => void); + + /** + * Function to be called when we receive an error from the worker thread. + */ + onerror: undefined | ((m: any) => void); + + cryptoImplR: TalerCryptoInterfaceR; + + rpcClient: CryptoRpcClient | undefined; + + constructor() { + this.onerror = undefined; + this.onmessage = undefined; + + this.cryptoImplR = { ...nativeCryptoR }; + + if (process.env["TALER_WALLET_PRIMITIVE_WORKER"]) { + logger.info("using RPC for some crypto operations"); + const rpc = (this.rpcClient = new CryptoRpcClient()); + this.cryptoImplR.eddsaSign = async (_, req) => { + return await rpc.queueRequest({ + op: "eddsa_sign", + args: { + msg: req.msg, + priv: req.priv, + }, + }); + }; + this.cryptoImplR.setupRefreshPlanchet = async (_, req) => { + const res = await rpc.queueRequest({ + op: "setup_refresh_planchet", + args: { + coin_index: req.coinNumber, + transfer_secret: req.transferSecret, + }, + }); + return { + bks: res.blinding_key, + coinPriv: res.coin_priv, + coinPub: res.coin_pub, + }; + }; + this.cryptoImplR.rsaBlind = async (_, req) => { + const res = await rpc.queueRequest({ + op: "rsa_blind", + args: { + bks: req.bks, + hm: req.hm, + pub: req.pub, + }, + }); + return { + blinded: res.blinded, + }; + }; + this.cryptoImplR.keyExchangeEcdheEddsa = async (_, req) => { + const res = await rpc.queueRequest({ + op: "kx_ecdhe_eddsa", + args: { + ecdhe_priv: req.ecdhePriv, + eddsa_pub: req.eddsaPub, + }, + }); + return { + h: res.h, + }; + }; + this.cryptoImplR.eddsaGetPublic = async (_, req) => { + const res = await rpc.queueRequest({ + op: "eddsa_get_public", + args: { + eddsa_priv: req.priv, + }, + }); + return { + pub: res.eddsa_pub, + }; + }; + this.cryptoImplR.ecdheGetPublic = async (_, req) => { + const res = await rpc.queueRequest({ + op: "ecdhe_get_public", + args: { + ecdhe_priv: req.priv, + }, + }); + return { + pub: res.ecdhe_pub, + }; + }; + } + } + + /** + * Add an event listener for either an "error" or "message" event. + */ + addEventListener(event: "message" | "error", fn: (x: any) => void): void { + switch (event) { + case "message": + this.onmessage = fn; + break; + case "error": + this.onerror = fn; + break; + } + } + + private dispatchMessage(msg: any): void { + if (this.onmessage) { + this.onmessage({ data: msg }); + } + } + + private async handleRequest( + operation: string, + id: number, + req: unknown, + ): Promise<void> { + const impl = this.cryptoImplR; + + if (!(operation in impl)) { + console.error(`crypto operation '${operation}' not found`); + return; + } + + let result: any; + try { + result = await (impl as any)[operation](impl, req); + } catch (e: any) { + logger.error(`error during operation '${operation}': ${e}`); + return; + } + + try { + setTimeout(() => this.dispatchMessage({ result, id }), 0); + } catch (e) { + logger.error("got error during dispatch", e); + } + } + + /** + * 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"); + return; + } + const id = msg.id; + if (typeof id !== "number") { + console.error("RPC id must be number"); + return; + } + const operation = msg.operation; + if (typeof operation !== "string") { + console.error("RPC operation must be string"); + return; + } + + this.handleRequest(operation, id, args).catch((e) => { + console.error("Error while handling crypto request:", e); + }); + } + + /** + * Forcibly terminate the worker thread. + */ + terminate(): void { + // This is a no-op. + } +} diff --git a/packages/taler-wallet-core/src/crypto/workers/synchronousWorkerWeb.ts b/packages/taler-wallet-core/src/crypto/workers/synchronousWorkerWeb.ts new file mode 100644 index 000000000..3a2bddeed --- /dev/null +++ b/packages/taler-wallet-core/src/crypto/workers/synchronousWorkerWeb.ts @@ -0,0 +1,127 @@ +/* + This file is part of GNU Taler + (C) 2019 GNUnet e.V. + + 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/> + */ + +import { Logger } from "@gnu-taler/taler-util"; +import { + nativeCryptoR, + TalerCryptoInterfaceR, +} from "../cryptoImplementation.js"; + + + +const logger = new Logger("synchronousWorker.ts"); + +/** + * Worker implementation that uses node subprocesses. + */ +export class SynchronousCryptoWorker { + /** + * Function to be called when we receive a message from the worker thread. + */ + onmessage: undefined | ((m: any) => void); + + /** + * Function to be called when we receive an error from the worker thread. + */ + onerror: undefined | ((m: any) => void); + + cryptoImplR: TalerCryptoInterfaceR; + + constructor() { + this.onerror = undefined; + this.onmessage = undefined; + this.cryptoImplR = { ...nativeCryptoR }; + } + + /** + * Add an event listener for either an "error" or "message" event. + */ + addEventListener(event: "message" | "error", fn: (x: any) => void): void { + switch (event) { + case "message": + this.onmessage = fn; + break; + case "error": + this.onerror = fn; + break; + } + } + + private dispatchMessage(msg: any): void { + if (this.onmessage) { + this.onmessage({ data: msg }); + } + } + + private async handleRequest( + operation: string, + id: number, + args: string[], + ): Promise<void> { + const impl = this.cryptoImplR; + + if (!(operation in impl)) { + console.error(`crypto operation '${operation}' not found`); + return; + } + + let result: any; + try { + result = await (impl as any)[operation](...args); + } catch (e: any) { + logger.error(`error during operation '${operation}': ${e}`); + return; + } + + try { + setTimeout(() => this.dispatchMessage({ result, id }), 0); + } catch (e) { + logger.error("got error during dispatch", e); + } + } + + /** + * 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"); + return; + } + const id = msg.id; + if (typeof id !== "number") { + console.error("RPC id must be number"); + return; + } + const operation = msg.operation; + if (typeof operation !== "string") { + console.error("RPC operation must be string"); + return; + } + + this.handleRequest(operation, id, args).catch((e) => { + console.error("Error while handling crypto request:", e); + }); + } + + /** + * Forcibly terminate the worker thread. + */ + terminate(): void { + // This is a no-op. + } +} diff --git a/packages/taler-wallet-core/src/index.browser.ts b/packages/taler-wallet-core/src/index.browser.ts index 88ea52479..029bc533f 100644 --- a/packages/taler-wallet-core/src/index.browser.ts +++ b/packages/taler-wallet-core/src/index.browser.ts @@ -15,3 +15,4 @@ */ export * from "./index.js"; +export { SynchronousCryptoWorker } from "./crypto/workers/synchronousWorkerWeb.js"; diff --git a/packages/taler-wallet-core/src/index.node.ts b/packages/taler-wallet-core/src/index.node.ts index 33d9c4a27..d7211b4dc 100644 --- a/packages/taler-wallet-core/src/index.node.ts +++ b/packages/taler-wallet-core/src/index.node.ts @@ -24,6 +24,7 @@ export { DefaultNodeWalletArgs, } from "./headless/helpers.js"; export * from "./crypto/workers/nodeThreadWorker.js"; +export { SynchronousCryptoWorker } from "./crypto/workers/synchronousWorkerNode.js"; export type { AccessStats } from "@gnu-taler/idb-bridge"; export * from "./crypto/workers/synchronousWorkerFactory.js"; diff --git a/packages/taler-wallet-core/src/index.ts b/packages/taler-wallet-core/src/index.ts index 979d631c0..6b97f56f4 100644 --- a/packages/taler-wallet-core/src/index.ts +++ b/packages/taler-wallet-core/src/index.ts @@ -38,7 +38,6 @@ export { CryptoWorkerFactory, CryptoDispatcher, } from "./crypto/workers/cryptoDispatcher.js"; -export { SynchronousCryptoWorker } from "./crypto/workers/synchronousWorker.js"; export * from "./pending-types.js"; |