wallet: simplify crypto workers
This commit is contained in:
parent
e21c1b3192
commit
d881f4fd25
@ -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);
|
||||
|
@ -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"]) {
|
||||
|
@ -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.
|
||||
|
@ -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");
|
||||
|
||||
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 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 }>;
|
||||
}
|
||||
|
||||
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 class CryptoImplementation {
|
||||
static enableTracing = false;
|
||||
|
||||
constructor(private primitiveWorker?: PrimitiveWorker) {}
|
||||
//const logger = new Logger("cryptoImplementation.ts");
|
||||
|
||||
/**
|
||||
* 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>;
|
||||
|
||||
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>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 RsaVerificationRequest {
|
||||
hm: string;
|
||||
sig: string;
|
||||
pk: string;
|
||||
}
|
||||
|
||||
export interface EddsaSigningResult {
|
||||
sig: string;
|
||||
}
|
||||
|
||||
export interface ValidationResult {
|
||||
valid: boolean;
|
||||
}
|
||||
|
||||
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))),
|
||||
};
|
||||
},
|
||||
|
||||
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;
|
||||
}
|
||||
return { valid: res };
|
||||
},
|
||||
|
||||
isValidWireAccount(
|
||||
versionCurrent: ExchangeProtocolVersion,
|
||||
paytoUri: string,
|
||||
sig: string,
|
||||
masterPub: string,
|
||||
): boolean {
|
||||
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));
|
||||
}
|
||||
return { valid: eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub)) };
|
||||
},
|
||||
|
||||
isValidContractTermsSignature(
|
||||
contractTermsHash: string,
|
||||
sig: string,
|
||||
merchantPub: string,
|
||||
): boolean {
|
||||
const cthDec = decodeCrock(contractTermsHash);
|
||||
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;
|
@ -29,7 +29,6 @@
|
||||
*/
|
||||
import {
|
||||
AmountJson,
|
||||
AmountString,
|
||||
CoinEnvelope,
|
||||
DenominationPubKey,
|
||||
ExchangeProtocolVersion,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
90
packages/taler-wallet-core/src/crypto/workers/rpcClient.ts
Normal file
90
packages/taler-wallet-core/src/crypto/workers/rpcClient.ts
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2022 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { Logger } from "@gnu-taler/taler-util";
|
||||
import child_process from "child_process";
|
||||
import type internal from "stream";
|
||||
import { OpenedPromise, openPromise } from "../../util/promiseUtils.js";
|
||||
|
||||
const logger = new Logger("synchronousWorkerFactory.ts");
|
||||
|
||||
export class CryptoRpcClient {
|
||||
proc: child_process.ChildProcessByStdio<
|
||||
internal.Writable,
|
||||
internal.Readable,
|
||||
null
|
||||
>;
|
||||
requests: Array<{
|
||||
p: OpenedPromise<any>;
|
||||
req: any;
|
||||
}> = [];
|
||||
|
||||
constructor() {
|
||||
const stdoutChunks: Buffer[] = [];
|
||||
this.proc = child_process.spawn("taler-crypto-worker", {
|
||||
//stdio: ["pipe", "pipe", "inherit"],
|
||||
stdio: ["pipe", "pipe", "inherit"],
|
||||
detached: true,
|
||||
});
|
||||
this.proc.on("close", (): void => {
|
||||
logger.error("child process exited");
|
||||
});
|
||||
(this.proc.stdout as any).unref();
|
||||
(this.proc.stdin as any).unref();
|
||||
this.proc.unref();
|
||||
|
||||
this.proc.stdout.on("data", (x) => {
|
||||
// console.log("got chunk", x.toString("utf-8"));
|
||||
if (x instanceof Buffer) {
|
||||
const nlIndex = x.indexOf("\n");
|
||||
if (nlIndex >= 0) {
|
||||
const before = x.slice(0, nlIndex);
|
||||
const after = x.slice(nlIndex + 1);
|
||||
stdoutChunks.push(after);
|
||||
const str = Buffer.concat([...stdoutChunks, before]).toString(
|
||||
"utf-8",
|
||||
);
|
||||
const req = this.requests.shift();
|
||||
if (!req) {
|
||||
throw Error("request was undefined");
|
||||
}
|
||||
if (this.requests.length === 0) {
|
||||
this.proc.unref();
|
||||
}
|
||||
//logger.info(`got response: ${str}`);
|
||||
req.p.resolve(JSON.parse(str));
|
||||
} else {
|
||||
stdoutChunks.push(x);
|
||||
}
|
||||
} else {
|
||||
throw Error(`unexpected data chunk type (${typeof x})`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async queueRequest(req: any): Promise<any> {
|
||||
const p = openPromise<any>();
|
||||
if (this.requests.length === 0) {
|
||||
this.proc.ref();
|
||||
}
|
||||
this.requests.push({ req, p });
|
||||
this.proc.stdin.write(`${JSON.stringify(req)}\n`);
|
||||
return p.promise;
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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> {
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
? {
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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(
|
||||
|
@ -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`);
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user