wallet-core/src/crypto/cryptoImplementation.ts

759 lines
23 KiB
TypeScript
Raw Normal View History

2019-08-15 23:34:08 +02:00
/*
This file is part of GNU Taler
(C) 2019 GNUnet e.V.
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.
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
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* Synchronous implementation of crypto-related functions for the wallet.
*
* The functionality is parameterized over an Emscripten environment.
*/
/**
* Imports.
*/
import {
CoinRecord,
CoinStatus,
DenominationRecord,
PreCoinRecord,
RefreshPreCoinRecord,
RefreshSessionRecord,
ReserveRecord,
TipPlanchet,
WireFee,
} from "../dbTypes";
import { CoinPaySig, ContractTerms, PaybackRequest } from "../talerTypes";
import { BenchmarkResult, CoinWithDenom, PayCoinInfo } from "../walletTypes";
import { canonicalJson } from "../helpers";
import { EmscEnvironment } from "./emscInterface";
import * as native from "./emscInterface";
import { AmountJson } from "../amounts";
import * as Amounts from "../amounts";
import * as timer from "../timer";
import { getRandomBytes, encodeCrock } from "./talerCrypto";
2019-08-15 23:34:08 +02:00
export class CryptoImplementation {
static enableTracing: boolean = false;
constructor(private emsc: EmscEnvironment) {}
/**
* Create a pre-coin of the given denomination to be withdrawn from then given
* reserve.
*/
createPreCoin(
denom: DenominationRecord,
reserve: ReserveRecord,
): PreCoinRecord {
const reservePriv = new native.EddsaPrivateKey(this.emsc);
2019-11-21 23:09:43 +01:00
reservePriv.loadCrock(reserve.reservePriv);
2019-08-15 23:34:08 +02:00
const reservePub = new native.EddsaPublicKey(this.emsc);
2019-11-21 23:09:43 +01:00
reservePub.loadCrock(reserve.reservePub);
2019-08-15 23:34:08 +02:00
const denomPub = native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub);
const coinPriv = native.EddsaPrivateKey.create(this.emsc);
const coinPub = coinPriv.getPublicKey();
const blindingFactor = native.RsaBlindingKeySecret.create(this.emsc);
const pubHash: native.HashCode = coinPub.hash();
const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
if (!ev) {
throw Error("couldn't blind (malicious exchange key?)");
}
if (!denom.feeWithdraw) {
throw Error("Field fee_withdraw missing");
}
const amountWithFee = new native.Amount(this.emsc, denom.value);
amountWithFee.add(new native.Amount(this.emsc, denom.feeWithdraw));
const withdrawFee = new native.Amount(this.emsc, denom.feeWithdraw);
const denomPubHash = denomPub.encode().hash();
// Signature
const withdrawRequest = new native.WithdrawRequestPS(this.emsc, {
amount_with_fee: amountWithFee.toNbo(),
h_coin_envelope: ev.hash(),
h_denomination_pub: denomPubHash,
reserve_pub: reservePub,
withdraw_fee: withdrawFee.toNbo(),
});
const sig = native.eddsaSign(withdrawRequest.toPurpose(), reservePriv);
const preCoin: PreCoinRecord = {
blindingKey: blindingFactor.toCrock(),
coinEv: ev.toCrock(),
coinPriv: coinPriv.toCrock(),
coinPub: coinPub.toCrock(),
coinValue: denom.value,
denomPub: denomPub.toCrock(),
denomPubHash: denomPubHash.toCrock(),
2019-11-21 23:09:43 +01:00
exchangeBaseUrl: reserve.exchangeBaseUrl,
2019-08-15 23:34:08 +02:00
isFromTip: false,
reservePub: reservePub.toCrock(),
withdrawSig: sig.toCrock(),
};
return preCoin;
}
/**
* Create a planchet used for tipping, including the private keys.
*/
createTipPlanchet(denom: DenominationRecord): TipPlanchet {
const denomPub = native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub);
const coinPriv = native.EddsaPrivateKey.create(this.emsc);
const coinPub = coinPriv.getPublicKey();
const blindingFactor = native.RsaBlindingKeySecret.create(this.emsc);
const pubHash: native.HashCode = coinPub.hash();
const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
if (!ev) {
throw Error("couldn't blind (malicious exchange key?)");
}
if (!denom.feeWithdraw) {
throw Error("Field fee_withdraw missing");
}
const tipPlanchet: TipPlanchet = {
blindingKey: blindingFactor.toCrock(),
coinEv: ev.toCrock(),
coinPriv: coinPriv.toCrock(),
coinPub: coinPub.toCrock(),
coinValue: denom.value,
denomPub: denomPub.encode().toCrock(),
denomPubHash: denomPub
.encode()
.hash()
.toCrock(),
};
return tipPlanchet;
}
/**
* Create and sign a message to request payback for a coin.
*/
createPaybackRequest(coin: CoinRecord): PaybackRequest {
const p = new native.PaybackRequestPS(this.emsc, {
coin_blind: native.RsaBlindingKeySecret.fromCrock(
this.emsc,
coin.blindingKey,
),
coin_pub: native.EddsaPublicKey.fromCrock(this.emsc, coin.coinPub),
h_denom_pub: native.RsaPublicKey.fromCrock(this.emsc, coin.denomPub)
.encode()
.hash(),
});
const coinPriv = native.EddsaPrivateKey.fromCrock(this.emsc, coin.coinPriv);
const coinSig = native.eddsaSign(p.toPurpose(), coinPriv);
const paybackRequest: PaybackRequest = {
coin_blind_key_secret: coin.blindingKey,
coin_pub: coin.coinPub,
coin_sig: coinSig.toCrock(),
denom_pub: coin.denomPub,
denom_sig: coin.denomSig,
};
return paybackRequest;
}
/**
* Check if a payment signature is valid.
*/
isValidPaymentSignature(
sig: string,
contractHash: string,
merchantPub: string,
): boolean {
const p = new native.PaymentSignaturePS(this.emsc, {
contract_hash: native.HashCode.fromCrock(this.emsc, contractHash),
});
const nativeSig = new native.EddsaSignature(this.emsc);
nativeSig.loadCrock(sig);
const nativePub = native.EddsaPublicKey.fromCrock(this.emsc, merchantPub);
return native.eddsaVerify(
native.SignaturePurpose.MERCHANT_PAYMENT_OK,
p.toPurpose(),
nativeSig,
nativePub,
);
}
/**
* Check if a wire fee is correctly signed.
*/
isValidWireFee(type: string, wf: WireFee, masterPub: string): boolean {
const p = new native.MasterWireFeePS(this.emsc, {
closing_fee: new native.Amount(this.emsc, wf.closingFee).toNbo(),
2019-11-21 23:09:43 +01:00
end_date: native.AbsoluteTimeNbo.fromStampSeconds(this.emsc, (wf.endStamp.t_ms / 1000)),
2019-08-15 23:34:08 +02:00
h_wire_method: native.ByteArray.fromStringWithNull(
this.emsc,
type,
).hash(),
start_date: native.AbsoluteTimeNbo.fromStampSeconds(
this.emsc,
2019-11-21 23:09:43 +01:00
Math.floor(wf.startStamp.t_ms / 1000),
2019-08-15 23:34:08 +02:00
),
wire_fee: new native.Amount(this.emsc, wf.wireFee).toNbo(),
});
const nativeSig = new native.EddsaSignature(this.emsc);
nativeSig.loadCrock(wf.sig);
const nativePub = native.EddsaPublicKey.fromCrock(this.emsc, masterPub);
return native.eddsaVerify(
native.SignaturePurpose.MASTER_WIRE_FEES,
p.toPurpose(),
nativeSig,
nativePub,
);
}
/**
* Check if the signature of a denomination is valid.
*/
isValidDenom(denom: DenominationRecord, masterPub: string): boolean {
const p = new native.DenominationKeyValidityPS(this.emsc, {
denom_hash: native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub)
.encode()
.hash(),
expire_legal: native.AbsoluteTimeNbo.fromTalerString(
this.emsc,
denom.stampExpireLegal,
),
expire_spend: native.AbsoluteTimeNbo.fromTalerString(
this.emsc,
denom.stampExpireDeposit,
),
expire_withdraw: native.AbsoluteTimeNbo.fromTalerString(
this.emsc,
denom.stampExpireWithdraw,
),
fee_deposit: new native.Amount(this.emsc, denom.feeDeposit).toNbo(),
fee_refresh: new native.Amount(this.emsc, denom.feeRefresh).toNbo(),
fee_refund: new native.Amount(this.emsc, denom.feeRefund).toNbo(),
fee_withdraw: new native.Amount(this.emsc, denom.feeWithdraw).toNbo(),
master: native.EddsaPublicKey.fromCrock(this.emsc, masterPub),
start: native.AbsoluteTimeNbo.fromTalerString(
this.emsc,
denom.stampStart,
),
value: new native.Amount(this.emsc, denom.value).toNbo(),
});
const nativeSig = new native.EddsaSignature(this.emsc);
nativeSig.loadCrock(denom.masterSig);
const nativePub = native.EddsaPublicKey.fromCrock(this.emsc, masterPub);
return native.eddsaVerify(
native.SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY,
p.toPurpose(),
nativeSig,
nativePub,
);
}
/**
* Create a new EdDSA key pair.
*/
createEddsaKeypair(): { priv: string; pub: string } {
const priv = native.EddsaPrivateKey.create(this.emsc);
const pub = priv.getPublicKey();
return { priv: priv.toCrock(), pub: pub.toCrock() };
}
/**
* Unblind a blindly signed value.
*/
rsaUnblind(sig: string, bk: string, pk: string): string {
const denomSig = native.rsaUnblind(
native.RsaSignature.fromCrock(this.emsc, sig),
native.RsaBlindingKeySecret.fromCrock(this.emsc, bk),
native.RsaPublicKey.fromCrock(this.emsc, pk),
);
return denomSig.encode().toCrock();
}
/**
* Generate updated coins (to store in the database)
* and deposit permissions for each given coin.
*/
signDeposit(
contractTerms: ContractTerms,
cds: CoinWithDenom[],
totalAmount: AmountJson,
): PayCoinInfo {
const ret: PayCoinInfo = {
originalCoins: [],
sigs: [],
updatedCoins: [],
};
const contractTermsHash = this.hashString(canonicalJson(contractTerms));
const feeList: AmountJson[] = cds.map(x => x.denom.feeDeposit);
let fees = Amounts.add(Amounts.getZero(feeList[0].currency), ...feeList)
.amount;
// okay if saturates
fees = Amounts.sub(fees, Amounts.parseOrThrow(contractTerms.max_fee))
.amount;
const total = Amounts.add(fees, totalAmount).amount;
const amountSpent = native.Amount.getZero(
this.emsc,
cds[0].coin.currentAmount.currency,
);
const amountRemaining = new native.Amount(this.emsc, total);
for (const cd of cds) {
let coinSpend: native.Amount;
const originalCoin = { ...cd.coin };
if (amountRemaining.value === 0 && amountRemaining.fraction === 0) {
break;
}
if (
amountRemaining.cmp(
new native.Amount(this.emsc, cd.coin.currentAmount),
) < 0
) {
coinSpend = new native.Amount(this.emsc, amountRemaining.toJson());
} else {
coinSpend = new native.Amount(this.emsc, cd.coin.currentAmount);
}
amountSpent.add(coinSpend);
amountRemaining.sub(coinSpend);
const feeDeposit: native.Amount = new native.Amount(
this.emsc,
cd.denom.feeDeposit,
);
// Give the merchant at least the deposit fee, otherwise it'll reject
// the coin.
if (coinSpend.cmp(feeDeposit) < 0) {
coinSpend = feeDeposit;
}
const newAmount = new native.Amount(this.emsc, cd.coin.currentAmount);
newAmount.sub(coinSpend);
cd.coin.currentAmount = newAmount.toJson();
2019-11-21 23:09:43 +01:00
cd.coin.status = CoinStatus.Dirty;
2019-08-15 23:34:08 +02:00
const d = new native.DepositRequestPS(this.emsc, {
amount_with_fee: coinSpend.toNbo(),
coin_pub: native.EddsaPublicKey.fromCrock(this.emsc, cd.coin.coinPub),
deposit_fee: new native.Amount(this.emsc, cd.denom.feeDeposit).toNbo(),
h_contract: native.HashCode.fromCrock(this.emsc, contractTermsHash),
h_wire: native.HashCode.fromCrock(this.emsc, contractTerms.H_wire),
merchant: native.EddsaPublicKey.fromCrock(
this.emsc,
contractTerms.merchant_pub,
),
refund_deadline: native.AbsoluteTimeNbo.fromTalerString(
this.emsc,
contractTerms.refund_deadline,
),
timestamp: native.AbsoluteTimeNbo.fromTalerString(
this.emsc,
contractTerms.timestamp,
),
});
const coinSig = native
.eddsaSign(
d.toPurpose(),
native.EddsaPrivateKey.fromCrock(this.emsc, cd.coin.coinPriv),
)
.toCrock();
const s: CoinPaySig = {
coin_pub: cd.coin.coinPub,
coin_sig: coinSig,
contribution: Amounts.toString(coinSpend.toJson()),
denom_pub: cd.coin.denomPub,
exchange_url: cd.denom.exchangeBaseUrl,
ub_sig: cd.coin.denomSig,
};
ret.sigs.push(s);
ret.updatedCoins.push(cd.coin);
ret.originalCoins.push(originalCoin);
}
return ret;
}
/**
* Create a new refresh session.
*/
createRefreshSession(
exchangeBaseUrl: string,
kappa: number,
meltCoin: CoinRecord,
newCoinDenoms: DenominationRecord[],
meltFee: AmountJson,
): RefreshSessionRecord {
let valueWithFee = Amounts.getZero(newCoinDenoms[0].value.currency);
for (const ncd of newCoinDenoms) {
valueWithFee = Amounts.add(valueWithFee, ncd.value, ncd.feeWithdraw)
.amount;
}
// melt fee
valueWithFee = Amounts.add(valueWithFee, meltFee).amount;
const sessionHc = new native.HashContext(this.emsc);
const transferPubs: string[] = [];
const transferPrivs: string[] = [];
const preCoinsForGammas: RefreshPreCoinRecord[][] = [];
for (let i = 0; i < kappa; i++) {
const t = native.EcdhePrivateKey.create(this.emsc);
const pub = t.getPublicKey();
sessionHc.read(pub);
transferPrivs.push(t.toCrock());
transferPubs.push(pub.toCrock());
}
for (const denom of newCoinDenoms) {
const r = native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub);
sessionHc.read(r.encode());
}
sessionHc.read(
native.EddsaPublicKey.fromCrock(this.emsc, meltCoin.coinPub),
);
sessionHc.read(new native.Amount(this.emsc, valueWithFee).toNbo());
for (let i = 0; i < kappa; i++) {
const preCoins: RefreshPreCoinRecord[] = [];
for (let j = 0; j < newCoinDenoms.length; j++) {
const transferPriv = native.EcdhePrivateKey.fromCrock(
this.emsc,
transferPrivs[i],
);
const oldCoinPub = native.EddsaPublicKey.fromCrock(
this.emsc,
meltCoin.coinPub,
);
const transferSecret = native.ecdhEddsa(transferPriv, oldCoinPub);
const fresh = native.setupFreshCoin(transferSecret, j);
const coinPriv = fresh.priv;
const coinPub = coinPriv.getPublicKey();
const blindingFactor = fresh.blindingKey;
const pubHash: native.HashCode = coinPub.hash();
const denomPub = native.RsaPublicKey.fromCrock(
this.emsc,
newCoinDenoms[j].denomPub,
);
const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
if (!ev) {
throw Error("couldn't blind (malicious exchange key?)");
}
const preCoin: RefreshPreCoinRecord = {
blindingKey: blindingFactor.toCrock(),
coinEv: ev.toCrock(),
privateKey: coinPriv.toCrock(),
publicKey: coinPub.toCrock(),
};
preCoins.push(preCoin);
sessionHc.read(ev);
}
preCoinsForGammas.push(preCoins);
}
const sessionHash = new native.HashCode(this.emsc);
sessionHash.alloc();
sessionHc.finish(sessionHash);
const confirmData = new native.RefreshMeltCoinAffirmationPS(this.emsc, {
amount_with_fee: new native.Amount(this.emsc, valueWithFee).toNbo(),
coin_pub: native.EddsaPublicKey.fromCrock(this.emsc, meltCoin.coinPub),
melt_fee: new native.Amount(this.emsc, meltFee).toNbo(),
session_hash: sessionHash,
});
const confirmSig: string = native
.eddsaSign(
confirmData.toPurpose(),
native.EddsaPrivateKey.fromCrock(this.emsc, meltCoin.coinPriv),
)
.toCrock();
let valueOutput = Amounts.getZero(newCoinDenoms[0].value.currency);
for (const denom of newCoinDenoms) {
valueOutput = Amounts.add(valueOutput, denom.value).amount;
}
2019-11-21 23:09:43 +01:00
const refreshSessionId = encodeCrock(getRandomBytes(32));
2019-08-15 23:34:08 +02:00
const refreshSession: RefreshSessionRecord = {
2019-11-21 23:09:43 +01:00
refreshSessionId,
2019-08-15 23:34:08 +02:00
confirmSig,
exchangeBaseUrl,
finished: false,
hash: sessionHash.toCrock(),
meltCoinPub: meltCoin.coinPub,
newDenomHashes: newCoinDenoms.map(d => d.denomPubHash),
newDenoms: newCoinDenoms.map(d => d.denomPub),
norevealIndex: undefined,
preCoinsForGammas,
transferPrivs,
transferPubs,
valueOutput,
valueWithFee,
};
return refreshSession;
}
/**
* Hash a string including the zero terminator.
*/
hashString(str: string): string {
const b = native.ByteArray.fromStringWithNull(this.emsc, str);
return b.hash().toCrock();
}
/**
* Hash a denomination public key.
*/
hashDenomPub(denomPub: string): string {
return native.RsaPublicKey.fromCrock(this.emsc, denomPub)
.encode()
.hash()
.toCrock();
}
signCoinLink(
oldCoinPriv: string,
newDenomHash: string,
oldCoinPub: string,
transferPub: string,
coinEv: string,
): string {
const coinEvHash = native.ByteArray.fromCrock(this.emsc, coinEv).hash();
const coinLink = new native.CoinLinkSignaturePS(this.emsc, {
coin_envelope_hash: coinEvHash,
h_denom_pub: native.HashCode.fromCrock(this.emsc, newDenomHash),
old_coin_pub: native.EddsaPublicKey.fromCrock(this.emsc, oldCoinPub),
transfer_pub: native.EcdhePublicKey.fromCrock(this.emsc, transferPub),
});
const coinPriv = native.EddsaPrivateKey.fromCrock(this.emsc, oldCoinPriv);
const sig = native.eddsaSign(coinLink.toPurpose(), coinPriv);
return sig.toCrock();
}
benchmark(repetitions: number): BenchmarkResult {
let time_hash = 0;
for (let i = 0; i < repetitions; i++) {
const start = timer.performanceNow();
this.hashString("hello world");
time_hash += timer.performanceNow() - start;
}
let time_hash_big = 0;
const ba = new native.ByteArray(this.emsc, 4096);
for (let i = 0; i < repetitions; i++) {
ba.randomize(native.RandomQuality.WEAK);
const start = timer.performanceNow();
ba.hash();
time_hash_big += timer.performanceNow() - start;
}
let time_eddsa_create = 0;
for (let i = 0; i < repetitions; i++) {
const start = timer.performanceNow();
const priv: native.EddsaPrivateKey = native.EddsaPrivateKey.create(
this.emsc,
);
time_eddsa_create += timer.performanceNow() - start;
priv.destroy();
}
let time_eddsa_sign = 0;
const eddsaPriv: native.EddsaPrivateKey = native.EddsaPrivateKey.create(
this.emsc,
);
const eddsaPub: native.EddsaPublicKey = eddsaPriv.getPublicKey();
const h: native.HashCode = new native.HashCode(this.emsc);
h.alloc();
h.random(native.RandomQuality.WEAK);
const ps = new native.PaymentSignaturePS(this.emsc, {
contract_hash: h,
});
const p = ps.toPurpose();
for (let i = 0; i < repetitions; i++) {
const start = timer.performanceNow();
native.eddsaSign(p, eddsaPriv);
time_eddsa_sign += timer.performanceNow() - start;
}
const eddsaSig = native.eddsaSign(p, eddsaPriv);
let time_ecdsa_create = 0;
for (let i = 0; i < repetitions; i++) {
const start = timer.performanceNow();
const priv: native.EcdsaPrivateKey = native.EcdsaPrivateKey.create(
this.emsc,
);
time_ecdsa_create += timer.performanceNow() - start;
priv.destroy();
}
let time_eddsa_verify = 0;
for (let i = 0; i < repetitions; i++) {
const start = timer.performanceNow();
native.eddsaVerify(
native.SignaturePurpose.MERCHANT_PAYMENT_OK,
p,
eddsaSig,
eddsaPub,
);
time_eddsa_verify += timer.performanceNow() - start;
}
/* rsa 2048 */
let time_rsa_2048_blind = 0;
const rsaPriv2048: native.RsaPrivateKey = native.RsaPrivateKey.create(
this.emsc,
2048,
);
const rsaPub2048 = rsaPriv2048.getPublicKey();
const blindingSecret2048 = native.RsaBlindingKeySecret.create(this.emsc);
for (let i = 0; i < repetitions; i++) {
const start = timer.performanceNow();
native.rsaBlind(h, blindingSecret2048, rsaPub2048);
time_rsa_2048_blind += timer.performanceNow() - start;
}
const blindedMessage2048 = native.rsaBlind(
h,
blindingSecret2048,
rsaPub2048,
);
if (!blindedMessage2048) {
throw Error("should not happen");
}
const rsaBlindSig2048 = native.rsaSignBlinded(
rsaPriv2048,
blindedMessage2048,
);
let time_rsa_2048_unblind = 0;
for (let i = 0; i < repetitions; i++) {
const start = timer.performanceNow();
native.rsaUnblind(rsaBlindSig2048, blindingSecret2048, rsaPub2048);
time_rsa_2048_unblind += timer.performanceNow() - start;
}
const unblindedSig2048 = native.rsaUnblind(
rsaBlindSig2048,
blindingSecret2048,
rsaPub2048,
);
let time_rsa_2048_verify = 0;
for (let i = 0; i < repetitions; i++) {
const start = timer.performanceNow();
native.rsaVerify(h, unblindedSig2048, rsaPub2048);
time_rsa_2048_verify += timer.performanceNow() - start;
}
/* rsa 4096 */
let time_rsa_4096_blind = 0;
const rsaPriv4096: native.RsaPrivateKey = native.RsaPrivateKey.create(
this.emsc,
4096,
);
const rsaPub4096 = rsaPriv4096.getPublicKey();
const blindingSecret4096 = native.RsaBlindingKeySecret.create(this.emsc);
for (let i = 0; i < repetitions; i++) {
const start = timer.performanceNow();
native.rsaBlind(h, blindingSecret4096, rsaPub4096);
time_rsa_4096_blind += timer.performanceNow() - start;
}
const blindedMessage4096 = native.rsaBlind(
h,
blindingSecret4096,
rsaPub4096,
);
if (!blindedMessage4096) {
throw Error("should not happen");
}
const rsaBlindSig4096 = native.rsaSignBlinded(
rsaPriv4096,
blindedMessage4096,
);
let time_rsa_4096_unblind = 0;
for (let i = 0; i < repetitions; i++) {
const start = timer.performanceNow();
native.rsaUnblind(rsaBlindSig4096, blindingSecret4096, rsaPub4096);
time_rsa_4096_unblind += timer.performanceNow() - start;
}
const unblindedSig4096 = native.rsaUnblind(
rsaBlindSig4096,
blindingSecret4096,
rsaPub4096,
);
let time_rsa_4096_verify = 0;
for (let i = 0; i < repetitions; i++) {
const start = timer.performanceNow();
native.rsaVerify(h, unblindedSig4096, rsaPub4096);
time_rsa_4096_verify += timer.performanceNow() - start;
}
return {
repetitions,
time: {
hash_small: time_hash,
hash_big: time_hash_big,
eddsa_create: time_eddsa_create,
eddsa_sign: time_eddsa_sign,
eddsa_verify: time_eddsa_verify,
ecdsa_create: time_ecdsa_create,
rsa_2048_blind: time_rsa_2048_blind,
rsa_2048_unblind: time_rsa_2048_unblind,
rsa_2048_verify: time_rsa_2048_verify,
rsa_4096_blind: time_rsa_4096_blind,
rsa_4096_unblind: time_rsa_4096_unblind,
rsa_4096_verify: time_rsa_4096_verify,
},
};
}
}