wallet: simplify crypto workers
This commit is contained in:
parent
e21c1b3192
commit
d881f4fd25
@ -27,7 +27,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
checkReserve,
|
checkReserve,
|
||||||
createFakebankReserve,
|
createFakebankReserve,
|
||||||
CryptoApi,
|
CryptoDispatcher,
|
||||||
depositCoin,
|
depositCoin,
|
||||||
downloadExchangeInfo,
|
downloadExchangeInfo,
|
||||||
findDenomOrThrow,
|
findDenomOrThrow,
|
||||||
@ -50,7 +50,8 @@ export async function runBench2(configJson: any): Promise<void> {
|
|||||||
// Validate the configuration file for this benchmark.
|
// Validate the configuration file for this benchmark.
|
||||||
const benchConf = codecForBench2Config().decode(configJson);
|
const benchConf = codecForBench2Config().decode(configJson);
|
||||||
const curr = benchConf.currency;
|
const curr = benchConf.currency;
|
||||||
const cryptoApi = new CryptoApi(new SynchronousCryptoWorkerFactory());
|
const cryptoDisp = new CryptoDispatcher(new SynchronousCryptoWorkerFactory());
|
||||||
|
const cryptoApi = cryptoDisp.cryptoApi;
|
||||||
|
|
||||||
const http = new NodeHttpLib();
|
const http = new NodeHttpLib();
|
||||||
http.setThrottling(false);
|
http.setThrottling(false);
|
||||||
|
@ -50,18 +50,21 @@ import {
|
|||||||
NodeHttpLib,
|
NodeHttpLib,
|
||||||
getDefaultNodeWallet,
|
getDefaultNodeWallet,
|
||||||
NodeThreadCryptoWorkerFactory,
|
NodeThreadCryptoWorkerFactory,
|
||||||
CryptoApi,
|
|
||||||
walletCoreDebugFlags,
|
walletCoreDebugFlags,
|
||||||
WalletApiOperation,
|
WalletApiOperation,
|
||||||
WalletCoreApiClient,
|
WalletCoreApiClient,
|
||||||
Wallet,
|
Wallet,
|
||||||
getErrorDetailFromException,
|
getErrorDetailFromException,
|
||||||
|
CryptoDispatcher,
|
||||||
|
SynchronousCryptoWorkerFactory,
|
||||||
|
nativeCrypto,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
import { lintExchangeDeployment } from "./lint.js";
|
import { lintExchangeDeployment } from "./lint.js";
|
||||||
import { runBench1 } from "./bench1.js";
|
import { runBench1 } from "./bench1.js";
|
||||||
import { runEnv1 } from "./env1.js";
|
import { runEnv1 } from "./env1.js";
|
||||||
import { GlobalTestState, runTestWithState } from "./harness/harness.js";
|
import { GlobalTestState, runTestWithState } from "./harness/harness.js";
|
||||||
import { runBench2 } from "./bench2.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
|
// This module also serves as the entry point for the crypto
|
||||||
// thread worker, and thus must expose these two handlers.
|
// thread worker, and thus must expose these two handlers.
|
||||||
@ -1121,14 +1124,30 @@ testCli.subcommand("tvgcheck", "tvgcheck").action(async (args) => {
|
|||||||
console.log("check passed!");
|
console.log("check passed!");
|
||||||
});
|
});
|
||||||
|
|
||||||
testCli.subcommand("cryptoworker", "cryptoworker").action(async (args) => {
|
testCli
|
||||||
const workerFactory = new NodeThreadCryptoWorkerFactory();
|
.subcommand("cryptoworker", "cryptoworker")
|
||||||
const cryptoApi = new CryptoApi(workerFactory);
|
.maybeOption("impl", ["--impl"], clk.STRING)
|
||||||
const input = "foo";
|
.action(async (args) => {
|
||||||
console.log(`testing crypto worker by hashing string '${input}'`);
|
let cryptoApi: TalerCryptoInterface;
|
||||||
const res = await cryptoApi.hashString(input);
|
if (!args.cryptoworker.impl || args.cryptoworker.impl === "node") {
|
||||||
console.log(res);
|
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() {
|
export function main() {
|
||||||
if (process.env["TALER_WALLET_DEBUG_DENOMSEL_ALLOW_LATE"]) {
|
if (process.env["TALER_WALLET_DEBUG_DENOMSEL_ALLOW_LATE"]) {
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
import { j2s } from "@gnu-taler/taler-util";
|
import { j2s } from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
checkReserve,
|
checkReserve,
|
||||||
CryptoApi,
|
CryptoDispatcher,
|
||||||
depositCoin,
|
depositCoin,
|
||||||
downloadExchangeInfo,
|
downloadExchangeInfo,
|
||||||
findDenomOrThrow,
|
findDenomOrThrow,
|
||||||
@ -44,7 +44,8 @@ export async function runWalletDblessTest(t: GlobalTestState) {
|
|||||||
const { bank, exchange } = await createSimpleTestkudosEnvironment(t);
|
const { bank, exchange } = await createSimpleTestkudosEnvironment(t);
|
||||||
|
|
||||||
const http = new NodeHttpLib();
|
const http = new NodeHttpLib();
|
||||||
const cryptoApi = new CryptoApi(new SynchronousCryptoWorkerFactory());
|
const cryptiDisp = new CryptoDispatcher(new SynchronousCryptoWorkerFactory());
|
||||||
|
const cryptoApi = cryptiDisp.cryptoApi;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Withdraw digital cash into the wallet.
|
// Withdraw digital cash into the wallet.
|
||||||
|
@ -44,7 +44,6 @@ import {
|
|||||||
ExchangeProtocolVersion,
|
ExchangeProtocolVersion,
|
||||||
FreshCoin,
|
FreshCoin,
|
||||||
hash,
|
hash,
|
||||||
HashCodeString,
|
|
||||||
hashCoinEv,
|
hashCoinEv,
|
||||||
hashCoinEvInner,
|
hashCoinEvInner,
|
||||||
hashDenomPub,
|
hashDenomPub,
|
||||||
@ -67,15 +66,13 @@ import {
|
|||||||
setupWithdrawPlanchet,
|
setupWithdrawPlanchet,
|
||||||
stringToBytes,
|
stringToBytes,
|
||||||
TalerSignaturePurpose,
|
TalerSignaturePurpose,
|
||||||
AbsoluteTime,
|
|
||||||
BlindedDenominationSignature,
|
BlindedDenominationSignature,
|
||||||
UnblindedSignature,
|
UnblindedSignature,
|
||||||
PlanchetUnblindInfo,
|
PlanchetUnblindInfo,
|
||||||
TalerProtocolTimestamp,
|
TalerProtocolTimestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import bigint from "big-integer";
|
import bigint from "big-integer";
|
||||||
import { DenominationRecord, WireFee } from "../../db.js";
|
import { DenominationRecord, WireFee } from "../db.js";
|
||||||
import * as timer from "../../util/timer.js";
|
|
||||||
import {
|
import {
|
||||||
CreateRecoupRefreshReqRequest,
|
CreateRecoupRefreshReqRequest,
|
||||||
CreateRecoupReqRequest,
|
CreateRecoupReqRequest,
|
||||||
@ -84,90 +81,288 @@ import {
|
|||||||
DeriveRefreshSessionRequest,
|
DeriveRefreshSessionRequest,
|
||||||
DeriveTipRequest,
|
DeriveTipRequest,
|
||||||
SignTrackTransactionRequest,
|
SignTrackTransactionRequest,
|
||||||
} from "../cryptoTypes.js";
|
} from "./cryptoTypes.js";
|
||||||
|
|
||||||
const logger = new Logger("cryptoImplementation.ts");
|
//const logger = new Logger("cryptoImplementation.ts");
|
||||||
|
|
||||||
function amountToBuffer(amount: AmountJson): Uint8Array {
|
|
||||||
const buffer = new ArrayBuffer(8 + 4 + 12);
|
|
||||||
const dvbuf = new DataView(buffer);
|
|
||||||
const u8buf = new Uint8Array(buffer);
|
|
||||||
const curr = stringToBytes(amount.currency);
|
|
||||||
if (typeof dvbuf.setBigUint64 !== "undefined") {
|
|
||||||
dvbuf.setBigUint64(0, BigInt(amount.value));
|
|
||||||
} else {
|
|
||||||
const arr = bigint(amount.value).toArray(2 ** 8).value;
|
|
||||||
let offset = 8 - arr.length;
|
|
||||||
for (let i = 0; i < arr.length; i++) {
|
|
||||||
dvbuf.setUint8(offset++, arr[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dvbuf.setUint32(8, amount.fraction);
|
|
||||||
u8buf.set(curr, 8 + 4);
|
|
||||||
|
|
||||||
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) {}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Create a pre-coin of the given denomination to be withdrawn from then given
|
||||||
* reserve.
|
* 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(
|
async createPlanchet(
|
||||||
|
tci: TalerCryptoInterfaceR,
|
||||||
req: PlanchetCreationRequest,
|
req: PlanchetCreationRequest,
|
||||||
): Promise<WithdrawalPlanchet> {
|
): Promise<WithdrawalPlanchet> {
|
||||||
const denomPub = req.denomPub;
|
const denomPub = req.denomPub;
|
||||||
@ -195,7 +390,7 @@ export class CryptoImplementation {
|
|||||||
.put(evHash)
|
.put(evHash)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
const sigResult = await myEddsaSign(this.primitiveWorker, {
|
const sigResult = await tci.eddsaSign(tci, {
|
||||||
msg: encodeCrock(withdrawRequest),
|
msg: encodeCrock(withdrawRequest),
|
||||||
priv: req.reservePriv,
|
priv: req.reservePriv,
|
||||||
});
|
});
|
||||||
@ -216,12 +411,12 @@ export class CryptoImplementation {
|
|||||||
} else {
|
} else {
|
||||||
throw Error("unsupported cipher, unable to create planchet");
|
throw Error("unsupported cipher, unable to create planchet");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
async createTipPlanchet(
|
||||||
* Create a planchet used for tipping, including the private keys.
|
tci: TalerCryptoInterfaceR,
|
||||||
*/
|
req: DeriveTipRequest,
|
||||||
createTipPlanchet(req: DeriveTipRequest): DerivedTipPlanchet {
|
): Promise<DerivedTipPlanchet> {
|
||||||
if (req.denomPub.cipher !== DenomKeyType.Rsa) {
|
if (req.denomPub.cipher !== DenomKeyType.Rsa) {
|
||||||
throw Error(`unsupported cipher (${req.denomPub.cipher})`);
|
throw Error(`unsupported cipher (${req.denomPub.cipher})`);
|
||||||
}
|
}
|
||||||
@ -243,22 +438,28 @@ export class CryptoImplementation {
|
|||||||
coinPub: encodeCrock(fc.coinPub),
|
coinPub: encodeCrock(fc.coinPub),
|
||||||
};
|
};
|
||||||
return tipPlanchet;
|
return tipPlanchet;
|
||||||
}
|
},
|
||||||
|
|
||||||
signTrackTransaction(req: SignTrackTransactionRequest): string {
|
async signTrackTransaction(
|
||||||
|
tci: TalerCryptoInterfaceR,
|
||||||
|
req: SignTrackTransactionRequest,
|
||||||
|
): Promise<EddsaSigningResult> {
|
||||||
const p = buildSigPS(TalerSignaturePurpose.MERCHANT_TRACK_TRANSACTION)
|
const p = buildSigPS(TalerSignaturePurpose.MERCHANT_TRACK_TRANSACTION)
|
||||||
.put(decodeCrock(req.contractTermsHash))
|
.put(decodeCrock(req.contractTermsHash))
|
||||||
.put(decodeCrock(req.wireHash))
|
.put(decodeCrock(req.wireHash))
|
||||||
.put(decodeCrock(req.merchantPub))
|
.put(decodeCrock(req.merchantPub))
|
||||||
.put(decodeCrock(req.coinPub))
|
.put(decodeCrock(req.coinPub))
|
||||||
.build();
|
.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.
|
* 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)
|
const p = buildSigPS(TalerSignaturePurpose.WALLET_COIN_RECOUP)
|
||||||
.put(decodeCrock(req.denomPubHash))
|
.put(decodeCrock(req.denomPubHash))
|
||||||
.put(decodeCrock(req.blindingKey))
|
.put(decodeCrock(req.blindingKey))
|
||||||
@ -281,14 +482,15 @@ export class CryptoImplementation {
|
|||||||
} else {
|
} else {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and sign a message to recoup a coin.
|
* Create and sign a message to recoup a coin.
|
||||||
*/
|
*/
|
||||||
createRecoupRefreshRequest(
|
async createRecoupRefreshRequest(
|
||||||
|
tci: TalerCryptoInterfaceR,
|
||||||
req: CreateRecoupRefreshReqRequest,
|
req: CreateRecoupRefreshReqRequest,
|
||||||
): RecoupRefreshRequest {
|
): Promise<RecoupRefreshRequest> {
|
||||||
const p = buildSigPS(TalerSignaturePurpose.WALLET_COIN_RECOUP_REFRESH)
|
const p = buildSigPS(TalerSignaturePurpose.WALLET_COIN_RECOUP_REFRESH)
|
||||||
.put(decodeCrock(req.denomPubHash))
|
.put(decodeCrock(req.denomPubHash))
|
||||||
.put(decodeCrock(req.blindingKey))
|
.put(decodeCrock(req.blindingKey))
|
||||||
@ -311,32 +513,32 @@ export class CryptoImplementation {
|
|||||||
} else {
|
} else {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a payment signature is valid.
|
* Check if a payment signature is valid.
|
||||||
*/
|
*/
|
||||||
isValidPaymentSignature(
|
async isValidPaymentSignature(
|
||||||
sig: string,
|
tci: TalerCryptoInterfaceR,
|
||||||
contractHash: string,
|
req: PaymentSignatureValidationRequest,
|
||||||
merchantPub: string,
|
): Promise<ValidationResult> {
|
||||||
): boolean {
|
const { contractHash, sig, merchantPub } = req;
|
||||||
const p = buildSigPS(TalerSignaturePurpose.MERCHANT_PAYMENT_OK)
|
const p = buildSigPS(TalerSignaturePurpose.MERCHANT_PAYMENT_OK)
|
||||||
.put(decodeCrock(contractHash))
|
.put(decodeCrock(contractHash))
|
||||||
.build();
|
.build();
|
||||||
const sigBytes = decodeCrock(sig);
|
const sigBytes = decodeCrock(sig);
|
||||||
const pubBytes = decodeCrock(merchantPub);
|
const pubBytes = decodeCrock(merchantPub);
|
||||||
return eddsaVerify(p, sigBytes, pubBytes);
|
return { valid: eddsaVerify(p, sigBytes, pubBytes) };
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a wire fee is correctly signed.
|
* Check if a wire fee is correctly signed.
|
||||||
*/
|
*/
|
||||||
async isValidWireFee(
|
async isValidWireFee(
|
||||||
type: string,
|
tci: TalerCryptoInterfaceR,
|
||||||
wf: WireFee,
|
req: WireFeeValidationRequest,
|
||||||
masterPub: string,
|
): Promise<ValidationResult> {
|
||||||
): Promise<boolean> {
|
const { type, wf, masterPub } = req;
|
||||||
const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_FEES)
|
const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_FEES)
|
||||||
.put(hash(stringToBytes(type + "\0")))
|
.put(hash(stringToBytes(type + "\0")))
|
||||||
.put(timestampRoundedToBuffer(wf.startStamp))
|
.put(timestampRoundedToBuffer(wf.startStamp))
|
||||||
@ -347,25 +549,17 @@ export class CryptoImplementation {
|
|||||||
.build();
|
.build();
|
||||||
const sig = decodeCrock(wf.sig);
|
const sig = decodeCrock(wf.sig);
|
||||||
const pub = decodeCrock(masterPub);
|
const pub = decodeCrock(masterPub);
|
||||||
if (this.primitiveWorker) {
|
return { valid: eddsaVerify(p, sig, pub) };
|
||||||
return (
|
},
|
||||||
await this.primitiveWorker.eddsaVerify({
|
|
||||||
msg: encodeCrock(p),
|
|
||||||
pub: masterPub,
|
|
||||||
sig: encodeCrock(sig),
|
|
||||||
})
|
|
||||||
).valid;
|
|
||||||
}
|
|
||||||
return eddsaVerify(p, sig, pub);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the signature of a denomination is valid.
|
* Check if the signature of a denomination is valid.
|
||||||
*/
|
*/
|
||||||
async isValidDenom(
|
async isValidDenom(
|
||||||
denom: DenominationRecord,
|
tci: TalerCryptoInterfaceR,
|
||||||
masterPub: string,
|
req: DenominationValidationRequest,
|
||||||
): Promise<boolean> {
|
): Promise<ValidationResult> {
|
||||||
|
const { masterPub, denom } = req;
|
||||||
const p = buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY)
|
const p = buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY)
|
||||||
.put(decodeCrock(masterPub))
|
.put(decodeCrock(masterPub))
|
||||||
.put(timestampRoundedToBuffer(denom.stampStart))
|
.put(timestampRoundedToBuffer(denom.stampStart))
|
||||||
@ -382,56 +576,59 @@ export class CryptoImplementation {
|
|||||||
const sig = decodeCrock(denom.masterSig);
|
const sig = decodeCrock(denom.masterSig);
|
||||||
const pub = decodeCrock(masterPub);
|
const pub = decodeCrock(masterPub);
|
||||||
const res = eddsaVerify(p, sig, pub);
|
const res = eddsaVerify(p, sig, pub);
|
||||||
return res;
|
return { valid: res };
|
||||||
}
|
},
|
||||||
|
|
||||||
isValidWireAccount(
|
async isValidWireAccount(
|
||||||
versionCurrent: ExchangeProtocolVersion,
|
tci: TalerCryptoInterfaceR,
|
||||||
paytoUri: string,
|
req: WireAccountValidationRequest,
|
||||||
sig: string,
|
): Promise<ValidationResult> {
|
||||||
masterPub: string,
|
const { sig, masterPub, paytoUri } = req;
|
||||||
): boolean {
|
|
||||||
const paytoHash = hashTruncate32(stringToBytes(paytoUri + "\0"));
|
const paytoHash = hashTruncate32(stringToBytes(paytoUri + "\0"));
|
||||||
const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_DETAILS)
|
const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_DETAILS)
|
||||||
.put(paytoHash)
|
.put(paytoHash)
|
||||||
.build();
|
.build();
|
||||||
return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub));
|
return { valid: eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub)) };
|
||||||
}
|
},
|
||||||
|
|
||||||
isValidContractTermsSignature(
|
async isValidContractTermsSignature(
|
||||||
contractTermsHash: string,
|
tci: TalerCryptoInterfaceR,
|
||||||
sig: string,
|
req: ContractTermsValidationRequest,
|
||||||
merchantPub: string,
|
): Promise<ValidationResult> {
|
||||||
): boolean {
|
const cthDec = decodeCrock(req.contractTermsHash);
|
||||||
const cthDec = decodeCrock(contractTermsHash);
|
|
||||||
const p = buildSigPS(TalerSignaturePurpose.MERCHANT_CONTRACT)
|
const p = buildSigPS(TalerSignaturePurpose.MERCHANT_CONTRACT)
|
||||||
.put(cthDec)
|
.put(cthDec)
|
||||||
.build();
|
.build();
|
||||||
return eddsaVerify(p, decodeCrock(sig), decodeCrock(merchantPub));
|
return {
|
||||||
}
|
valid: eddsaVerify(p, decodeCrock(req.sig), decodeCrock(req.merchantPub)),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new EdDSA key pair.
|
* Create a new EdDSA key pair.
|
||||||
*/
|
*/
|
||||||
createEddsaKeypair(): { priv: string; pub: string } {
|
async createEddsaKeypair(tci: TalerCryptoInterfaceR): Promise<EddsaKeypair> {
|
||||||
const pair = createEddsaKeyPair();
|
const pair = createEddsaKeyPair();
|
||||||
return {
|
return {
|
||||||
priv: encodeCrock(pair.eddsaPriv),
|
priv: encodeCrock(pair.eddsaPriv),
|
||||||
pub: encodeCrock(pair.eddsaPub),
|
pub: encodeCrock(pair.eddsaPub),
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
eddsaGetPublic(key: string): { priv: string; pub: string } {
|
async eddsaGetPublic(
|
||||||
|
tci: TalerCryptoInterfaceR,
|
||||||
|
req: EddsaGetPublicRequest,
|
||||||
|
): Promise<EddsaKeypair> {
|
||||||
return {
|
return {
|
||||||
priv: key,
|
priv: req.priv,
|
||||||
pub: encodeCrock(eddsaGetPublic(decodeCrock(key))),
|
pub: encodeCrock(eddsaGetPublic(decodeCrock(req.priv))),
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
unblindDenominationSignature(req: {
|
async unblindDenominationSignature(
|
||||||
planchet: PlanchetUnblindInfo;
|
tci: TalerCryptoInterfaceR,
|
||||||
evSig: BlindedDenominationSignature;
|
req: UnblindDenominationSignatureRequest,
|
||||||
}): UnblindedSignature {
|
): Promise<UnblindedSignature> {
|
||||||
if (req.evSig.cipher === DenomKeyType.Rsa) {
|
if (req.evSig.cipher === DenomKeyType.Rsa) {
|
||||||
if (req.planchet.denomPub.cipher !== DenomKeyType.Rsa) {
|
if (req.planchet.denomPub.cipher !== DenomKeyType.Rsa) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -450,32 +647,45 @@ export class CryptoImplementation {
|
|||||||
} else {
|
} else {
|
||||||
throw Error(`unblinding for cipher ${req.evSig.cipher} not implemented`);
|
throw Error(`unblinding for cipher ${req.evSig.cipher} not implemented`);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unblind a blindly signed value.
|
* Unblind a blindly signed value.
|
||||||
*/
|
*/
|
||||||
rsaUnblind(blindedSig: string, bk: string, pk: string): string {
|
async rsaUnblind(
|
||||||
|
tci: TalerCryptoInterfaceR,
|
||||||
|
req: RsaUnblindRequest,
|
||||||
|
): Promise<RsaUnblindResponse> {
|
||||||
const denomSig = rsaUnblind(
|
const denomSig = rsaUnblind(
|
||||||
decodeCrock(blindedSig),
|
decodeCrock(req.blindedSig),
|
||||||
decodeCrock(pk),
|
decodeCrock(req.pk),
|
||||||
decodeCrock(bk),
|
decodeCrock(req.bk),
|
||||||
);
|
);
|
||||||
return encodeCrock(denomSig);
|
return { sig: encodeCrock(denomSig) };
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unblind a blindly signed value.
|
* Unblind a blindly signed value.
|
||||||
*/
|
*/
|
||||||
rsaVerify(hm: string, sig: string, pk: string): boolean {
|
async rsaVerify(
|
||||||
return rsaVerify(hash(decodeCrock(hm)), decodeCrock(sig), decodeCrock(pk));
|
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)
|
* Generate updated coins (to store in the database)
|
||||||
* and deposit permissions for each given coin.
|
* and deposit permissions for each given coin.
|
||||||
*/
|
*/
|
||||||
async signDepositPermission(
|
async signDepositPermission(
|
||||||
|
tci: TalerCryptoInterfaceR,
|
||||||
depositInfo: DepositInfo,
|
depositInfo: DepositInfo,
|
||||||
): Promise<CoinDepositPermission> {
|
): Promise<CoinDepositPermission> {
|
||||||
// FIXME: put extensions here if used
|
// FIXME: put extensions here if used
|
||||||
@ -498,7 +708,7 @@ export class CryptoImplementation {
|
|||||||
} else {
|
} else {
|
||||||
throw Error("unsupported exchange protocol version");
|
throw Error("unsupported exchange protocol version");
|
||||||
}
|
}
|
||||||
const coinSigRes = await myEddsaSign(this.primitiveWorker, {
|
const coinSigRes = await this.eddsaSign(tci, {
|
||||||
msg: encodeCrock(d),
|
msg: encodeCrock(d),
|
||||||
priv: depositInfo.coinPriv,
|
priv: depositInfo.coinPriv,
|
||||||
});
|
});
|
||||||
@ -521,9 +731,10 @@ export class CryptoImplementation {
|
|||||||
`unsupported denomination cipher (${depositInfo.denomKeyType})`,
|
`unsupported denomination cipher (${depositInfo.denomKeyType})`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
async deriveRefreshSession(
|
async deriveRefreshSession(
|
||||||
|
tci: TalerCryptoInterfaceR,
|
||||||
req: DeriveRefreshSessionRequest,
|
req: DeriveRefreshSessionRequest,
|
||||||
): Promise<DerivedRefreshSession> {
|
): Promise<DerivedRefreshSession> {
|
||||||
const {
|
const {
|
||||||
@ -596,24 +807,14 @@ export class CryptoImplementation {
|
|||||||
let coinPub: Uint8Array;
|
let coinPub: Uint8Array;
|
||||||
let coinPriv: Uint8Array;
|
let coinPriv: Uint8Array;
|
||||||
let blindingFactor: Uint8Array;
|
let blindingFactor: Uint8Array;
|
||||||
// disabled while not implemented in the C code
|
// FIXME: make setupRefreshPlanchet a crypto api fn
|
||||||
if (0 && this.primitiveWorker) {
|
let fresh: FreshCoin = setupRefreshPlanchet(
|
||||||
const r = await this.primitiveWorker.setupRefreshPlanchet({
|
transferSecret,
|
||||||
transfer_secret: encodeCrock(transferSecret),
|
coinIndex,
|
||||||
coin_index: coinIndex,
|
);
|
||||||
});
|
coinPriv = fresh.coinPriv;
|
||||||
coinPub = decodeCrock(r.coin_pub);
|
coinPub = fresh.coinPub;
|
||||||
coinPriv = decodeCrock(r.coin_priv);
|
blindingFactor = fresh.bks;
|
||||||
blindingFactor = decodeCrock(r.blinding_key);
|
|
||||||
} else {
|
|
||||||
let fresh: FreshCoin = setupRefreshPlanchet(
|
|
||||||
transferSecret,
|
|
||||||
coinIndex,
|
|
||||||
);
|
|
||||||
coinPriv = fresh.coinPriv;
|
|
||||||
coinPub = fresh.coinPub;
|
|
||||||
blindingFactor = fresh.bks;
|
|
||||||
}
|
|
||||||
const coinPubHash = hash(coinPub);
|
const coinPubHash = hash(coinPub);
|
||||||
if (denomSel.denomPub.cipher !== DenomKeyType.Rsa) {
|
if (denomSel.denomPub.cipher !== DenomKeyType.Rsa) {
|
||||||
throw Error("unsupported cipher, can't create refresh session");
|
throw Error("unsupported cipher, can't create refresh session");
|
||||||
@ -654,7 +855,7 @@ export class CryptoImplementation {
|
|||||||
.put(amountToBuffer(meltFee))
|
.put(amountToBuffer(meltFee))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
const confirmSigResp = await myEddsaSign(this.primitiveWorker, {
|
const confirmSigResp = await tci.eddsaSign(tci, {
|
||||||
msg: encodeCrock(confirmData),
|
msg: encodeCrock(confirmData),
|
||||||
priv: meltCoinPriv,
|
priv: meltCoinPriv,
|
||||||
});
|
});
|
||||||
@ -670,102 +871,42 @@ export class CryptoImplementation {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return refreshSession;
|
return refreshSession;
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hash a string including the zero terminator.
|
* Hash a string including the zero terminator.
|
||||||
*/
|
*/
|
||||||
hashString(str: string): string {
|
async hashString(
|
||||||
const b = stringToBytes(str + "\0");
|
tci: TalerCryptoInterfaceR,
|
||||||
return encodeCrock(hash(b));
|
req: HashStringRequest,
|
||||||
}
|
): Promise<HashStringResult> {
|
||||||
|
const b = stringToBytes(req.str + "\0");
|
||||||
/**
|
return { h: encodeCrock(hash(b)) };
|
||||||
* Hash a crockford encoded value.
|
},
|
||||||
*/
|
|
||||||
hashEncoded(encodedBytes: string): string {
|
|
||||||
return encodeCrock(hash(decodeCrock(encodedBytes)));
|
|
||||||
}
|
|
||||||
|
|
||||||
async signCoinLink(
|
async signCoinLink(
|
||||||
oldCoinPriv: string,
|
tci: TalerCryptoInterfaceR,
|
||||||
newDenomHash: string,
|
req: SignCoinLinkRequest,
|
||||||
oldCoinPub: string,
|
): Promise<EddsaSigningResult> {
|
||||||
transferPub: string,
|
const coinEvHash = hashCoinEv(req.coinEv, req.newDenomHash);
|
||||||
coinEv: CoinEnvelope,
|
|
||||||
): Promise<string> {
|
|
||||||
const coinEvHash = hashCoinEv(coinEv, newDenomHash);
|
|
||||||
// FIXME: fill in
|
// FIXME: fill in
|
||||||
const hAgeCommitment = new Uint8Array(32);
|
const hAgeCommitment = new Uint8Array(32);
|
||||||
const coinLink = buildSigPS(TalerSignaturePurpose.WALLET_COIN_LINK)
|
const coinLink = buildSigPS(TalerSignaturePurpose.WALLET_COIN_LINK)
|
||||||
.put(decodeCrock(newDenomHash))
|
.put(decodeCrock(req.newDenomHash))
|
||||||
.put(decodeCrock(transferPub))
|
.put(decodeCrock(req.transferPub))
|
||||||
.put(hAgeCommitment)
|
.put(hAgeCommitment)
|
||||||
.put(coinEvHash)
|
.put(coinEvHash)
|
||||||
.build();
|
.build();
|
||||||
const sig = await myEddsaSign(this.primitiveWorker, {
|
return tci.eddsaSign(tci, {
|
||||||
msg: encodeCrock(coinLink),
|
msg: encodeCrock(coinLink),
|
||||||
priv: oldCoinPriv,
|
priv: req.oldCoinPriv,
|
||||||
});
|
});
|
||||||
return sig.sig;
|
},
|
||||||
}
|
|
||||||
|
|
||||||
benchmark(repetitions: number): BenchmarkResult {
|
async makeSyncSignature(
|
||||||
let time_hash = BigInt(0);
|
tci: TalerCryptoInterfaceR,
|
||||||
for (let i = 0; i < repetitions; i++) {
|
req: MakeSyncSignatureRequest,
|
||||||
const start = timer.performanceNow();
|
): Promise<EddsaSigningResult> {
|
||||||
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 {
|
|
||||||
const hNew = decodeCrock(req.newHash);
|
const hNew = decodeCrock(req.newHash);
|
||||||
let hOld: Uint8Array;
|
let hOld: Uint8Array;
|
||||||
if (req.oldHash) {
|
if (req.oldHash) {
|
||||||
@ -778,6 +919,64 @@ export class CryptoImplementation {
|
|||||||
.put(hNew)
|
.put(hNew)
|
||||||
.build();
|
.build();
|
||||||
const uploadSig = eddsaSign(sigBlob, decodeCrock(req.accountPriv));
|
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 {
|
import {
|
||||||
AmountJson,
|
AmountJson,
|
||||||
AmountString,
|
|
||||||
CoinEnvelope,
|
CoinEnvelope,
|
||||||
DenominationPubKey,
|
DenominationPubKey,
|
||||||
ExchangeProtocolVersion,
|
ExchangeProtocolVersion,
|
||||||
|
@ -22,39 +22,10 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* 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 { Logger } from "@gnu-taler/taler-util";
|
||||||
import {
|
import * as timer from "../../util/timer.js";
|
||||||
CreateRecoupRefreshReqRequest,
|
import { nullCrypto, TalerCryptoInterface } from "../cryptoImplementation.js";
|
||||||
CreateRecoupReqRequest,
|
import { CryptoWorker } from "./cryptoWorkerInterface.js";
|
||||||
DerivedRefreshSession,
|
|
||||||
DerivedTipPlanchet,
|
|
||||||
DeriveRefreshSessionRequest,
|
|
||||||
DeriveTipRequest,
|
|
||||||
SignTrackTransactionRequest,
|
|
||||||
} from "../cryptoTypes.js";
|
|
||||||
|
|
||||||
const logger = new Logger("cryptoApi.ts");
|
const logger = new Logger("cryptoApi.ts");
|
||||||
|
|
||||||
@ -80,7 +51,7 @@ interface WorkerState {
|
|||||||
|
|
||||||
interface WorkItem {
|
interface WorkItem {
|
||||||
operation: string;
|
operation: string;
|
||||||
args: any[];
|
req: unknown;
|
||||||
resolve: any;
|
resolve: any;
|
||||||
reject: any;
|
reject: any;
|
||||||
|
|
||||||
@ -122,10 +93,9 @@ export class CryptoApiStoppedError extends Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Crypto API that interfaces manages a background crypto thread
|
* Dispatcher for cryptographic operations to underlying crypto workers.
|
||||||
* for the execution of expensive operations.
|
|
||||||
*/
|
*/
|
||||||
export class CryptoApi {
|
export class CryptoDispatcher {
|
||||||
private nextRpcId = 1;
|
private nextRpcId = 1;
|
||||||
private workers: WorkerState[];
|
private workers: WorkerState[];
|
||||||
private workQueues: WorkItem[][];
|
private workQueues: WorkItem[][];
|
||||||
@ -191,7 +161,7 @@ export class CryptoApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const msg: any = {
|
const msg: any = {
|
||||||
args: work.args,
|
req: work.req,
|
||||||
id: work.rpcId,
|
id: work.rpcId,
|
||||||
operation: work.operation,
|
operation: work.operation,
|
||||||
};
|
};
|
||||||
@ -277,7 +247,16 @@ export class CryptoApi {
|
|||||||
currentWorkItem.resolve(msg.data.result);
|
currentWorkItem.resolve(msg.data.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cryptoApi: TalerCryptoInterface;
|
||||||
|
|
||||||
constructor(workerFactory: CryptoWorkerFactory) {
|
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.workerFactory = workerFactory;
|
||||||
this.workers = new Array<WorkerState>(workerFactory.getConcurrency());
|
this.workers = new Array<WorkerState>(workerFactory.getConcurrency());
|
||||||
|
|
||||||
@ -298,7 +277,7 @@ export class CryptoApi {
|
|||||||
private doRpc<T>(
|
private doRpc<T>(
|
||||||
operation: string,
|
operation: string,
|
||||||
priority: number,
|
priority: number,
|
||||||
...args: any[]
|
req: unknown,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
if (this.stopped) {
|
if (this.stopped) {
|
||||||
throw new CryptoApiStoppedError();
|
throw new CryptoApiStoppedError();
|
||||||
@ -307,7 +286,7 @@ export class CryptoApi {
|
|||||||
const rpcId = this.nextRpcId++;
|
const rpcId = this.nextRpcId++;
|
||||||
const workItem: WorkItem = {
|
const workItem: WorkItem = {
|
||||||
operation,
|
operation,
|
||||||
args,
|
req,
|
||||||
resolve,
|
resolve,
|
||||||
reject,
|
reject,
|
||||||
rpcId,
|
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
|
* Imports
|
||||||
*/
|
*/
|
||||||
import { CryptoWorkerFactory } from "./cryptoApi.js";
|
import { CryptoWorkerFactory } from "./cryptoDispatcher.js";
|
||||||
import { CryptoWorker } from "./cryptoWorkerInterface.js";
|
import { CryptoWorker } from "./cryptoWorkerInterface.js";
|
||||||
import os from "os";
|
import os from "os";
|
||||||
import { CryptoImplementation } from "./cryptoImplementation.js";
|
|
||||||
import { Logger } from "@gnu-taler/taler-util";
|
import { Logger } from "@gnu-taler/taler-util";
|
||||||
|
import { nativeCryptoR } from "../cryptoImplementation.js";
|
||||||
|
|
||||||
const logger = new Logger("nodeThreadWorker.ts");
|
const logger = new Logger("nodeThreadWorker.ts");
|
||||||
|
|
||||||
@ -69,9 +69,9 @@ const workerCode = `
|
|||||||
* a message.
|
* a message.
|
||||||
*/
|
*/
|
||||||
export function handleWorkerMessage(msg: any): void {
|
export function handleWorkerMessage(msg: any): void {
|
||||||
const args = msg.args;
|
const req = msg.req;
|
||||||
if (!Array.isArray(args)) {
|
if (typeof req !== "object") {
|
||||||
console.error("args must be array");
|
console.error("request must be an object");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const id = msg.id;
|
const id = msg.id;
|
||||||
@ -86,7 +86,7 @@ export function handleWorkerMessage(msg: any): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleRequest = async (): Promise<void> => {
|
const handleRequest = async (): Promise<void> => {
|
||||||
const impl = new CryptoImplementation();
|
const impl = nativeCryptoR;
|
||||||
|
|
||||||
if (!(operation in impl)) {
|
if (!(operation in impl)) {
|
||||||
console.error(`crypto operation '${operation}' not found`);
|
console.error(`crypto operation '${operation}' not found`);
|
||||||
@ -94,12 +94,11 @@ export function handleWorkerMessage(msg: any): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const _r = "require";
|
const _r = "require";
|
||||||
const worker_threads: typeof import("worker_threads") = module[_r](
|
const worker_threads: typeof import("worker_threads") =
|
||||||
"worker_threads",
|
module[_r]("worker_threads");
|
||||||
);
|
|
||||||
// const worker_threads = require("worker_threads");
|
// const worker_threads = require("worker_threads");
|
||||||
|
|
||||||
const p = worker_threads.parentPort;
|
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 { Logger } from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
CryptoImplementation,
|
nativeCryptoR,
|
||||||
PrimitiveWorker
|
TalerCryptoInterfaceR,
|
||||||
} from "./cryptoImplementation.js";
|
} from "../cryptoImplementation.js";
|
||||||
|
import { CryptoRpcClient } from "./rpcClient.js";
|
||||||
|
|
||||||
|
|
||||||
const logger = new Logger("synchronousWorker.ts");
|
const logger = new Logger("synchronousWorker.ts");
|
||||||
|
|
||||||
@ -38,9 +37,33 @@ export class SynchronousCryptoWorker {
|
|||||||
*/
|
*/
|
||||||
onerror: undefined | ((m: any) => void);
|
onerror: undefined | ((m: any) => void);
|
||||||
|
|
||||||
constructor(private primitiveWorker?: PrimitiveWorker) {
|
cryptoImplR: TalerCryptoInterfaceR;
|
||||||
|
|
||||||
|
rpcClient: CryptoRpcClient | undefined;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
this.onerror = undefined;
|
this.onerror = undefined;
|
||||||
this.onmessage = 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(
|
private async handleRequest(
|
||||||
operation: string,
|
operation: string,
|
||||||
id: number,
|
id: number,
|
||||||
args: string[],
|
req: unknown,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const impl = new CryptoImplementation(this.primitiveWorker);
|
const impl = this.cryptoImplR;
|
||||||
|
|
||||||
if (!(operation in impl)) {
|
if (!(operation in impl)) {
|
||||||
console.error(`crypto operation '${operation}' not found`);
|
console.error(`crypto operation '${operation}' not found`);
|
||||||
@ -77,7 +100,7 @@ export class SynchronousCryptoWorker {
|
|||||||
|
|
||||||
let result: any;
|
let result: any;
|
||||||
try {
|
try {
|
||||||
result = await (impl as any)[operation](...args);
|
result = await (impl as any)[operation](impl, req);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error("error during operation", e);
|
logger.error("error during operation", e);
|
||||||
return;
|
return;
|
||||||
@ -94,9 +117,9 @@ export class SynchronousCryptoWorker {
|
|||||||
* Send a message to the worker thread.
|
* Send a message to the worker thread.
|
||||||
*/
|
*/
|
||||||
postMessage(msg: any): void {
|
postMessage(msg: any): void {
|
||||||
const args = msg.args;
|
const req = msg.req;
|
||||||
if (!Array.isArray(args)) {
|
if (typeof req !== "object") {
|
||||||
console.error("args must be array");
|
console.error("request must be an object");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const id = msg.id;
|
const id = msg.id;
|
||||||
@ -110,7 +133,7 @@ export class SynchronousCryptoWorker {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleRequest(operation, id, args).catch((e) => {
|
this.handleRequest(operation, id, req).catch((e) => {
|
||||||
console.error("Error while handling crypto request:", 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/>
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
/**
|
||||||
PrimitiveWorker,
|
* Imports.
|
||||||
} from "./cryptoImplementation.js";
|
*/
|
||||||
|
import { CryptoWorkerFactory } from "./cryptoDispatcher.js";
|
||||||
import { CryptoWorkerFactory } from "./cryptoApi.js";
|
|
||||||
import { CryptoWorker } from "./cryptoWorkerInterface.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";
|
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
|
* The synchronous crypto worker produced by this factory doesn't run in the
|
||||||
* background, but actually blocks the caller until the operation is done.
|
* 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");
|
throw Error("cannot make worker, require(...) not defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
let primitiveWorker;
|
return new SynchronousCryptoWorker();
|
||||||
if (process.env["TALER_WALLET_PRIMITIVE_WORKER"]) {
|
|
||||||
primitiveWorker = new MyPrimitiveWorker();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SynchronousCryptoWorker(primitiveWorker);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getConcurrency(): number {
|
getConcurrency(): number {
|
||||||
|
@ -47,10 +47,10 @@ import {
|
|||||||
AbsoluteTime,
|
AbsoluteTime,
|
||||||
UnblindedSignature,
|
UnblindedSignature,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
|
import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
|
||||||
import { DenominationRecord } from "./db.js";
|
import { DenominationRecord } from "./db.js";
|
||||||
import {
|
import {
|
||||||
assembleRefreshRevealRequest,
|
assembleRefreshRevealRequest,
|
||||||
CryptoApi,
|
|
||||||
ExchangeInfo,
|
ExchangeInfo,
|
||||||
getBankWithdrawalInfo,
|
getBankWithdrawalInfo,
|
||||||
HttpRequestLibrary,
|
HttpRequestLibrary,
|
||||||
@ -149,7 +149,7 @@ export async function topupReserveWithDemobank(
|
|||||||
|
|
||||||
export async function withdrawCoin(args: {
|
export async function withdrawCoin(args: {
|
||||||
http: HttpRequestLibrary;
|
http: HttpRequestLibrary;
|
||||||
cryptoApi: CryptoApi;
|
cryptoApi: TalerCryptoInterface;
|
||||||
reserveKeyPair: ReserveKeypair;
|
reserveKeyPair: ReserveKeypair;
|
||||||
denom: DenominationRecord;
|
denom: DenominationRecord;
|
||||||
exchangeBaseUrl: string;
|
exchangeBaseUrl: string;
|
||||||
@ -212,7 +212,7 @@ export function findDenomOrThrow(
|
|||||||
|
|
||||||
export async function depositCoin(args: {
|
export async function depositCoin(args: {
|
||||||
http: HttpRequestLibrary;
|
http: HttpRequestLibrary;
|
||||||
cryptoApi: CryptoApi;
|
cryptoApi: TalerCryptoInterface;
|
||||||
exchangeBaseUrl: string;
|
exchangeBaseUrl: string;
|
||||||
coin: CoinInfo;
|
coin: CoinInfo;
|
||||||
amount: AmountString;
|
amount: AmountString;
|
||||||
@ -263,7 +263,7 @@ export async function depositCoin(args: {
|
|||||||
|
|
||||||
export async function refreshCoin(req: {
|
export async function refreshCoin(req: {
|
||||||
http: HttpRequestLibrary;
|
http: HttpRequestLibrary;
|
||||||
cryptoApi: CryptoApi;
|
cryptoApi: TalerCryptoInterface;
|
||||||
oldCoin: CoinInfo;
|
oldCoin: CoinInfo;
|
||||||
newDenoms: DenominationRecord[];
|
newDenoms: DenominationRecord[];
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
|
@ -25,7 +25,9 @@
|
|||||||
import type { IDBFactory } from "@gnu-taler/idb-bridge";
|
import type { IDBFactory } from "@gnu-taler/idb-bridge";
|
||||||
// eslint-disable-next-line no-duplicate-imports
|
// eslint-disable-next-line no-duplicate-imports
|
||||||
import {
|
import {
|
||||||
BridgeIDBFactory, MemoryBackend, shimIndexedDB
|
BridgeIDBFactory,
|
||||||
|
MemoryBackend,
|
||||||
|
shimIndexedDB,
|
||||||
} from "@gnu-taler/idb-bridge";
|
} from "@gnu-taler/idb-bridge";
|
||||||
import { AccessStats } from "@gnu-taler/idb-bridge/src/MemoryBackend";
|
import { AccessStats } from "@gnu-taler/idb-bridge/src/MemoryBackend";
|
||||||
import { Logger, WalletNotification } from "@gnu-taler/taler-util";
|
import { Logger, WalletNotification } from "@gnu-taler/taler-util";
|
||||||
|
@ -33,9 +33,11 @@ export * from "./db-utils.js";
|
|||||||
|
|
||||||
// Crypto and crypto workers
|
// Crypto and crypto workers
|
||||||
// export * from "./crypto/workers/nodeThreadWorker.js";
|
// export * from "./crypto/workers/nodeThreadWorker.js";
|
||||||
export { CryptoImplementation } from "./crypto/workers/cryptoImplementation.js";
|
|
||||||
export type { CryptoWorker } from "./crypto/workers/cryptoWorkerInterface.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 { SynchronousCryptoWorker } from "./crypto/workers/synchronousWorker.js";
|
||||||
|
|
||||||
export * from "./pending-types.js";
|
export * from "./pending-types.js";
|
||||||
@ -58,3 +60,8 @@ export * from "./operations/refresh.js";
|
|||||||
|
|
||||||
export * from "./dbless.js";
|
export * from "./dbless.js";
|
||||||
|
|
||||||
|
export {
|
||||||
|
nativeCryptoR,
|
||||||
|
nativeCrypto,
|
||||||
|
nullCrypto,
|
||||||
|
} from "./crypto/cryptoImplementation.js";
|
||||||
|
@ -36,7 +36,8 @@ import {
|
|||||||
DenominationPubKey,
|
DenominationPubKey,
|
||||||
TalerProtocolTimestamp,
|
TalerProtocolTimestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} 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 { ExchangeDetailsRecord, ExchangeRecord, WalletStoresV1 } from "./db.js";
|
||||||
import { PendingOperationsResponse } from "./pending-types.js";
|
import { PendingOperationsResponse } from "./pending-types.js";
|
||||||
import { AsyncOpMemoMap, AsyncOpMemoSingle } from "./util/asyncMemo.js";
|
import { AsyncOpMemoMap, AsyncOpMemoSingle } from "./util/asyncMemo.js";
|
||||||
@ -200,7 +201,7 @@ export interface InternalWalletState {
|
|||||||
memoProcessRefresh: AsyncOpMemoMap<void>;
|
memoProcessRefresh: AsyncOpMemoMap<void>;
|
||||||
memoProcessRecoup: AsyncOpMemoMap<void>;
|
memoProcessRecoup: AsyncOpMemoMap<void>;
|
||||||
memoProcessDeposit: AsyncOpMemoMap<void>;
|
memoProcessDeposit: AsyncOpMemoMap<void>;
|
||||||
cryptoApi: CryptoApi;
|
cryptoApi: TalerCryptoInterface;
|
||||||
|
|
||||||
timerGroup: TimerGroup;
|
timerGroup: TimerGroup;
|
||||||
stopped: boolean;
|
stopped: boolean;
|
||||||
|
@ -69,7 +69,7 @@ import {
|
|||||||
rsaBlind,
|
rsaBlind,
|
||||||
stringToBytes,
|
stringToBytes,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { CryptoApi } from "../../crypto/workers/cryptoApi.js";
|
import { CryptoDispatcher } from "../../crypto/workers/cryptoDispatcher.js";
|
||||||
import {
|
import {
|
||||||
BackupProviderRecord,
|
BackupProviderRecord,
|
||||||
BackupProviderState,
|
BackupProviderState,
|
||||||
@ -99,6 +99,7 @@ import { exportBackup } from "./export.js";
|
|||||||
import { BackupCryptoPrecomputedData, importBackup } from "./import.js";
|
import { BackupCryptoPrecomputedData, importBackup } from "./import.js";
|
||||||
import { getWalletBackupState, provideBackupState } from "./state.js";
|
import { getWalletBackupState, provideBackupState } from "./state.js";
|
||||||
import { guardOperationException } from "../common.js";
|
import { guardOperationException } from "../common.js";
|
||||||
|
import { TalerCryptoInterface } from "../../crypto/cryptoImplementation.js";
|
||||||
|
|
||||||
const logger = new Logger("operations/backup.ts");
|
const logger = new Logger("operations/backup.ts");
|
||||||
|
|
||||||
@ -154,7 +155,7 @@ export async function encryptBackup(
|
|||||||
* FIXME: Move computations into crypto worker.
|
* FIXME: Move computations into crypto worker.
|
||||||
*/
|
*/
|
||||||
async function computeBackupCryptoData(
|
async function computeBackupCryptoData(
|
||||||
cryptoApi: CryptoApi,
|
cryptoApi: TalerCryptoInterface,
|
||||||
backupContent: WalletBackupContentV1,
|
backupContent: WalletBackupContentV1,
|
||||||
): Promise<BackupCryptoPrecomputedData> {
|
): Promise<BackupCryptoPrecomputedData> {
|
||||||
const cryptoData: BackupCryptoPrecomputedData = {
|
const cryptoData: BackupCryptoPrecomputedData = {
|
||||||
@ -193,18 +194,18 @@ async function computeBackupCryptoData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const prop of backupContent.proposals) {
|
for (const prop of backupContent.proposals) {
|
||||||
const contractTermsHash = await cryptoApi.hashString(
|
const { h: contractTermsHash } = await cryptoApi.hashString({
|
||||||
canonicalJson(prop.contract_terms_raw),
|
str: canonicalJson(prop.contract_terms_raw),
|
||||||
);
|
});
|
||||||
const noncePub = encodeCrock(eddsaGetPublic(decodeCrock(prop.nonce_priv)));
|
const noncePub = encodeCrock(eddsaGetPublic(decodeCrock(prop.nonce_priv)));
|
||||||
cryptoData.proposalNoncePrivToPub[prop.nonce_priv] = noncePub;
|
cryptoData.proposalNoncePrivToPub[prop.nonce_priv] = noncePub;
|
||||||
cryptoData.proposalIdToContractTermsHash[prop.proposal_id] =
|
cryptoData.proposalIdToContractTermsHash[prop.proposal_id] =
|
||||||
contractTermsHash;
|
contractTermsHash;
|
||||||
}
|
}
|
||||||
for (const purch of backupContent.purchases) {
|
for (const purch of backupContent.purchases) {
|
||||||
const contractTermsHash = await cryptoApi.hashString(
|
const { h: contractTermsHash } = await cryptoApi.hashString({
|
||||||
canonicalJson(purch.contract_terms_raw),
|
str: canonicalJson(purch.contract_terms_raw),
|
||||||
);
|
});
|
||||||
const noncePub = encodeCrock(eddsaGetPublic(decodeCrock(purch.nonce_priv)));
|
const noncePub = encodeCrock(eddsaGetPublic(decodeCrock(purch.nonce_priv)));
|
||||||
cryptoData.proposalNoncePrivToPub[purch.nonce_priv] = noncePub;
|
cryptoData.proposalNoncePrivToPub[purch.nonce_priv] = noncePub;
|
||||||
cryptoData.proposalIdToContractTermsHash[purch.proposal_id] =
|
cryptoData.proposalIdToContractTermsHash[purch.proposal_id] =
|
||||||
@ -286,13 +287,13 @@ async function runBackupCycleForProvider(
|
|||||||
logger.trace(`trying to upload backup to ${provider.baseUrl}`);
|
logger.trace(`trying to upload backup to ${provider.baseUrl}`);
|
||||||
logger.trace(`old hash ${oldHash}, new hash ${newHash}`);
|
logger.trace(`old hash ${oldHash}, new hash ${newHash}`);
|
||||||
|
|
||||||
const syncSig = await ws.cryptoApi.makeSyncSignature({
|
const syncSigResp = await ws.cryptoApi.makeSyncSignature({
|
||||||
newHash: encodeCrock(currentBackupHash),
|
newHash: encodeCrock(currentBackupHash),
|
||||||
oldHash: provider.lastBackupHash,
|
oldHash: provider.lastBackupHash,
|
||||||
accountPriv: encodeCrock(accountKeyPair.eddsaPriv),
|
accountPriv: encodeCrock(accountKeyPair.eddsaPriv),
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.trace(`sync signature is ${syncSig}`);
|
logger.trace(`sync signature is ${syncSigResp}`);
|
||||||
|
|
||||||
const accountBackupUrl = new URL(
|
const accountBackupUrl = new URL(
|
||||||
`/backups/${encodeCrock(accountKeyPair.eddsaPub)}`,
|
`/backups/${encodeCrock(accountKeyPair.eddsaPub)}`,
|
||||||
@ -304,7 +305,7 @@ async function runBackupCycleForProvider(
|
|||||||
body: encBackup,
|
body: encBackup,
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/octet-stream",
|
"content-type": "application/octet-stream",
|
||||||
"sync-signature": syncSig,
|
"sync-signature": syncSigResp.sig,
|
||||||
"if-none-match": newHash,
|
"if-none-match": newHash,
|
||||||
...(provider.lastBackupHash
|
...(provider.lastBackupHash
|
||||||
? {
|
? {
|
||||||
|
@ -41,7 +41,7 @@ export async function provideBackupState(
|
|||||||
}
|
}
|
||||||
// We need to generate the key outside of the transaction
|
// We need to generate the key outside of the transaction
|
||||||
// due to how IndexedDB works.
|
// due to how IndexedDB works.
|
||||||
const k = await ws.cryptoApi.createEddsaKeypair();
|
const k = await ws.cryptoApi.createEddsaKeypair({});
|
||||||
const d = getRandomBytes(5);
|
const d = getRandomBytes(5);
|
||||||
// FIXME: device ID should be configured when wallet is initialized
|
// FIXME: device ID should be configured when wallet is initialized
|
||||||
// and be based on hostname
|
// and be based on hostname
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { TalerErrorDetail, TalerErrorCode } from "@gnu-taler/taler-util";
|
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";
|
import { TalerError, getErrorDetailFromException } from "../errors.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -254,14 +254,14 @@ export async function trackDepositGroup(
|
|||||||
`deposits/${wireHash}/${depositGroup.merchantPub}/${depositGroup.contractTermsHash}/${dp.coin_pub}`,
|
`deposits/${wireHash}/${depositGroup.merchantPub}/${depositGroup.contractTermsHash}/${dp.coin_pub}`,
|
||||||
dp.exchange_url,
|
dp.exchange_url,
|
||||||
);
|
);
|
||||||
const sig = await ws.cryptoApi.signTrackTransaction({
|
const sigResp = await ws.cryptoApi.signTrackTransaction({
|
||||||
coinPub: dp.coin_pub,
|
coinPub: dp.coin_pub,
|
||||||
contractTermsHash: depositGroup.contractTermsHash,
|
contractTermsHash: depositGroup.contractTermsHash,
|
||||||
merchantPriv: depositGroup.merchantPriv,
|
merchantPriv: depositGroup.merchantPriv,
|
||||||
merchantPub: depositGroup.merchantPub,
|
merchantPub: depositGroup.merchantPub,
|
||||||
wireHash,
|
wireHash,
|
||||||
});
|
});
|
||||||
url.searchParams.set("merchant_sig", sig);
|
url.searchParams.set("merchant_sig", sigResp.sig);
|
||||||
const httpResp = await ws.http.get(url.href);
|
const httpResp = await ws.http.get(url.href);
|
||||||
const body = await httpResp.json();
|
const body = await httpResp.json();
|
||||||
responses.push({
|
responses.push({
|
||||||
@ -391,8 +391,8 @@ export async function createDepositGroup(
|
|||||||
|
|
||||||
const now = AbsoluteTime.now();
|
const now = AbsoluteTime.now();
|
||||||
const nowRounded = AbsoluteTime.toTimestamp(now);
|
const nowRounded = AbsoluteTime.toTimestamp(now);
|
||||||
const noncePair = await ws.cryptoApi.createEddsaKeypair();
|
const noncePair = await ws.cryptoApi.createEddsaKeypair({});
|
||||||
const merchantPair = await ws.cryptoApi.createEddsaKeypair();
|
const merchantPair = await ws.cryptoApi.createEddsaKeypair({});
|
||||||
const wireSalt = encodeCrock(getRandomBytes(16));
|
const wireSalt = encodeCrock(getRandomBytes(16));
|
||||||
const wireHash = hashWire(req.depositPaytoUri, wireSalt);
|
const wireHash = hashWire(req.depositPaytoUri, wireSalt);
|
||||||
const contractTerms: ContractTerms = {
|
const contractTerms: ContractTerms = {
|
||||||
@ -421,9 +421,9 @@ export async function createDepositGroup(
|
|||||||
refund_deadline: TalerProtocolTimestamp.zero(),
|
refund_deadline: TalerProtocolTimestamp.zero(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const contractTermsHash = await ws.cryptoApi.hashString(
|
const { h: contractTermsHash } = await ws.cryptoApi.hashString({
|
||||||
canonicalJson(contractTerms),
|
str: canonicalJson(contractTerms),
|
||||||
);
|
});
|
||||||
|
|
||||||
const contractData = extractContractData(
|
const contractData = extractContractData(
|
||||||
contractTerms,
|
contractTerms,
|
||||||
|
@ -46,7 +46,7 @@ import {
|
|||||||
TalerProtocolDuration,
|
TalerProtocolDuration,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { decodeCrock, encodeCrock, hash } 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 {
|
import {
|
||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
DenominationVerificationStatus,
|
DenominationVerificationStatus,
|
||||||
@ -243,12 +243,13 @@ async function validateWireInfo(
|
|||||||
if (ws.insecureTrustExchange) {
|
if (ws.insecureTrustExchange) {
|
||||||
isValid = true;
|
isValid = true;
|
||||||
} else {
|
} else {
|
||||||
isValid = await ws.cryptoApi.isValidWireAccount(
|
const { valid: v } = await ws.cryptoApi.isValidWireAccount({
|
||||||
|
masterPub: masterPublicKey,
|
||||||
|
paytoUri: a.payto_uri,
|
||||||
|
sig: a.master_sig,
|
||||||
versionCurrent,
|
versionCurrent,
|
||||||
a.payto_uri,
|
});
|
||||||
a.master_sig,
|
isValid = v;
|
||||||
masterPublicKey,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
throw Error("exchange acct signature invalid");
|
throw Error("exchange acct signature invalid");
|
||||||
@ -272,11 +273,12 @@ async function validateWireInfo(
|
|||||||
if (ws.insecureTrustExchange) {
|
if (ws.insecureTrustExchange) {
|
||||||
isValid = true;
|
isValid = true;
|
||||||
} else {
|
} else {
|
||||||
isValid = await ws.cryptoApi.isValidWireFee(
|
const { valid: v } = await ws.cryptoApi.isValidWireFee({
|
||||||
wireMethod,
|
masterPub: masterPublicKey,
|
||||||
fee,
|
type: wireMethod,
|
||||||
masterPublicKey,
|
wf: fee,
|
||||||
);
|
});
|
||||||
|
isValid = v;
|
||||||
}
|
}
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
throw Error("exchange wire fee signature invalid");
|
throw Error("exchange wire fee signature invalid");
|
||||||
|
@ -55,7 +55,10 @@ import {
|
|||||||
TransactionType,
|
TransactionType,
|
||||||
URL,
|
URL,
|
||||||
} from "@gnu-taler/taler-util";
|
} 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 {
|
import {
|
||||||
AbortStatus,
|
AbortStatus,
|
||||||
AllowedAuditorInfo,
|
AllowedAuditorInfo,
|
||||||
@ -100,6 +103,7 @@ import {
|
|||||||
import { getExchangeDetails } from "./exchanges.js";
|
import { getExchangeDetails } from "./exchanges.js";
|
||||||
import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js";
|
import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js";
|
||||||
import { guardOperationException } from "./common.js";
|
import { guardOperationException } from "./common.js";
|
||||||
|
import { EddsaKeypair } from "../crypto/cryptoImplementation.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logger.
|
* Logger.
|
||||||
@ -795,11 +799,11 @@ async function processDownloadProposalImpl(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sigValid = await ws.cryptoApi.isValidContractTermsSignature(
|
const sigValid = await ws.cryptoApi.isValidContractTermsSignature({
|
||||||
contractTermsHash,
|
contractTermsHash,
|
||||||
proposalResp.sig,
|
merchantPub: parsedContractTerms.merchant_pub,
|
||||||
parsedContractTerms.merchant_pub,
|
sig: proposalResp.sig,
|
||||||
);
|
});
|
||||||
|
|
||||||
if (!sigValid) {
|
if (!sigValid) {
|
||||||
const err = makeErrorDetail(
|
const err = makeErrorDetail(
|
||||||
@ -921,9 +925,14 @@ async function startDownloadProposal(
|
|||||||
return oldProposal.proposalId;
|
return oldProposal.proposalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { priv, pub } = await (noncePriv
|
let noncePair: EddsaKeypair;
|
||||||
? ws.cryptoApi.eddsaGetPublic(noncePriv)
|
if (noncePriv) {
|
||||||
: ws.cryptoApi.createEddsaKeypair());
|
noncePair = await ws.cryptoApi.eddsaGetPublic({ priv: noncePriv });
|
||||||
|
} else {
|
||||||
|
noncePair = await ws.cryptoApi.createEddsaKeypair({});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { priv, pub } = noncePair;
|
||||||
const proposalId = encodeCrock(getRandomBytes(32));
|
const proposalId = encodeCrock(getRandomBytes(32));
|
||||||
|
|
||||||
const proposalRecord: ProposalRecord = {
|
const proposalRecord: ProposalRecord = {
|
||||||
@ -1673,11 +1682,11 @@ async function processPurchasePayImpl(
|
|||||||
logger.trace("got success from pay URL", merchantResp);
|
logger.trace("got success from pay URL", merchantResp);
|
||||||
|
|
||||||
const merchantPub = purchase.download.contractData.merchantPub;
|
const merchantPub = purchase.download.contractData.merchantPub;
|
||||||
const valid: boolean = await ws.cryptoApi.isValidPaymentSignature(
|
const { valid } = await ws.cryptoApi.isValidPaymentSignature({
|
||||||
merchantResp.sig,
|
contractHash: purchase.download.contractData.contractTermsHash,
|
||||||
purchase.download.contractData.contractTermsHash,
|
|
||||||
merchantPub,
|
merchantPub,
|
||||||
);
|
sig: merchantResp.sig,
|
||||||
|
});
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
logger.error("merchant payment signature invalid");
|
logger.error("merchant payment signature invalid");
|
||||||
|
@ -76,9 +76,9 @@ import {
|
|||||||
RefreshNewDenomInfo,
|
RefreshNewDenomInfo,
|
||||||
} from "../crypto/cryptoTypes.js";
|
} from "../crypto/cryptoTypes.js";
|
||||||
import { GetReadWriteAccess } from "../util/query.js";
|
import { GetReadWriteAccess } from "../util/query.js";
|
||||||
import { CryptoApi } from "../index.browser.js";
|
|
||||||
import { guardOperationException } from "./common.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");
|
const logger = new Logger("refresh.ts");
|
||||||
|
|
||||||
@ -461,7 +461,7 @@ async function refreshMelt(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function assembleRefreshRevealRequest(args: {
|
export async function assembleRefreshRevealRequest(args: {
|
||||||
cryptoApi: CryptoApi;
|
cryptoApi: TalerCryptoInterface;
|
||||||
derived: DerivedRefreshSession;
|
derived: DerivedRefreshSession;
|
||||||
norevealIndex: number;
|
norevealIndex: number;
|
||||||
oldCoinPub: CoinPublicKeyString;
|
oldCoinPub: CoinPublicKeyString;
|
||||||
@ -494,14 +494,14 @@ export async function assembleRefreshRevealRequest(args: {
|
|||||||
const dsel = newDenoms[i];
|
const dsel = newDenoms[i];
|
||||||
for (let j = 0; j < dsel.count; j++) {
|
for (let j = 0; j < dsel.count; j++) {
|
||||||
const newCoinIndex = linkSigs.length;
|
const newCoinIndex = linkSigs.length;
|
||||||
const linkSig = await cryptoApi.signCoinLink(
|
const linkSig = await cryptoApi.signCoinLink({
|
||||||
oldCoinPriv,
|
coinEv: planchets[newCoinIndex].coinEv,
|
||||||
dsel.denomPubHash,
|
newDenomHash: dsel.denomPubHash,
|
||||||
oldCoinPub,
|
oldCoinPriv: oldCoinPriv,
|
||||||
derived.transferPubs[norevealIndex],
|
oldCoinPub: oldCoinPub,
|
||||||
planchets[newCoinIndex].coinEv,
|
transferPub: derived.transferPubs[norevealIndex],
|
||||||
);
|
});
|
||||||
linkSigs.push(linkSig);
|
linkSigs.push(linkSig.sig);
|
||||||
newDenomsFlat.push(dsel.denomPubHash);
|
newDenomsFlat.push(dsel.denomPubHash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@ export async function createReserve(
|
|||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
req: CreateReserveRequest,
|
req: CreateReserveRequest,
|
||||||
): Promise<CreateReserveResponse> {
|
): Promise<CreateReserveResponse> {
|
||||||
const keypair = await ws.cryptoApi.createEddsaKeypair();
|
const keypair = await ws.cryptoApi.createEddsaKeypair({});
|
||||||
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||||
const canonExchange = canonicalizeBaseUrl(req.exchange);
|
const canonExchange = canonicalizeBaseUrl(req.exchange);
|
||||||
|
|
||||||
|
@ -336,17 +336,17 @@ async function processTipImpl(
|
|||||||
throw Error("unsupported cipher");
|
throw Error("unsupported cipher");
|
||||||
}
|
}
|
||||||
|
|
||||||
const denomSigRsa = await ws.cryptoApi.rsaUnblind(
|
const denomSigRsa = await ws.cryptoApi.rsaUnblind({
|
||||||
blindedSig.blinded_rsa_signature,
|
bk: planchet.blindingKey,
|
||||||
planchet.blindingKey,
|
blindedSig: blindedSig.blinded_rsa_signature,
|
||||||
denom.denomPub.rsa_public_key,
|
pk: denom.denomPub.rsa_public_key,
|
||||||
);
|
});
|
||||||
|
|
||||||
const isValid = await ws.cryptoApi.rsaVerify(
|
const isValid = await ws.cryptoApi.rsaVerify({
|
||||||
planchet.coinPub,
|
hm: planchet.coinPub,
|
||||||
denomSigRsa,
|
pk: denom.denomPub.rsa_public_key,
|
||||||
denom.denomPub.rsa_public_key,
|
sig: denomSigRsa.sig,
|
||||||
);
|
});
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
await ws.db
|
await ws.db
|
||||||
@ -377,7 +377,7 @@ async function processTipImpl(
|
|||||||
},
|
},
|
||||||
currentAmount: denom.value,
|
currentAmount: denom.value,
|
||||||
denomPubHash: denom.denomPubHash,
|
denomPubHash: denom.denomPubHash,
|
||||||
denomSig: { cipher: DenomKeyType.Rsa, rsa_signature: denomSigRsa },
|
denomSig: { cipher: DenomKeyType.Rsa, rsa_signature: denomSigRsa.sig },
|
||||||
exchangeBaseUrl: tipRecord.exchangeBaseUrl,
|
exchangeBaseUrl: tipRecord.exchangeBaseUrl,
|
||||||
status: CoinStatus.Fresh,
|
status: CoinStatus.Fresh,
|
||||||
suspended: false,
|
suspended: false,
|
||||||
|
@ -603,17 +603,17 @@ async function processPlanchetVerifyAndStoreCoin(
|
|||||||
throw Error("unsupported cipher");
|
throw Error("unsupported cipher");
|
||||||
}
|
}
|
||||||
|
|
||||||
const denomSigRsa = await ws.cryptoApi.rsaUnblind(
|
const denomSigRsa = await ws.cryptoApi.rsaUnblind({
|
||||||
evSig.blinded_rsa_signature,
|
bk: planchet.blindingKey,
|
||||||
planchet.blindingKey,
|
blindedSig: evSig.blinded_rsa_signature,
|
||||||
planchetDenomPub.rsa_public_key,
|
pk: planchetDenomPub.rsa_public_key,
|
||||||
);
|
});
|
||||||
|
|
||||||
const isValid = await ws.cryptoApi.rsaVerify(
|
const isValid = await ws.cryptoApi.rsaVerify({
|
||||||
planchet.coinPub,
|
hm: planchet.coinPub,
|
||||||
denomSigRsa,
|
pk: planchetDenomPub.rsa_public_key,
|
||||||
planchetDenomPub.rsa_public_key,
|
sig: denomSigRsa.sig,
|
||||||
);
|
});
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
await ws.db
|
await ws.db
|
||||||
@ -640,7 +640,7 @@ async function processPlanchetVerifyAndStoreCoin(
|
|||||||
if (planchetDenomPub.cipher === DenomKeyType.Rsa) {
|
if (planchetDenomPub.cipher === DenomKeyType.Rsa) {
|
||||||
denomSig = {
|
denomSig = {
|
||||||
cipher: planchetDenomPub.cipher,
|
cipher: planchetDenomPub.cipher,
|
||||||
rsa_signature: denomSigRsa,
|
rsa_signature: denomSigRsa.sig,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
throw Error("unsupported cipher");
|
throw Error("unsupported cipher");
|
||||||
@ -759,10 +759,11 @@ export async function updateWithdrawalDenoms(
|
|||||||
if (ws.insecureTrustExchange) {
|
if (ws.insecureTrustExchange) {
|
||||||
valid = true;
|
valid = true;
|
||||||
} else {
|
} else {
|
||||||
valid = await ws.cryptoApi.isValidDenom(
|
const res = await ws.cryptoApi.isValidDenom({
|
||||||
denom,
|
denom,
|
||||||
exchangeDetails.masterPublicKey,
|
masterPub: exchangeDetails.masterPublicKey,
|
||||||
);
|
});
|
||||||
|
valid = res.valid;
|
||||||
}
|
}
|
||||||
logger.trace(`Done validating ${denom.denomPubHash}`);
|
logger.trace(`Done validating ${denom.denomPubHash}`);
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
|
@ -90,7 +90,10 @@ import {
|
|||||||
RecoupOperations,
|
RecoupOperations,
|
||||||
ReserveOperations,
|
ReserveOperations,
|
||||||
} from "./internal-wallet-state.js";
|
} from "./internal-wallet-state.js";
|
||||||
import { CryptoApi, CryptoWorkerFactory } from "./crypto/workers/cryptoApi.js";
|
import {
|
||||||
|
CryptoDispatcher,
|
||||||
|
CryptoWorkerFactory,
|
||||||
|
} from "./crypto/workers/cryptoDispatcher.js";
|
||||||
import {
|
import {
|
||||||
AuditorTrustRecord,
|
AuditorTrustRecord,
|
||||||
CoinSourceType,
|
CoinSourceType,
|
||||||
@ -99,10 +102,7 @@ import {
|
|||||||
ReserveRecordStatus,
|
ReserveRecordStatus,
|
||||||
WalletStoresV1,
|
WalletStoresV1,
|
||||||
} from "./db.js";
|
} from "./db.js";
|
||||||
import {
|
import { getErrorDetailFromException, TalerError } from "./errors.js";
|
||||||
getErrorDetailFromException,
|
|
||||||
TalerError,
|
|
||||||
} from "./errors.js";
|
|
||||||
import { exportBackup } from "./operations/backup/export.js";
|
import { exportBackup } from "./operations/backup/export.js";
|
||||||
import {
|
import {
|
||||||
addBackupProvider,
|
addBackupProvider,
|
||||||
@ -193,6 +193,10 @@ import {
|
|||||||
import { DbAccess, GetReadWriteAccess } from "./util/query.js";
|
import { DbAccess, GetReadWriteAccess } from "./util/query.js";
|
||||||
import { TimerGroup } from "./util/timer.js";
|
import { TimerGroup } from "./util/timer.js";
|
||||||
import { WalletCoreApiClient } from "./wallet-api-types.js";
|
import { WalletCoreApiClient } from "./wallet-api-types.js";
|
||||||
|
import {
|
||||||
|
TalerCryptoInterface,
|
||||||
|
TalerCryptoInterfaceR,
|
||||||
|
} from "./crypto/cryptoImplementation.js";
|
||||||
|
|
||||||
const builtinAuditors: AuditorTrustRecord[] = [
|
const builtinAuditors: AuditorTrustRecord[] = [
|
||||||
{
|
{
|
||||||
@ -1158,7 +1162,8 @@ class InternalWalletStateImpl implements InternalWalletState {
|
|||||||
memoProcessRefresh: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
memoProcessRefresh: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
||||||
memoProcessRecoup: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
memoProcessRecoup: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
||||||
memoProcessDeposit: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
memoProcessDeposit: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
||||||
cryptoApi: CryptoApi;
|
cryptoApi: TalerCryptoInterface;
|
||||||
|
cryptoDispatcher: CryptoDispatcher;
|
||||||
|
|
||||||
merchantInfoCache: Record<string, MerchantInfo> = {};
|
merchantInfoCache: Record<string, MerchantInfo> = {};
|
||||||
|
|
||||||
@ -1213,7 +1218,8 @@ class InternalWalletStateImpl implements InternalWalletState {
|
|||||||
public http: HttpRequestLibrary,
|
public http: HttpRequestLibrary,
|
||||||
cryptoWorkerFactory: CryptoWorkerFactory,
|
cryptoWorkerFactory: CryptoWorkerFactory,
|
||||||
) {
|
) {
|
||||||
this.cryptoApi = new CryptoApi(cryptoWorkerFactory);
|
this.cryptoDispatcher = new CryptoDispatcher(cryptoWorkerFactory);
|
||||||
|
this.cryptoApi = this.cryptoDispatcher.cryptoApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDenomInfo(
|
async getDenomInfo(
|
||||||
@ -1258,7 +1264,7 @@ class InternalWalletStateImpl implements InternalWalletState {
|
|||||||
stop(): void {
|
stop(): void {
|
||||||
this.stopped = true;
|
this.stopped = true;
|
||||||
this.timerGroup.stopCurrentAndFutureTimers();
|
this.timerGroup.stopCurrentAndFutureTimers();
|
||||||
this.cryptoApi.stop();
|
this.cryptoDispatcher.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
async runUntilDone(
|
async runUntilDone(
|
||||||
|
@ -23,18 +23,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Logger } from "@gnu-taler/taler-util";
|
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 logger = new Logger("browserWorkerEntry.ts");
|
||||||
|
|
||||||
const worker: Worker = (self as any) as Worker;
|
const worker: Worker = self as any as Worker;
|
||||||
|
|
||||||
async function handleRequest(
|
async function handleRequest(
|
||||||
operation: string,
|
operation: string,
|
||||||
id: number,
|
id: number,
|
||||||
args: string[],
|
args: string[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const impl = new CryptoImplementation();
|
const impl = nativeCrypto;
|
||||||
|
|
||||||
if (!(operation in impl)) {
|
if (!(operation in impl)) {
|
||||||
console.error(`crypto operation '${operation}' not found`);
|
console.error(`crypto operation '${operation}' not found`);
|
||||||
|
@ -97,10 +97,10 @@ export interface UpgradeResponse {
|
|||||||
async function callBackend(operation: string, payload: any): Promise<any> {
|
async function callBackend(operation: string, payload: any): Promise<any> {
|
||||||
let response: CoreApiResponse;
|
let response: CoreApiResponse;
|
||||||
try {
|
try {
|
||||||
response = await platform.setMessageToWalletBackground(operation, payload)
|
response = await platform.setMessageToWalletBackground(operation, payload);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Error calling backend");
|
console.log("Error calling backend");
|
||||||
throw new Error(`Error contacting backend: ${e}`)
|
throw new Error(`Error contacting backend: ${e}`);
|
||||||
}
|
}
|
||||||
console.log("got response", response);
|
console.log("got response", response);
|
||||||
if (response.type === "error") {
|
if (response.type === "error") {
|
||||||
@ -413,12 +413,15 @@ export function importDB(dump: any): Promise<void> {
|
|||||||
return callBackend("importDb", { dump });
|
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 listener = (message: MessageFromBackend): void => {
|
||||||
const shouldNotify = messageTypes.includes(message.type);
|
const shouldNotify = messageTypes.includes(message.type);
|
||||||
if (shouldNotify) {
|
if (shouldNotify) {
|
||||||
doCallback();
|
doCallback();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return platform.listenToWalletNotifications(listener)
|
return platform.listenToWalletNotifications(listener);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user