Skip to content

Commit 966ee6a

Browse files
committed
feat: add ed25519
1 parent 084d2c2 commit 966ee6a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1711
-1836
lines changed

.github/workflows/validate-cpp.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ jobs:
2727
github_token: ${{ secrets.github_token }}
2828
reporter: github-pr-review
2929
flags: --linelength=230
30-
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++
30+
targets: --recursive packages/react-native-quick-crypto/cpp packages/react-native-quick-crypto/android/src/main/cpp
3131
filter: "-legal/copyright\
3232
,-readability/todo\
3333
,-build/namespaces\
3434
,-whitespace/comments\
3535
,-build/include_order\
36+
,-whitespace/indent_namespace\
37+
,-whitespace/parens\
3638
"

bun.lockb

1.11 KB
Binary file not shown.

docs/implementation-coverage.md

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ This document attempts to describe the implementation status of Crypto APIs/Inte
111111
*`crypto.diffieHellman(options)`
112112
*`crypto.hash(algorithm, data[, outputEncoding])`
113113
*`crypto.generateKey(type, options, callback)`
114-
* `crypto.generateKeyPair(type, options, callback)`
115-
* `crypto.generateKeyPairSync(type, options)`
114+
* 🚧 `crypto.generateKeyPair(type, options, callback)`
115+
* 🚧 `crypto.generateKeyPairSync(type, options)`
116116
*`crypto.generateKeySync(type, options)`
117117
*`crypto.generatePrime(size[, options[, callback]])`
118118
*`crypto.generatePrimeSync(size[, options])`
@@ -141,10 +141,10 @@ This document attempts to describe the implementation status of Crypto APIs/Inte
141141
*`crypto.secureHeapUsed()`
142142
*`crypto.setEngine(engine[, flags])`
143143
*`crypto.setFips(bool)`
144-
* `crypto.sign(algorithm, data, key[, callback])`
144+
* 🚧 `crypto.sign(algorithm, data, key[, callback])`
145145
*`crypto.subtle` (see below)
146146
*`crypto.timingSafeEqual(a, b)`
147-
* `crypto.verify(algorithm, data, key, signature[, callback])`
147+
* 🚧 `crypto.verify(algorithm, data, key, signature[, callback])`
148148
*`crypto.webcrypto` (see below)
149149

150150
🚧 Details below still a work in progress 🚧
@@ -162,10 +162,10 @@ This document attempts to describe the implementation status of Crypto APIs/Inte
162162
| `rsa-pss` ||
163163
| `dsa` ||
164164
| `ec` ||
165-
| `ed25519` | |
166-
| `ed448` | |
167-
| `x25519` | |
168-
| `x448` | |
165+
| `ed25519` | |
166+
| `ed448` | |
167+
| `x25519` | |
168+
| `x448` | |
169169
| `dh` ||
170170

171171
## `crypto.generateKeyPairSync`
@@ -175,10 +175,10 @@ This document attempts to describe the implementation status of Crypto APIs/Inte
175175
| `rsa-pss` ||
176176
| `dsa` ||
177177
| `ec` ||
178-
| `ed25519` | |
179-
| `ed448` | |
180-
| `x25519` | |
181-
| `x448` | |
178+
| `ed25519` | |
179+
| `ed448` | |
180+
| `x25519` | |
181+
| `x448` | |
182182
| `dh` ||
183183

184184
## `crypto.generateKeySync`
@@ -187,6 +187,25 @@ This document attempts to describe the implementation status of Crypto APIs/Inte
187187
| `aes` ||
188188
| `hmac` ||
189189

190+
## `crypto.sign`
191+
| Algorithm | Status |
192+
| --------- | :----: |
193+
| `RSASSA-PKCS1-v1_5` | |
194+
| `RSA-PSS` | |
195+
| `ECDSA` | |
196+
| `Ed25519` ||
197+
| `Ed448` ||
198+
| `HMAC` | |
199+
200+
## `crypto.verify`
201+
| Algorithm | Status |
202+
| --------- | :----: |
203+
| `RSASSA-PKCS1-v1_5` | |
204+
| `RSA-PSS` | |
205+
| `ECDSA` | |
206+
| `Ed25519` ||
207+
| `Ed448` ||
208+
| `HMAC` | |
190209

191210
# `WebCrypto`
192211

example/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import { install } from 'react-native-quick-crypto';
33
install();
44

5+
// event-target-shim
6+
import 'event-target-polyfill';
7+
58
// readable-stream
69
// @ts-expect-error - although process.version is readonly, we're setting it for readable-stream
710
global.process.version = 'v22.0.0';

example/ios/Podfile.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ PODS:
2828
- ReactCommon/turbomodule/bridging
2929
- ReactCommon/turbomodule/core
3030
- Yoga
31-
- OpenSSL-Universal (3.2.2000)
32-
- QuickCrypto (1.0.0-beta.3):
31+
- OpenSSL-Universal (3.3.2000)
32+
- QuickCrypto (1.0.0-beta.5):
3333
- DoubleConversion
3434
- glog
3535
- hermes-engine
@@ -1937,8 +1937,8 @@ SPEC CHECKSUMS:
19371937
glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a
19381938
hermes-engine: 46f1ffbf0297f4298862068dd4c274d4ac17a1fd
19391939
NitroModules: 69a6524b390ed8ca220e15f00bcfbd2f7c24472e
1940-
OpenSSL-Universal: f8a9c4fdab7e21cb70bda471c269e86e9212439c
1941-
QuickCrypto: 452b6fe586fa5c0a93e7ccca0a619387763d5adf
1940+
OpenSSL-Universal: b60a3702c9fea8b3145549d421fdb018e53ab7b4
1941+
QuickCrypto: 8d76ae3a0bf60509f671193eb4ed666a80da34cb
19421942
RCT-Folly: 84578c8756030547307e4572ab1947de1685c599
19431943
RCTDeprecation: fde92935b3caa6cb65cbff9fbb7d3a9867ffb259
19441944
RCTRequired: 75c6cee42d21c1530a6f204ba32ff57335d19007

example/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,27 @@
2121
},
2222
"dependencies": {
2323
"@craftzdog/react-native-buffer": "6.0.5",
24+
"@noble/curves": "^1.7.0",
2425
"@noble/hashes": "^1.5.0",
2526
"@react-navigation/bottom-tabs": "^6.6.1",
2627
"@react-navigation/native": "6.1.18",
2728
"@react-navigation/native-stack": "6.11.0",
2829
"buffer": "6.0.3",
2930
"chai": "<5.0.0",
3031
"crypto-browserify": "^3.12.0",
32+
"event-target-polyfill": "^0.0.4",
3133
"events": "3.3.0",
3234
"react": "18.3.1",
3335
"react-native": "0.76.1",
3436
"react-native-bouncy-checkbox": "4.0.1",
3537
"react-native-nitro-modules": "0.14.0",
3638
"react-native-quick-base64": "2.1.2",
37-
"react-native-quick-crypto": "1.0.0-beta.5",
39+
"react-native-quick-crypto": "workspace:*",
3840
"react-native-safe-area-context": "4.14.0",
3941
"react-native-screens": "3.35.0",
4042
"react-native-vector-icons": "^10.1.0",
4143
"readable-stream": "4.5.2",
44+
"tinybench": "^3.0.6",
4245
"util": "0.12.5"
4346
},
4447
"devDependencies": {
Lines changed: 23 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,50 @@
1-
import type {
2-
BenchmarkFn,
3-
BenchmarkResult,
4-
Challenger,
5-
ImportedBenchmark,
6-
SuiteState,
7-
} from '../types/benchmarks';
8-
import { calculateTimes } from './utils';
1+
import type { Bench } from 'tinybench';
2+
import type { BenchFn, BenchmarkResult, SuiteState } from '../types/benchmarks';
93

104
export class BenchmarkSuite {
115
name: string;
126
enabled: boolean;
13-
benchmarks: Benchmark[];
7+
benchmarks: BenchFn[];
148
state: SuiteState;
159
results: BenchmarkResult[] = [];
1610

17-
constructor(name: string) {
11+
constructor(name: string, benchmarks: BenchFn[]) {
1812
this.name = name;
1913
this.enabled = false;
2014
this.state = 'idle';
21-
this.benchmarks = [];
15+
this.benchmarks = benchmarks;
2216
this.results = [];
2317
}
2418

25-
addBenchmark(imported: ImportedBenchmark) {
26-
this.benchmarks.push(new Benchmark(imported));
27-
}
28-
2919
addResult(result: BenchmarkResult) {
3020
this.results.push(result);
3121
}
3222

33-
run(multiplier: number = 1) {
23+
async run() {
3424
this.results = [];
35-
this.benchmarks.forEach(benchmark => {
36-
benchmark.run(this, multiplier);
25+
const promises = this.benchmarks.map(async benchFn => {
26+
const b = await benchFn();
27+
await b.run();
28+
this.processResults(b);
29+
this.state = 'done';
3730
});
31+
await Promise.all(promises);
3832
}
39-
}
4033

41-
export class Benchmark {
42-
name: string; // function name
43-
runCount: number;
44-
us?: BenchmarkFn;
45-
them: Challenger[];
46-
47-
constructor(benchmark: ImportedBenchmark) {
48-
this.name = benchmark.name;
49-
this.runCount = benchmark.runCount;
50-
this.us = benchmark.us;
51-
this.them = benchmark.them;
52-
}
34+
processResults = (b: Bench): void => {
35+
const tasks = b.tasks;
36+
const us = tasks.find(t => t.name === 'rnqc');
37+
const themTasks = tasks.filter(t => t.name !== 'rnqc');
5338

54-
run(suite: BenchmarkSuite, multiplier: number = 1) {
55-
const usTime = this.timeFn(this.us!, multiplier);
56-
this.them.forEach(them => {
57-
const themTime = this.timeFn(them.fn, multiplier);
58-
const type = usTime < themTime ? 'faster' : 'slower';
59-
const times = calculateTimes(usTime, themTime);
60-
const result: BenchmarkResult = {
39+
themTasks.map(them => {
40+
this.addResult({
6141
errorMsg: undefined,
6242
challenger: them.name,
63-
notes: them.notes,
64-
runCount: this.runCount * multiplier,
65-
fnName: this.name,
66-
time: themTime,
67-
us: usTime,
68-
type,
69-
times,
70-
};
71-
suite.addResult(result);
43+
notes: '',
44+
benchName: b.name,
45+
them: them.result,
46+
us: us?.result,
47+
});
7248
});
73-
}
74-
75-
/**
76-
* @returns time in ms
77-
*/
78-
timeFn = (fn: BenchmarkFn, multiplier: number = 1): number => {
79-
// warm up imports, etc.
80-
fn();
81-
82-
const totalRunCount = this.runCount * multiplier;
83-
84-
// do the actual benchmark
85-
const start = performance.now();
86-
for (let i = 0; i < totalRunCount; i++) {
87-
fn();
88-
}
89-
const end = performance.now();
90-
return end - start;
9149
};
9250
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { Bench } from 'tinybench';
2+
import rnqc from 'react-native-quick-crypto';
3+
import { ed25519 as noble } from '@noble/curves/ed25519';
4+
import type { BenchFn } from '../../types/benchmarks';
5+
6+
const TIME_MS = 1000;
7+
8+
const ed25519_sign_verify_async: BenchFn = async () => {
9+
const message = 'hello world';
10+
const buffer = Buffer.from(message);
11+
const ab = buffer.buffer;
12+
const arr = new Uint8Array(buffer);
13+
14+
// rnqc setup
15+
const ed = new rnqc.Ed('ed25519', {});
16+
await ed.generateKeyPair();
17+
18+
// noble setup
19+
const noblePrivateKey = noble.utils.randomPrivateKey();
20+
const noblePublicKey = noble.getPublicKey(noblePrivateKey);
21+
22+
const bench = new Bench({
23+
name: 'ed25519 sign/verify (async)',
24+
time: TIME_MS,
25+
});
26+
27+
bench.add('rnqc', async () => {
28+
const signature = await ed.sign(ab);
29+
const verified = await ed.verify(signature, ab);
30+
if (!verified) {
31+
throw new Error('Signature verification failed');
32+
}
33+
});
34+
35+
bench.add('@noble/curves/ed25519', () => {
36+
const signature = noble.sign(arr, noblePrivateKey);
37+
const verified = noble.verify(signature, arr, noblePublicKey);
38+
if (!verified) {
39+
throw new Error('Signature verification failed');
40+
}
41+
});
42+
43+
bench.warmupTime = 100;
44+
return bench;
45+
};
46+
47+
const ed25519_sign_verify_sync: BenchFn = () => {
48+
const message = 'hello world';
49+
const buffer = Buffer.from(message);
50+
const ab = buffer.buffer;
51+
const arr = new Uint8Array(buffer);
52+
53+
// rnqc setup
54+
const ed = new rnqc.Ed('ed25519', {});
55+
ed.generateKeyPairSync();
56+
57+
// noble setup
58+
const noblePrivateKey = noble.utils.randomPrivateKey();
59+
const noblePublicKey = noble.getPublicKey(noblePrivateKey);
60+
61+
const bench = new Bench({
62+
name: 'ed25519 sign/verify (sync)',
63+
time: TIME_MS,
64+
});
65+
66+
bench.add('rnqc', () => {
67+
const signature = ed.signSync(ab);
68+
const verified = ed.verifySync(signature, ab);
69+
if (!verified) {
70+
throw new Error('Signature verification failed');
71+
}
72+
});
73+
74+
bench.add('@noble/curves/ed25519', () => {
75+
const signature = noble.sign(arr, noblePrivateKey);
76+
const verified = noble.verify(signature, arr, noblePublicKey);
77+
if (!verified) {
78+
throw new Error('Signature verification failed');
79+
}
80+
});
81+
82+
bench.warmupTime = 100;
83+
return bench;
84+
};
85+
86+
export default [ed25519_sign_verify_async, ed25519_sign_verify_sync];

0 commit comments

Comments
 (0)