From 966ee6a86c99af79f0fecea2e7f43726bdb03955 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Mon, 2 Dec 2024 14:18:13 -0500 Subject: [PATCH 1/6] feat: add `ed25519` --- .github/workflows/validate-cpp.yml | 4 +- bun.lockb | Bin 575492 -> 576628 bytes docs/implementation-coverage.md | 43 +- example/index.ts | 3 + example/ios/Podfile.lock | 8 +- example/package.json | 5 +- example/src/benchmarks/benchmarks.ts | 88 +- example/src/benchmarks/ed/ed25519.ts | 86 ++ example/src/benchmarks/pbkdf2/pbkdf2.ts | 81 +- example/src/benchmarks/random/randomBytes.ts | 57 +- example/src/components/BenchmarkItem.tsx | 42 +- .../src/components/BenchmarkResultItem.tsx | 64 +- example/src/hooks/useBenchmarks.ts | 66 +- example/src/hooks/useTestsList.ts | 1 + example/src/hooks/useTestsRun.ts | 4 +- .../children/BenchmarkSuitesScreen.tsx | 66 +- example/src/tests/ed25519/ed25519_tests.ts | 56 ++ example/src/tests/util.ts | 6 +- example/src/types/benchmarks.ts | 22 +- example/src/types/tests.ts | 2 +- example/tsconfig.json | 3 - package.json | 10 +- .../android/CMakeLists.txt | 2 + .../cpp/ed25519/HybridEdKeyPair.cpp | 236 +++++ .../cpp/ed25519/HybridEdKeyPair.hpp | 75 ++ .../cpp/utils/Utils.hpp | 5 + packages/react-native-quick-crypto/nitro.json | 1 + .../android/QuickCrypto+autolinking.cmake | 1 + .../generated/android/QuickCryptoOnLoad.cpp | 10 + .../generated/ios/QuickCryptoAutolinking.mm | 10 + ...mmetricKeyType.hpp => CFRGKeyPairType.hpp} | 50 +- .../shared/c++/HybridEdKeyPairSpec.cpp | 28 + .../shared/c++/HybridEdKeyPairSpec.hpp | 73 ++ .../shared/c++/HybridKeyObjectHandleSpec.hpp | 8 +- packages/react-native-quick-crypto/src/ed.ts | 58 ++ .../react-native-quick-crypto/src/index.ts | 9 +- .../src/keys/classes.ts | 211 +++++ .../src/keys/generateKeyPair.ts | 146 +++ .../src/keys/index.ts | 141 +-- .../src/keys/signVerify.ts | 39 + .../src/keys/utils.ts | 184 ++++ .../src/specs/edKeyPair.nitro.ts | 32 + .../src/utils/conversion.ts | 2 + .../src/utils/index.ts | 1 + .../src/utils/types.ts | 88 +- .../src/utils/validation.ts | 35 + .../zzz/ts/Cipher.ts | 832 ------------------ .../react-native-quick-crypto/zzz/ts/keys.ts | 553 ------------ 48 files changed, 1711 insertions(+), 1836 deletions(-) create mode 100644 example/src/benchmarks/ed/ed25519.ts create mode 100644 example/src/tests/ed25519/ed25519_tests.ts create mode 100644 packages/react-native-quick-crypto/cpp/ed25519/HybridEdKeyPair.cpp create mode 100644 packages/react-native-quick-crypto/cpp/ed25519/HybridEdKeyPair.hpp rename packages/react-native-quick-crypto/nitrogen/generated/shared/c++/{AsymmetricKeyType.hpp => CFRGKeyPairType.hpp} (53%) create mode 100644 packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEdKeyPairSpec.cpp create mode 100644 packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEdKeyPairSpec.hpp create mode 100644 packages/react-native-quick-crypto/src/ed.ts create mode 100644 packages/react-native-quick-crypto/src/keys/classes.ts create mode 100644 packages/react-native-quick-crypto/src/keys/generateKeyPair.ts create mode 100644 packages/react-native-quick-crypto/src/keys/signVerify.ts create mode 100644 packages/react-native-quick-crypto/src/keys/utils.ts create mode 100644 packages/react-native-quick-crypto/src/specs/edKeyPair.nitro.ts create mode 100644 packages/react-native-quick-crypto/src/utils/validation.ts delete mode 100644 packages/react-native-quick-crypto/zzz/ts/Cipher.ts delete mode 100644 packages/react-native-quick-crypto/zzz/ts/keys.ts diff --git a/.github/workflows/validate-cpp.yml b/.github/workflows/validate-cpp.yml index cd9dad45..cb4da4f5 100644 --- a/.github/workflows/validate-cpp.yml +++ b/.github/workflows/validate-cpp.yml @@ -27,10 +27,12 @@ jobs: github_token: ${{ secrets.github_token }} reporter: github-pr-review flags: --linelength=230 - targets: --recursive packages/react-native-quick-crypto/cpp packages/react-native-quick-crypto/android/src/main/cpp packages/react-native-quick-crypto/nitrogen/generated/shared/c++ + targets: --recursive packages/react-native-quick-crypto/cpp packages/react-native-quick-crypto/android/src/main/cpp filter: "-legal/copyright\ ,-readability/todo\ ,-build/namespaces\ ,-whitespace/comments\ ,-build/include_order\ + ,-whitespace/indent_namespace\ + ,-whitespace/parens\ " diff --git a/bun.lockb b/bun.lockb index 8ebdaea2c1e98a6064655eb56244b57bd57eeb60..da05e888becfea195a1f9d5a1564b0178e5bc71d 100755 GIT binary patch delta 89856 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

$=L`b|!DMf+l10MyTNq!+s4e!rEuY&XT7R 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 { - benchmark.run(this, multiplier); + const promises = this.benchmarks.map(async benchFn => { + const b = await benchFn(); + await b.run(); + this.processResults(b); + this.state = 'done'; }); + await Promise.all(promises); } -} -export class Benchmark { - name: string; // function name - runCount: number; - us?: BenchmarkFn; - them: Challenger[]; - - constructor(benchmark: ImportedBenchmark) { - this.name = benchmark.name; - this.runCount = benchmark.runCount; - this.us = benchmark.us; - this.them = benchmark.them; - } + processResults = (b: Bench): void => { + const tasks = b.tasks; + const us = tasks.find(t => t.name === 'rnqc'); + const themTasks = tasks.filter(t => t.name !== 'rnqc'); - run(suite: BenchmarkSuite, multiplier: number = 1) { - const usTime = this.timeFn(this.us!, multiplier); - this.them.forEach(them => { - const themTime = this.timeFn(them.fn, multiplier); - const type = usTime < themTime ? 'faster' : 'slower'; - const times = calculateTimes(usTime, themTime); - const result: BenchmarkResult = { + themTasks.map(them => { + this.addResult({ errorMsg: undefined, challenger: them.name, - notes: them.notes, - runCount: this.runCount * multiplier, - fnName: this.name, - time: themTime, - us: usTime, - type, - times, - }; - suite.addResult(result); + notes: '', + benchName: b.name, + them: them.result, + us: us?.result, + }); }); - } - - /** - * @returns time in ms - */ - timeFn = (fn: BenchmarkFn, multiplier: number = 1): number => { - // warm up imports, etc. - fn(); - - const totalRunCount = this.runCount * multiplier; - - // do the actual benchmark - const start = performance.now(); - for (let i = 0; i < totalRunCount; i++) { - fn(); - } - const end = performance.now(); - return end - start; }; } diff --git a/example/src/benchmarks/ed/ed25519.ts b/example/src/benchmarks/ed/ed25519.ts new file mode 100644 index 00000000..e255a75d --- /dev/null +++ b/example/src/benchmarks/ed/ed25519.ts @@ -0,0 +1,86 @@ +import { Bench } from 'tinybench'; +import rnqc from 'react-native-quick-crypto'; +import { ed25519 as noble } from '@noble/curves/ed25519'; +import type { BenchFn } from '../../types/benchmarks'; + +const TIME_MS = 1000; + +const ed25519_sign_verify_async: BenchFn = async () => { + const message = 'hello world'; + const buffer = Buffer.from(message); + const ab = buffer.buffer; + const arr = new Uint8Array(buffer); + + // rnqc setup + const ed = new rnqc.Ed('ed25519', {}); + await ed.generateKeyPair(); + + // noble setup + const noblePrivateKey = noble.utils.randomPrivateKey(); + const noblePublicKey = noble.getPublicKey(noblePrivateKey); + + const bench = new Bench({ + name: 'ed25519 sign/verify (async)', + time: TIME_MS, + }); + + bench.add('rnqc', async () => { + const signature = await ed.sign(ab); + const verified = await ed.verify(signature, ab); + if (!verified) { + throw new Error('Signature verification failed'); + } + }); + + bench.add('@noble/curves/ed25519', () => { + const signature = noble.sign(arr, noblePrivateKey); + const verified = noble.verify(signature, arr, noblePublicKey); + if (!verified) { + throw new Error('Signature verification failed'); + } + }); + + bench.warmupTime = 100; + return bench; +}; + +const ed25519_sign_verify_sync: BenchFn = () => { + const message = 'hello world'; + const buffer = Buffer.from(message); + const ab = buffer.buffer; + const arr = new Uint8Array(buffer); + + // rnqc setup + const ed = new rnqc.Ed('ed25519', {}); + ed.generateKeyPairSync(); + + // noble setup + const noblePrivateKey = noble.utils.randomPrivateKey(); + const noblePublicKey = noble.getPublicKey(noblePrivateKey); + + const bench = new Bench({ + name: 'ed25519 sign/verify (sync)', + time: TIME_MS, + }); + + bench.add('rnqc', () => { + const signature = ed.signSync(ab); + const verified = ed.verifySync(signature, ab); + if (!verified) { + throw new Error('Signature verification failed'); + } + }); + + bench.add('@noble/curves/ed25519', () => { + const signature = noble.sign(arr, noblePrivateKey); + const verified = noble.verify(signature, arr, noblePublicKey); + if (!verified) { + throw new Error('Signature verification failed'); + } + }); + + bench.warmupTime = 100; + return bench; +}; + +export default [ed25519_sign_verify_async, ed25519_sign_verify_sync]; diff --git a/example/src/benchmarks/pbkdf2/pbkdf2.ts b/example/src/benchmarks/pbkdf2/pbkdf2.ts index aab26a37..08f8af1a 100644 --- a/example/src/benchmarks/pbkdf2/pbkdf2.ts +++ b/example/src/benchmarks/pbkdf2/pbkdf2.ts @@ -3,44 +3,51 @@ import * as noble from '@noble/hashes/pbkdf2'; import { sha256 } from '@noble/hashes/sha2'; // @ts-expect-error - crypto-browserify is not typed import browserify from 'crypto-browserify'; +import type { BenchFn } from '../../types/benchmarks'; +import { Bench } from 'tinybench'; -import type { ImportedBenchmark } from '../../types/benchmarks'; - -export const pbkdf2_256_1_32_async: ImportedBenchmark = { - name: 'pbkdf2_256_1_32_async', - runCount: 1000, - us: () => rnqc.pbkdf2('password', 'salt', 1, 32, 'sha256', () => {}), - them: [ - { - name: '@noble/hashes/pbkdf2', - notes: '', - fn: () => - noble.pbkdf2Async(sha256, 'password', 'salt', { c: 1, dkLen: 32 }), - }, - { - name: 'browserify/pbkdf2', - notes: '', - fn: () => - browserify.pbkdf2('password', 'salt', 1, 32, 'sha256', () => {}), - }, - ], +const TIME_MS = 1000; + +const pbkdf2_256_1_32_async: BenchFn = () => { + const bench = new Bench({ + name: 'pbkdf2 sha256 1x 32b (async)', + time: TIME_MS, + }); + + bench + .add('rnqc', () => { + rnqc.pbkdf2('password', 'salt', 1, 32, 'sha256', () => {}); + }) + .add('@noble/hashes/pbkdf2', async () => { + await noble.pbkdf2Async(sha256, 'password', 'salt', { c: 1, dkLen: 32 }); + }) + .add('browserify/pbkdf2', () => { + browserify.pbkdf2('password', 'salt', 1, 32, 'sha256', () => {}); + }); + + bench.warmupTime = 100; + return bench; }; -export const pbkdf2_256_1_32_sync: ImportedBenchmark = { - name: 'pbkdf2_256_1_32_sync', - runCount: 1000, - us: () => - rnqc.pbkdf2Sync('password', 'salt', 1, 32, 'sha256' as HashAlgorithm), - them: [ - { - name: '@noble/hashes/pbkdf2', - notes: '', - fn: () => noble.pbkdf2(sha256, 'password', 'salt', { c: 1, dkLen: 32 }), - }, - { - name: 'browserify/pbkdf2', - notes: '', - fn: () => browserify.pbkdf2Sync('password', 'salt', 1, 32, 'sha256'), - }, - ], +const pbkdf2_256_1_32_sync: BenchFn = () => { + const bench = new Bench({ + name: 'pbkdf2 sha256 1x 32b (sync)', + time: TIME_MS, + }); + + bench + .add('rnqc', () => { + rnqc.pbkdf2Sync('password', 'salt', 1, 32, 'sha256' as HashAlgorithm); + }) + .add('@noble/hashes/pbkdf2', () => { + noble.pbkdf2(sha256, 'password', 'salt', { c: 1, dkLen: 32 }); + }) + .add('browserify/pbkdf2', () => { + browserify.pbkdf2Sync('password', 'salt', 1, 32, 'sha256'); + }); + + bench.warmupTime = 100; + return bench; }; + +export default [pbkdf2_256_1_32_async, pbkdf2_256_1_32_sync]; diff --git a/example/src/benchmarks/random/randomBytes.ts b/example/src/benchmarks/random/randomBytes.ts index 9fb083ca..ae5b9ba7 100644 --- a/example/src/benchmarks/random/randomBytes.ts +++ b/example/src/benchmarks/random/randomBytes.ts @@ -1,30 +1,39 @@ import rnqc from 'react-native-quick-crypto'; // @ts-expect-error - crypto-browserify is not typed import browserify from 'crypto-browserify'; -import type { ImportedBenchmark } from '../../types/benchmarks'; - -export const randomBytes10: ImportedBenchmark = { - name: 'randomBytes10', - runCount: 10000, - us: () => rnqc.randomBytes(10), - them: [ - { - name: 'crypto-browserify', - notes: `crypto-browserify uses 'globalThis.crypto' under the hood, which on RN is this library, if polyfilled. So this benchmark doesn't make a lot of sense.`, - fn: () => browserify.randomBytes(10), - }, - ], +import type { BenchFn } from '../../types/benchmarks'; +import { Bench } from 'tinybench'; + +const TIME_MS = 1000; + +const randomBytes10: BenchFn = () => { + const bench = new Bench({ + name: 'randomBytes10', + time: TIME_MS, + }); + + bench + .add('rnqc', () => { + rnqc.randomBytes(10); + }) + .add('crypto-browserify', () => browserify.randomBytes(10)); + + bench.warmupTime = 100; + return bench; }; -export const randomBytes1024: ImportedBenchmark = { - name: 'randomBytes1024', - runCount: 5000, - us: () => rnqc.randomBytes(1024), - them: [ - { - name: 'crypto-browserify', - notes: `crypto-browserify uses 'globalThis.crypto' under the hood, which on RN is this library, if polyfilled. So this benchmark doesn't make a lot of sense.`, - fn: () => browserify.randomBytes(1024), - }, - ], +const randomBytes1024: BenchFn = () => { + const bench = new Bench({ + name: 'randomBytes1024', + time: TIME_MS, + }); + + bench + .add('rnqc', () => rnqc.randomBytes(1024)) + .add('crypto-browserify', () => browserify.randomBytes(1024)); + bench.warmupTime = 100; + + return bench; }; + +export default [randomBytes10, randomBytes1024]; diff --git a/example/src/components/BenchmarkItem.tsx b/example/src/components/BenchmarkItem.tsx index 2873924a..129aa627 100644 --- a/example/src/components/BenchmarkItem.tsx +++ b/example/src/components/BenchmarkItem.tsx @@ -5,11 +5,9 @@ import { StyleSheet, TouchableOpacity, ActivityIndicator, - InteractionManager, } from 'react-native'; import BouncyCheckbox from 'react-native-bouncy-checkbox'; import { useNavigation } from '@react-navigation/native'; -// import { calculateTimes, formatNumber } from '../benchmarks/utils'; import { colors } from '../styles/colors'; import type { BenchmarkSuite } from '../benchmarks/benchmarks'; import { calculateTimes, formatNumber } from '../benchmarks/utils'; @@ -17,13 +15,13 @@ import { calculateTimes, formatNumber } from '../benchmarks/utils'; type BenchmarkItemProps = { suite: BenchmarkSuite; toggle: () => void; - multiplier: number; + bumpRunCurrent: () => void; }; export const BenchmarkItem: React.FC = ({ suite, toggle, - multiplier, + bumpRunCurrent, }: BenchmarkItemProps) => { const [running, setRunning] = useState(false); const navigation = useNavigation(); @@ -35,38 +33,24 @@ export const BenchmarkItem: React.FC = ({ useEffect(() => { const run = async () => { - await waitForGc(); - suite.run(multiplier); - suite.state = 'done'; + await suite.run(); setRunning(false); + bumpRunCurrent(); }; - if (running) run(); + if (running) { + run(); + } }, [running]); - function delay(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); - } - - async function waitForGc(): Promise { - await delay(500); - return new Promise(resolve => { - requestAnimationFrame(() => { - InteractionManager.runAfterInteractions(() => { - resolve(); - }); - }); - }); - } - // results handling - const usTime = suite.results.reduce((acc, result) => { - return acc + result.us; + const usTput = suite.results.reduce((acc, result) => { + return acc + (result.us?.throughput.mean || 0); }, 0); - const themTime = suite.results.reduce((acc, result) => { - return acc + result.time; + const themTput = suite.results.reduce((acc, result) => { + return acc + (result.them?.throughput.mean || 0); }, 0); - const times = calculateTimes(usTime, themTime); - const timesStyle = usTime < themTime ? styles.faster : styles.slower; + const times = calculateTimes(usTput, themTput); + const timesStyle = usTput > themTput ? styles.faster : styles.slower; // render component return ( diff --git a/example/src/components/BenchmarkResultItem.tsx b/example/src/components/BenchmarkResultItem.tsx index dc92bd4a..dc0f780d 100644 --- a/example/src/components/BenchmarkResultItem.tsx +++ b/example/src/components/BenchmarkResultItem.tsx @@ -1,17 +1,18 @@ import React from 'react'; import { View, Text, StyleSheet } from 'react-native'; import type { BenchmarkResult } from '../types/benchmarks'; -import { formatNumber } from '../benchmarks/utils'; +import { calculateTimes, formatNumber } from '../benchmarks/utils'; import { colors } from '../styles/colors'; type BenchmarkResultItemProps = { result: BenchmarkResult; }; +type Key = 'throughput' | 'latency'; + export const BenchmarkResultItemHeader: React.FC = () => { return ( -     times rnqc @@ -23,30 +24,42 @@ export const BenchmarkResultItemHeader: React.FC = () => { export const BenchmarkResultItem: React.FC = ({ result, }: BenchmarkResultItemProps) => { - const emoji = result.type === 'faster' ? '🐇' : '🐢'; - const timesType = result.type === 'faster' ? 'faster' : 'slower'; - const timesStyle = timesType === 'faster' ? styles.faster : styles.slower; + const rows = ['throughput', 'latency'].map((key, i) => { + const us = result.us![key as Key].mean; + const them = result.them![key as Key].mean; + const comparison = key === 'throughput' ? us > them : us < them; + const places = key === 'throughput' ? 2 : 3; + const times = calculateTimes(us, them); + const emoji = comparison ? '🐇' : '🐢'; + const timesType = comparison ? 'faster' : 'slower'; + const timesStyle = timesType === 'faster' ? styles.faster : styles.slower; + + return ( + + + {emoji} + + {key} {key === 'throughput' ? '(ops/s)' : '(ms)'} + + + {formatNumber(times, 2, 'x')} + + {formatNumber(us, places, '')} + {formatNumber(them, places, '')} + + + ); + }); return ( - - {emoji} - {result.fnName} - - {formatNumber(result.times, 2, 'x')} - - {formatNumber(result.us, 2, 'ms')} - {formatNumber(result.time, 2, 'ms')} + + {result.benchName} + {rows} challenger {result.challenger} - runs - {result.runCount} - - - notes - {result.notes} ); @@ -60,6 +73,7 @@ const styles = StyleSheet.create({ subContainer: { flexDirection: 'row', paddingHorizontal: 4, + paddingTop: 8, }, text: { flexShrink: 1, @@ -68,12 +82,14 @@ const styles = StyleSheet.create({ }, description: { flex: 3, + fontSize: 10, + alignSelf: 'flex-end', }, value: { fontSize: 10, fontFamily: 'Courier New', minWidth: 60, - textAlign: 'center', + textAlign: 'right', alignSelf: 'flex-end', }, label: { @@ -101,8 +117,10 @@ const styles = StyleSheet.create({ subValue: { flex: 2, }, - notes: { - paddingTop: 2, - flex: 5, + benchName: { + fontSize: 10, + fontWeight: 'bold', + flex: 1, + textAlign: 'left', }, }); diff --git a/example/src/hooks/useBenchmarks.ts b/example/src/hooks/useBenchmarks.ts index 16d7930b..0f8188fc 100644 --- a/example/src/hooks/useBenchmarks.ts +++ b/example/src/hooks/useBenchmarks.ts @@ -1,6 +1,8 @@ -/* eslint-disable @typescript-eslint/no-require-imports */ import { useEffect, useState } from 'react'; import { BenchmarkSuite } from '../benchmarks/benchmarks'; +import ed from '../benchmarks/ed/ed25519'; +import pbkdf2 from '../benchmarks/pbkdf2/pbkdf2'; +import random from '../benchmarks/random/randomBytes'; export const useBenchmarks = (): [ BenchmarkSuite[], @@ -8,17 +10,42 @@ export const useBenchmarks = (): [ () => void, () => void, () => void, + () => void, ] => { const [suites, setSuites] = useState([]); + const [runCurrent, setRunCurrent] = useState(-1); // initial load of benchmark suites useEffect(() => { const newSuites: BenchmarkSuite[] = []; - newSuites.push(random()); - newSuites.push(pbkdf2()); + newSuites.push(new BenchmarkSuite('ed', ed)); + newSuites.push(new BenchmarkSuite('pbkdf2', pbkdf2)); + newSuites.push(new BenchmarkSuite('random', random)); setSuites(newSuites); }, []); + // This jank is used to trick async functions into running synchronously + // so we run one benchmark at a time and have dedicated resources instead of + // conflicting with other benchmarks. + useEffect(() => { + if (runCurrent < 0) return; // not running benchmarks + // reset to -1 if we're past the end + if (runCurrent >= suites.length) { + setRunCurrent(-1); + return; + } + const s = suites[runCurrent]; + if (s?.enabled) { + updateSuites(suite => { + if (suite.name === s.name) { + suite.state = 'running'; + } + }); + } else { + setRunCurrent(runCurrent + 1); + } + }, [runCurrent]); + const updateSuites = (fn: (suite: BenchmarkSuite) => void) => { if (!suites.length) return; const copy = [...suites]; @@ -43,32 +70,13 @@ export const useBenchmarks = (): [ suite.enabled = false; }); - const runBenchmarks = () => - updateSuites(suite => { - if (suite.enabled && suite.state !== 'running') { - suite.state = 'running'; - } - }); - - return [suites, toggle, checkAll, clearAll, runBenchmarks]; -}; + const runBenchmarks = () => { + setRunCurrent(0); + }; -const random = () => { - const suite = new BenchmarkSuite('random'); - suite.addBenchmark(require('../benchmarks/random/randomBytes').randomBytes10); - suite.addBenchmark( - require('../benchmarks/random/randomBytes').randomBytes1024, - ); - return suite; -}; + const bumpRunCurrent = () => { + setRunCurrent(runCurrent + 1); + }; -const pbkdf2 = () => { - const suite = new BenchmarkSuite('pbkdf2'); - suite.addBenchmark( - require('../benchmarks/pbkdf2/pbkdf2').pbkdf2_256_1_32_async, - ); - suite.addBenchmark( - require('../benchmarks/pbkdf2/pbkdf2').pbkdf2_256_1_32_sync, - ); - return suite; + return [suites, toggle, checkAll, clearAll, runBenchmarks, bumpRunCurrent]; }; diff --git a/example/src/hooks/useTestsList.ts b/example/src/hooks/useTestsList.ts index b76ad333..bbe542fa 100644 --- a/example/src/hooks/useTestsList.ts +++ b/example/src/hooks/useTestsList.ts @@ -2,6 +2,7 @@ import { useState, useCallback } from 'react'; import type { TestSuites } from '../types/tests'; import { TestsContext } from '../tests/util'; +import '../tests/ed25519/ed25519_tests'; import '../tests/pbkdf2/pbkdf2_tests'; import '../tests/random/random_tests'; // import '../tests/HmacTests/HmacTests'; diff --git a/example/src/hooks/useTestsRun.ts b/example/src/hooks/useTestsRun.ts index 9e5d04c0..71e4c0a0 100644 --- a/example/src/hooks/useTestsRun.ts +++ b/example/src/hooks/useTestsRun.ts @@ -50,10 +50,10 @@ const run = ( Object.entries(suites).map(([suiteName, suite]) => { stats.suites++; - Object.entries(suite.tests).map(([testName, test]) => { + Object.entries(suite.tests).map(async ([testName, test]) => { if (!suite.value) return; try { - test(); + await test(); stats.passes++; addTestResult({ type: 'correct', diff --git a/example/src/navigators/children/BenchmarkSuitesScreen.tsx b/example/src/navigators/children/BenchmarkSuitesScreen.tsx index 7c8902ce..5ba525f8 100644 --- a/example/src/navigators/children/BenchmarkSuitesScreen.tsx +++ b/example/src/navigators/children/BenchmarkSuitesScreen.tsx @@ -1,41 +1,17 @@ -import React, { useState } from 'react'; -import { - View, - Text, - StyleSheet, - SafeAreaView, - TextInput, - FlatList, -} from 'react-native'; +import React from 'react'; +import { View, Text, StyleSheet, SafeAreaView, FlatList } from 'react-native'; import { BenchmarkItem } from '../../components/BenchmarkItem'; -import { colors } from '../../styles/colors'; import { useBenchmarks } from '../../hooks/useBenchmarks'; import { Button } from '../../components/Button'; export const BenchmarkSuitesScreen = () => { - const [suites, toggle, checkAll, clearAll, runBenchmarks] = useBenchmarks(); - const [multiplier, setMultiplier] = useState(1); + const [suites, toggle, checkAll, clearAll, runBenchmarks, bumpRunCurrent] = + useBenchmarks(); let totalCount = 0; return ( - - - run count multiplier - setMultiplier(parseInt(s, 10))} - /> - - - Each benchmark has a distinct run count. If you want to really - exercise the device, you can increase this number to multiply the run - count of each benchmark. Recommended values are 1-5. - - - { key={index.toString()} suite={item} toggle={() => toggle(item.name)} - multiplier={multiplier} + bumpRunCurrent={bumpRunCurrent} /> ); }} @@ -59,7 +35,7 @@ export const BenchmarkSuitesScreen = () => {