Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/implementation-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ These algorithms provide quantum-resistant cryptography.
* ❌ `subtle.decapsulateKey(decapsulationAlgorithm, decapsulationKey, ciphertext, sharedKeyAlgorithm, extractable, usages)`
* ✅ `subtle.decrypt(algorithm, key, data)`
* 🚧 `subtle.deriveBits(algorithm, baseKey, length)`
* `subtle.deriveKey(algorithm, baseKey, derivedKeyAlgorithm, extractable, keyUsages)`
* 🚧 `subtle.deriveKey(algorithm, baseKey, derivedKeyAlgorithm, extractable, keyUsages)`
* 🚧 `subtle.digest(algorithm, data)`
* ❌ `subtle.encapsulateBits(encapsulationAlgorithm, encapsulationKey)`
* ❌ `subtle.encapsulateKey(encapsulationAlgorithm, encapsulationKey, sharedKeyAlgorithm, extractable, usages)`
Expand Down
8 changes: 4 additions & 4 deletions example/src/benchmarks/benchmarks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ export class BenchmarkSuite {

async run() {
this.results = [];
const promises = this.benchmarks.map(async benchFn => {
// Run benchmarks sequentially to avoid timing interference
for (const benchFn of this.benchmarks) {
const b = await benchFn();
await b.run();
this.processResults(b);
this.state = 'done';
});
await Promise.all(promises);
}
this.state = 'done';
}

processResults = (b: Bench): void => {
Expand Down
45 changes: 45 additions & 0 deletions example/src/benchmarks/cipher/cipher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import rnqc from 'react-native-quick-crypto';
// @ts-expect-error - crypto-browserify is not typed
import browserify from 'crypto-browserify';
import { gcm } from '@noble/ciphers/aes.js';
import type { BenchFn } from '../../types/benchmarks';
import { Bench } from 'tinybench';
import { buffer1MB } from '../testData';

// Generate a key for AES-256-GCM
const key = rnqc.randomBytes(32);
const iv = rnqc.randomBytes(12);

// @noble requires Uint8Array
const nobleKey = new Uint8Array(key);
const nobleIv = new Uint8Array(iv);

const cipher_aes256gcm_1mb_buffer: BenchFn = () => {
const bench = new Bench({
name: 'cipher aes256gcm 1MB Buffer',
iterations: 1,
warmupIterations: 0,
});

const nobleData = new Uint8Array(buffer1MB);

bench
.add('rnqc', () => {
const cipher = rnqc.createCipheriv('aes-256-gcm', key, iv);
cipher.update(buffer1MB);
cipher.final();
})
.add('@noble/ciphers/aes', () => {
const cipher = gcm(nobleKey, nobleIv);
cipher.encrypt(nobleData);
})
.add('browserify', () => {
const cipher = browserify.createCipheriv('aes-256-gcm', key, iv);
cipher.update(buffer1MB);
cipher.final();
});

return bench;
};

export default [cipher_aes256gcm_1mb_buffer];
118 changes: 118 additions & 0 deletions example/src/benchmarks/hash/hash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import rnqc from 'react-native-quick-crypto';
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 { text1MB, text8MB, buffer1MB, buffer8MB } from '../testData';

const hash_sha256_8mb_string: BenchFn = () => {
const bench = new Bench({
name: 'hash sha256 8MB string',
iterations: 3,
warmupIterations: 1,
time: 0,
});

bench
.add('rnqc', () => {
const hash = rnqc.createHash('sha256');
hash.update(text8MB);
hash.digest('hex');
})
.add('@noble/hashes/sha256', () => {
sha256(text8MB);
})
.add('browserify', () => {
const hash = browserify.createHash('sha256');
hash.update(text8MB);
hash.digest('hex');
});

return bench;
};

const hash_sha256_1mb_string: BenchFn = () => {
const bench = new Bench({
name: 'hash sha256 1MB string',
iterations: 5,
warmupIterations: 2,
time: 0,
});

bench
.add('rnqc', () => {
const hash = rnqc.createHash('sha256');
hash.update(text1MB);
hash.digest('hex');
})
.add('@noble/hashes/sha256', () => {
sha256(text1MB);
})
.add('browserify', () => {
const hash = browserify.createHash('sha256');
hash.update(text1MB);
hash.digest('hex');
});

return bench;
};

const hash_sha256_8mb_buffer: BenchFn = () => {
const bench = new Bench({
name: 'hash sha256 8MB Buffer',
iterations: 3,
warmupIterations: 1,
time: 0,
});

bench
.add('rnqc', () => {
const hash = rnqc.createHash('sha256');
hash.update(buffer8MB);
hash.digest('hex');
})
.add('@noble/hashes/sha256', () => {
sha256(buffer8MB);
})
.add('browserify', () => {
const hash = browserify.createHash('sha256');
hash.update(buffer8MB);
hash.digest('hex');
});

return bench;
};

const hash_sha256_1mb_buffer: BenchFn = () => {
const bench = new Bench({
name: 'hash sha256 1MB Buffer',
iterations: 5,
warmupIterations: 2,
time: 0,
});

bench
.add('rnqc', () => {
const hash = rnqc.createHash('sha256');
hash.update(buffer1MB);
hash.digest('hex');
})
.add('@noble/hashes/sha256', () => {
sha256(buffer1MB);
})
.add('browserify', () => {
const hash = browserify.createHash('sha256');
hash.update(buffer1MB);
hash.digest('hex');
});

return bench;
};

export default [
hash_sha256_1mb_string,
hash_sha256_1mb_buffer,
hash_sha256_8mb_string,
hash_sha256_8mb_buffer,
];
121 changes: 121 additions & 0 deletions example/src/benchmarks/hmac/hmac.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import rnqc from 'react-native-quick-crypto';
// @ts-expect-error - crypto-browserify is not typed
import browserify from 'crypto-browserify';
import { hmac } from '@noble/hashes/hmac';
import { sha256 } from '@noble/hashes/sha2';
import type { BenchFn } from '../../types/benchmarks';
import { Bench } from 'tinybench';
import { text1MB, text8MB, buffer1MB, buffer8MB } from '../testData';

const hmacKey = 'test-key-for-hmac-benchmarks';

const hmac_sha256_8mb_string: BenchFn = () => {
const bench = new Bench({
name: 'hmac sha256 8MB string',
iterations: 3,
warmupIterations: 1,
time: 0,
});

bench
.add('rnqc', () => {
const h = rnqc.createHmac('sha256', hmacKey);
h.update(text8MB);
h.digest('hex');
})
.add('@noble/hashes/hmac', () => {
hmac(sha256, hmacKey, text8MB);
})
.add('browserify', () => {
const h = browserify.createHmac('sha256', hmacKey);
h.update(text8MB);
h.digest('hex');
});

return bench;
};

const hmac_sha256_1mb_string: BenchFn = () => {
const bench = new Bench({
name: 'hmac sha256 1MB string',
iterations: 5,
warmupIterations: 2,
time: 0,
});

bench
.add('rnqc', () => {
const h = rnqc.createHmac('sha256', hmacKey);
h.update(text1MB);
h.digest('hex');
})
.add('@noble/hashes/hmac', () => {
hmac(sha256, hmacKey, text1MB);
})
.add('browserify', () => {
const h = browserify.createHmac('sha256', hmacKey);
h.update(text1MB);
h.digest('hex');
});

return bench;
};

const hmac_sha256_8mb_buffer: BenchFn = () => {
const bench = new Bench({
name: 'hmac sha256 8MB Buffer',
iterations: 3,
warmupIterations: 1,
time: 0,
});

bench
.add('rnqc', () => {
const h = rnqc.createHmac('sha256', hmacKey);
h.update(buffer8MB);
h.digest('hex');
})
.add('@noble/hashes/hmac', () => {
hmac(sha256, hmacKey, buffer8MB);
})
.add('browserify', () => {
const h = browserify.createHmac('sha256', hmacKey);
h.update(buffer8MB);
h.digest('hex');
});

return bench;
};

const hmac_sha256_1mb_buffer: BenchFn = () => {
const bench = new Bench({
name: 'hmac sha256 1MB Buffer',
iterations: 5,
warmupIterations: 2,
time: 0,
});

bench
.add('rnqc', () => {
const h = rnqc.createHmac('sha256', hmacKey);
h.update(buffer1MB);
h.digest('hex');
})
.add('@noble/hashes/hmac', () => {
hmac(sha256, hmacKey, buffer1MB);
})
.add('browserify', () => {
const h = browserify.createHmac('sha256', hmacKey);
h.update(buffer1MB);
h.digest('hex');
});

return bench;
};

export default [
hmac_sha256_1mb_string,
hmac_sha256_1mb_buffer,
hmac_sha256_8mb_string,
hmac_sha256_8mb_buffer,
];
20 changes: 20 additions & 0 deletions example/src/benchmarks/testData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Shared test data for benchmarks
// Generate test data of different sizes using repeating pattern
const generateString = (sizeInMB: number): string => {
const chunk =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const bytesPerMB = 1024 * 1024;
const totalBytes = Math.floor(sizeInMB * bytesPerMB);
const repeatCount = Math.ceil(totalBytes / chunk.length);
return chunk.repeat(repeatCount).substring(0, totalBytes);
};

// Pre-generate test data once for all benchmarks
export const text100KB = generateString(0.1);
export const text1MB = generateString(1);
export const text8MB = generateString(8);

// Pre-generate Buffer versions for comparison
export const buffer100KB = Buffer.from(text100KB);
export const buffer1MB = Buffer.from(text1MB);
export const buffer8MB = Buffer.from(text8MB);
40 changes: 34 additions & 6 deletions example/src/components/BenchmarkResultItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,36 @@ export const BenchmarkResultItemHeader: React.FC = () => {
export const BenchmarkResultItem: React.FC<BenchmarkResultItemProps> = ({
result,
}: BenchmarkResultItemProps) => {
// Check if benchmark errored out
const usHasError = result.us?.error !== undefined;
const themHasError = result.them?.error !== undefined;

if (usHasError || themHasError) {
return (
<View>
<View style={styles.subContainer}>
<Text style={[styles.sub, styles.benchName]}>{result.benchName}</Text>
</View>
<View style={styles.subContainer}>
<Text style={[styles.sub, styles.subLabel]}>error</Text>
<Text style={[styles.sub, styles.subValue, styles.slower]}>
{usHasError ? 'rnqc failed' : ''}
{usHasError && themHasError ? ' / ' : ''}
{themHasError ? `${result.challenger} failed` : ''}
</Text>
</View>
</View>
);
}

const hasComparison = result.them !== undefined;

const rows = ['throughput', 'latency'].map((key, i) => {
const us = result.us![key as Key].mean;
const them = result.them![key as Key].mean;
const them = hasComparison ? result.them![key as Key].mean : 0;
const comparison = key === 'throughput' ? us > them : us < them;
const places = key === 'throughput' ? 2 : 3;
const times = calculateTimes(us, them);
const times = hasComparison ? calculateTimes(us, them) : 0;
const emoji = comparison ? '🐇' : '🐢';
const timesType = comparison ? 'faster' : 'slower';
const timesStyle = timesType === 'faster' ? styles.faster : styles.slower;
Expand All @@ -41,11 +65,15 @@ export const BenchmarkResultItem: React.FC<BenchmarkResultItemProps> = ({
<Text style={[styles.text, styles.description]}>
{key} {key === 'throughput' ? '(ops/s)' : '(ms)'}
</Text>
<Text style={[styles.value, timesStyle]}>
{formatNumber(times, 2, 'x')}
</Text>
{hasComparison && (
<Text style={[styles.value, timesStyle]}>
{formatNumber(times, 2, 'x')}
</Text>
)}
<Text style={styles.value}>{formatNumber(us, places, '')}</Text>
<Text style={styles.value}>{formatNumber(them, places, '')}</Text>
{hasComparison && (
<Text style={styles.value}>{formatNumber(them, places, '')}</Text>
)}
</View>
</View>
);
Expand Down
Loading
Loading