aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/taler-util/src/talerCrypto.ts39
-rw-r--r--packages/taler-wallet-cli/src/index.ts29
-rw-r--r--packages/taler-wallet-core/src/crypto/cryptoImplementation.ts168
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/cryptoDispatcher.ts8
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/synchronousWorker.ts53
-rw-r--r--packages/taler-wallet-core/src/operations/refresh.ts17
-rw-r--r--packages/taler-wallet-core/src/wallet.ts1
7 files changed, 234 insertions, 81 deletions
diff --git a/packages/taler-util/src/talerCrypto.ts b/packages/taler-util/src/talerCrypto.ts
index 4d6e73671..0385658ca 100644
--- a/packages/taler-util/src/talerCrypto.ts
+++ b/packages/taler-util/src/talerCrypto.ts
@@ -692,7 +692,7 @@ export interface FreshCoin {
bks: Uint8Array;
}
-function bufferForUint32(n: number): Uint8Array {
+export function bufferForUint32(n: number): Uint8Array {
const arrBuf = new ArrayBuffer(4);
const buf = new Uint8Array(arrBuf);
const dv = new DataView(arrBuf);
@@ -700,37 +700,6 @@ function bufferForUint32(n: number): Uint8Array {
return buf;
}
-export function setupRefreshPlanchet(
- transferSecret: Uint8Array,
- coinNumber: number,
-): FreshCoin {
- // See TALER_transfer_secret_to_planchet_secret in C impl
- const planchetMasterSecret = kdfKw({
- ikm: transferSecret,
- outputLength: 32,
- salt: bufferForUint32(coinNumber),
- info: stringToBytes("taler-coin-derivation"),
- });
-
- const coinPriv = kdfKw({
- ikm: planchetMasterSecret,
- outputLength: 32,
- salt: stringToBytes("coin"),
- });
-
- const bks = kdfKw({
- ikm: planchetMasterSecret,
- outputLength: 32,
- salt: stringToBytes("bks"),
- });
-
- return {
- bks,
- coinPriv,
- coinPub: eddsaGetPublic(coinPriv),
- };
-}
-
export function setupWithdrawPlanchet(
secretSeed: Uint8Array,
coinNumber: number,
@@ -786,10 +755,10 @@ export function setupRefreshTransferPub(
}
/**
- *
+ *
* @param paytoUri
- * @param salt 16-byte salt
- * @returns
+ * @param salt 16-byte salt
+ * @returns
*/
export function hashWire(paytoUri: string, salt: string): string {
const r = kdf(
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
index 3b79f78b8..254dadf93 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -64,7 +64,10 @@ 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";
+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.
@@ -75,6 +78,12 @@ export {
const logger = new Logger("taler-wallet-cli.ts");
+process.on("unhandledRejection", (error: any) => {
+ logger.error("unhandledRejection", error.message);
+ logger.error("stack", error.stack);
+ process.exit(2);
+});
+
const defaultWalletDbPath = os.homedir + "/" + ".talerwalletdb.json";
function assertUnreachable(x: never): never {
@@ -218,6 +227,7 @@ async function withWallet<T>(
} finally {
logger.info("operation with wallet finished, stopping");
wallet.stop();
+ logger.info("stopped wallet");
}
}
@@ -250,13 +260,18 @@ walletCli
console.error("Invalid JSON");
process.exit(1);
}
- const resp = await wallet.ws.handleCoreApiRequest(
- args.api.operation,
- "reqid-1",
- requestJson,
- );
- console.log(JSON.stringify(resp, undefined, 2));
+ try {
+ const resp = await wallet.ws.handleCoreApiRequest(
+ args.api.operation,
+ "reqid-1",
+ requestJson,
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ } catch (e) {
+ logger.error(`Got exception while handling API request ${e}`);
+ }
});
+ logger.info("finished handling API request");
});
walletCli
diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
index 63b2687b6..a6093e365 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
@@ -42,7 +42,6 @@ import {
eddsaVerify,
encodeCrock,
ExchangeProtocolVersion,
- FreshCoin,
hash,
hashCoinEv,
hashCoinEvInner,
@@ -53,14 +52,12 @@ import {
MakeSyncSignatureRequest,
PlanchetCreationRequest,
WithdrawalPlanchet,
- randomBytes,
RecoupRefreshRequest,
RecoupRequest,
RefreshPlanchetInfo,
rsaBlind,
rsaUnblind,
rsaVerify,
- setupRefreshPlanchet,
setupRefreshTransferPub,
setupTipPlanchet,
setupWithdrawPlanchet,
@@ -70,6 +67,8 @@ import {
UnblindedSignature,
PlanchetUnblindInfo,
TalerProtocolTimestamp,
+ kdfKw,
+ bufferForUint32,
} from "@gnu-taler/taler-util";
import bigint from "big-integer";
import { DenominationRecord, WireFee } from "../db.js";
@@ -141,6 +140,8 @@ export interface TalerCryptoInterface {
rsaVerify(req: RsaVerificationRequest): Promise<ValidationResult>;
+ rsaBlind(req: RsaBlindRequest): Promise<RsaBlindResponse>;
+
signDepositPermission(
depositInfo: DepositInfo,
): Promise<CoinDepositPermission>;
@@ -154,6 +155,14 @@ export interface TalerCryptoInterface {
signCoinLink(req: SignCoinLinkRequest): Promise<EddsaSigningResult>;
makeSyncSignature(req: MakeSyncSignatureRequest): Promise<EddsaSigningResult>;
+
+ setupRefreshPlanchet(
+ req: SetupRefreshPlanchetRequest,
+ ): Promise<FreshCoinEncoded>;
+
+ keyExchangeEcdheEddsa(
+ req: KeyExchangeEcdheEddsaRequest,
+ ): Promise<KeyExchangeResult>;
}
/**
@@ -257,6 +266,19 @@ export const nullCrypto: TalerCryptoInterface = {
): Promise<EddsaSigningResult> {
throw new Error("Function not implemented.");
},
+ setupRefreshPlanchet: function (
+ req: SetupRefreshPlanchetRequest,
+ ): Promise<FreshCoinEncoded> {
+ throw new Error("Function not implemented.");
+ },
+ rsaBlind: function (req: RsaBlindRequest): Promise<RsaBlindResponse> {
+ throw new Error("Function not implemented.");
+ },
+ keyExchangeEcdheEddsa: function (
+ req: KeyExchangeEcdheEddsaRequest,
+ ): Promise<KeyExchangeResult> {
+ throw new Error("Function not implemented.");
+ },
};
export type WithArg<X> = X extends (req: infer T) => infer R
@@ -275,12 +297,23 @@ export interface SignCoinLinkRequest {
coinEv: CoinEnvelope;
}
+export interface SetupRefreshPlanchetRequest {
+ transferSecret: string;
+ coinNumber: number;
+}
+
export interface RsaVerificationRequest {
hm: string;
sig: string;
pk: string;
}
+export interface RsaBlindRequest {
+ hm: string;
+ bks: string;
+ pub: string;
+}
+
export interface EddsaSigningResult {
sig: string;
}
@@ -341,16 +374,35 @@ export interface UnblindDenominationSignatureRequest {
evSig: BlindedDenominationSignature;
}
+export interface FreshCoinEncoded {
+ coinPub: string;
+ coinPriv: string;
+ bks: string;
+}
+
export interface RsaUnblindRequest {
blindedSig: string;
bk: string;
pk: string;
}
+export interface RsaBlindResponse {
+ blinded: string;
+}
+
export interface RsaUnblindResponse {
sig: string;
}
+export interface KeyExchangeEcdheEddsaRequest {
+ ecdhePriv: string;
+ eddsaPub: string;
+}
+
+export interface KeyExchangeResult {
+ h: string;
+}
+
export const nativeCryptoR: TalerCryptoInterfaceR = {
async eddsaSign(
tci: TalerCryptoInterfaceR,
@@ -361,6 +413,53 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
};
},
+ async rsaBlind(
+ tci: TalerCryptoInterfaceR,
+ req: RsaBlindRequest,
+ ): Promise<RsaBlindResponse> {
+ const res = rsaBlind(
+ decodeCrock(req.hm),
+ decodeCrock(req.bks),
+ decodeCrock(req.pub),
+ );
+ return {
+ blinded: encodeCrock(res),
+ };
+ },
+
+ async setupRefreshPlanchet(
+ tci: TalerCryptoInterfaceR,
+ req: SetupRefreshPlanchetRequest,
+ ): Promise<FreshCoinEncoded> {
+ const transferSecret = decodeCrock(req.transferSecret);
+ const coinNumber = req.coinNumber;
+ // See TALER_transfer_secret_to_planchet_secret in C impl
+ const planchetMasterSecret = kdfKw({
+ ikm: transferSecret,
+ outputLength: 32,
+ salt: bufferForUint32(coinNumber),
+ info: stringToBytes("taler-coin-derivation"),
+ });
+
+ const coinPriv = kdfKw({
+ ikm: planchetMasterSecret,
+ outputLength: 32,
+ salt: stringToBytes("coin"),
+ });
+
+ const bks = kdfKw({
+ ikm: planchetMasterSecret,
+ outputLength: 32,
+ salt: stringToBytes("bks"),
+ });
+
+ return {
+ bks: encodeCrock(bks),
+ coinPriv: encodeCrock(coinPriv),
+ coinPub: encodeCrock(eddsaGetPublic(coinPriv)),
+ };
+ },
+
async createPlanchet(
tci: TalerCryptoInterfaceR,
req: PlanchetCreationRequest,
@@ -374,10 +473,14 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
req.coinIndex,
);
const coinPubHash = hash(derivedPlanchet.coinPub);
- const ev = rsaBlind(coinPubHash, derivedPlanchet.bks, denomPubRsa);
+ const blindResp = await tci.rsaBlind(tci, {
+ bks: encodeCrock(derivedPlanchet.bks),
+ hm: encodeCrock(coinPubHash),
+ pub: denomPub.rsa_public_key,
+ });
const coinEv: CoinEnvelope = {
cipher: DenomKeyType.Rsa,
- rsa_blinded_planchet: encodeCrock(ev),
+ rsa_blinded_planchet: blindResp.blinded,
};
const amountWithFee = Amounts.add(req.value, req.feeWithdraw).amount;
const denomPubHash = hashDenomPub(req.denomPub);
@@ -423,10 +526,14 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
const fc = setupTipPlanchet(decodeCrock(req.secretSeed), req.planchetIndex);
const denomPub = decodeCrock(req.denomPub.rsa_public_key);
const coinPubHash = hash(fc.coinPub);
- const ev = rsaBlind(coinPubHash, fc.bks, denomPub);
+ const blindResp = await tci.rsaBlind(tci, {
+ bks: encodeCrock(fc.bks),
+ hm: encodeCrock(coinPubHash),
+ pub: encodeCrock(denomPub),
+ });
const coinEv = {
cipher: DenomKeyType.Rsa,
- rsa_blinded_planchet: encodeCrock(ev),
+ rsa_blinded_planchet: blindResp.blinded,
};
const tipPlanchet: DerivedTipPlanchet = {
blindingKey: encodeCrock(fc.bks),
@@ -798,32 +905,32 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
const denomSel = newCoinDenoms[j];
for (let k = 0; k < denomSel.count; k++) {
const coinIndex = planchets.length;
- const transferPriv = decodeCrock(transferPrivs[i]);
- const oldCoinPub = decodeCrock(meltCoinPub);
- const transferSecret = keyExchangeEcdheEddsa(
- transferPriv,
- oldCoinPub,
- );
+ const transferSecretRes = await tci.keyExchangeEcdheEddsa(tci, {
+ ecdhePriv: transferPrivs[i],
+ eddsaPub: meltCoinPub,
+ });
let coinPub: Uint8Array;
let coinPriv: Uint8Array;
let blindingFactor: Uint8Array;
- // FIXME: make setupRefreshPlanchet a crypto api fn
- let fresh: FreshCoin = setupRefreshPlanchet(
- transferSecret,
- coinIndex,
- );
- coinPriv = fresh.coinPriv;
- coinPub = fresh.coinPub;
- blindingFactor = fresh.bks;
+ let fresh: FreshCoinEncoded = await tci.setupRefreshPlanchet(tci, {
+ coinNumber: coinIndex,
+ transferSecret: transferSecretRes.h,
+ });
+ coinPriv = decodeCrock(fresh.coinPriv);
+ coinPub = decodeCrock(fresh.coinPub);
+ blindingFactor = decodeCrock(fresh.bks);
const coinPubHash = hash(coinPub);
if (denomSel.denomPub.cipher !== DenomKeyType.Rsa) {
throw Error("unsupported cipher, can't create refresh session");
}
- const rsaDenomPub = decodeCrock(denomSel.denomPub.rsa_public_key);
- const ev = rsaBlind(coinPubHash, blindingFactor, rsaDenomPub);
+ const blindResult = await tci.rsaBlind(tci, {
+ bks: encodeCrock(blindingFactor),
+ hm: encodeCrock(coinPubHash),
+ pub: denomSel.denomPub.rsa_public_key,
+ });
const coinEv: CoinEnvelope = {
cipher: DenomKeyType.Rsa,
- rsa_blinded_planchet: encodeCrock(ev),
+ rsa_blinded_planchet: blindResult.blinded,
};
const coinEvHash = hashCoinEv(
coinEv,
@@ -921,6 +1028,19 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
const uploadSig = eddsaSign(sigBlob, decodeCrock(req.accountPriv));
return { sig: encodeCrock(uploadSig) };
},
+ async keyExchangeEcdheEddsa(
+ tci: TalerCryptoInterfaceR,
+ req: KeyExchangeEcdheEddsaRequest,
+ ): Promise<KeyExchangeResult> {
+ return {
+ h: encodeCrock(
+ keyExchangeEcdheEddsa(
+ decodeCrock(req.ecdhePriv),
+ decodeCrock(req.eddsaPub),
+ ),
+ ),
+ };
+ },
};
function amountToBuffer(amount: AmountJson): Uint8Array {
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoDispatcher.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoDispatcher.ts
index 810273cca..f5782ab09 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoDispatcher.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoDispatcher.ts
@@ -122,7 +122,7 @@ export class CryptoDispatcher {
worker.idleTimeoutHandle = null;
}
if (worker.currentWorkItem) {
- worker.currentWorkItem.reject(Error("explicitly terminated"));
+ worker.currentWorkItem.reject(new CryptoApiStoppedError());
worker.currentWorkItem = null;
}
if (worker.w) {
@@ -143,7 +143,7 @@ export class CryptoDispatcher {
*/
wake(ws: WorkerState, work: WorkItem): void {
if (this.stopped) {
- throw new CryptoApiStoppedError();
+ return;
}
if (ws.currentWorkItem !== null) {
throw Error("assertion failed");
@@ -331,8 +331,8 @@ export class CryptoDispatcher {
}
timeout.clear();
resolve(x);
- });
- p.catch((x) => {
+ }).catch((x) => {
+ logger.info(`crypto RPC call ${operation} threw`);
if (timedOut) {
return;
}
diff --git a/packages/taler-wallet-core/src/crypto/workers/synchronousWorker.ts b/packages/taler-wallet-core/src/crypto/workers/synchronousWorker.ts
index 1d7539ed6..a5f154e92 100644
--- a/packages/taler-wallet-core/src/crypto/workers/synchronousWorker.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/synchronousWorker.ts
@@ -47,14 +47,11 @@ export class SynchronousCryptoWorker {
this.cryptoImplR = { ...nativeCryptoR };
- if (
- process.env["TALER_WALLET_RPC_CRYPRO"] ||
- // Old name
- process.env["TALER_WALLET_PRIMITIVE_WORKER"]
- ) {
+ 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) => {
- logger.trace("making RPC request");
+ logger.info("calling RPC impl of eddsaSign");
return await rpc.queueRequest({
op: "eddsa_sign",
args: {
@@ -63,6 +60,46 @@ export class SynchronousCryptoWorker {
},
});
};
+ 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,
+ };
+ };
}
}
@@ -101,8 +138,8 @@ export class SynchronousCryptoWorker {
let result: any;
try {
result = await (impl as any)[operation](impl, req);
- } catch (e) {
- logger.error("error during operation", e);
+ } catch (e: any) {
+ logger.error(`error during operation '${operation}': ${e}`);
return;
}
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts
index a77738262..2ab06abae 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -797,11 +797,22 @@ async function processRefreshGroupImpl(
return;
}
// Process refresh sessions of the group in parallel.
+ logger.trace("processing refresh sessions for old coins");
const ps = refreshGroup.oldCoinPubs.map((x, i) =>
- processRefreshSession(ws, refreshGroupId, i),
+ processRefreshSession(ws, refreshGroupId, i).catch((x) => {
+ logger.warn("process refresh session got exception");
+ logger.warn(`exc ${x}`);
+ logger.warn(`exc stack ${x.stack}`);
+ }),
);
- await Promise.all(ps);
- logger.trace("refresh finished");
+ try {
+ logger.trace("waiting for refreshes");
+ await Promise.all(ps);
+ logger.trace("refresh finished");
+ } catch (e) {
+ logger.warn("process refresh sessions got exception");
+ logger.warn(`exception: ${e}`);
+ }
}
async function processRefreshSession(
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index d1e9aa646..bb560774a 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -1077,6 +1077,7 @@ export async function handleCoreApiRequest(
};
} catch (e: any) {
const err = getErrorDetailFromException(e);
+ logger.info(`finished wallet core request with error: ${j2s(err)}`);
return {
type: "error",
operation,