Skip to content

Commit 3252011

Browse files
authored
feature: add ed25519 (#534)
1 parent 084d2c2 commit 3252011

Some content is hidden

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

58 files changed

+1792
-1875
lines changed

.github/workflows/validate-cpp.yml

+3-1
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

+31-12
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

+3
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

+8-6
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,21 @@ PODS:
77
- hermes-engine (0.76.1):
88
- hermes-engine/Pre-built (= 0.76.1)
99
- hermes-engine/Pre-built (0.76.1)
10-
- NitroModules (0.14.0):
10+
- NitroModules (0.18.1):
1111
- DoubleConversion
1212
- glog
1313
- hermes-engine
1414
- RCT-Folly (= 2024.01.01.00)
1515
- RCTRequired
1616
- RCTTypeSafety
17+
- React-callinvoker
1718
- React-Core
1819
- React-debug
1920
- React-Fabric
2021
- React-featureflags
2122
- React-graphics
2223
- React-ImageManager
24+
- React-jsi
2325
- React-NativeModulesApple
2426
- React-RCTFabric
2527
- React-rendererdebug
@@ -28,8 +30,8 @@ PODS:
2830
- ReactCommon/turbomodule/bridging
2931
- ReactCommon/turbomodule/core
3032
- Yoga
31-
- OpenSSL-Universal (3.2.2000)
32-
- QuickCrypto (1.0.0-beta.3):
33+
- OpenSSL-Universal (3.3.2000)
34+
- QuickCrypto (1.0.0-beta.5):
3335
- DoubleConversion
3436
- glog
3537
- hermes-engine
@@ -1936,9 +1938,9 @@ SPEC CHECKSUMS:
19361938
fmt: 10c6e61f4be25dc963c36bd73fc7b1705fe975be
19371939
glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a
19381940
hermes-engine: 46f1ffbf0297f4298862068dd4c274d4ac17a1fd
1939-
NitroModules: 69a6524b390ed8ca220e15f00bcfbd2f7c24472e
1940-
OpenSSL-Universal: f8a9c4fdab7e21cb70bda471c269e86e9212439c
1941-
QuickCrypto: 452b6fe586fa5c0a93e7ccca0a619387763d5adf
1941+
NitroModules: 55f64932b4581a7d02103bc35b84c7bd3204106b
1942+
OpenSSL-Universal: b60a3702c9fea8b3145549d421fdb018e53ab7b4
1943+
QuickCrypto: 8d76ae3a0bf60509f671193eb4ed666a80da34cb
19421944
RCT-Folly: 84578c8756030547307e4572ab1947de1685c599
19431945
RCTDeprecation: fde92935b3caa6cb65cbff9fbb7d3a9867ffb259
19441946
RCTRequired: 75c6cee42d21c1530a6f204ba32ff57335d19007

example/package.json

+8-3
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",
35-
"react-native-nitro-modules": "0.14.0",
37+
"react-native-nitro-modules": "0.18.1",
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": {
@@ -86,7 +89,9 @@
8689
"@release-it/bumper": {
8790
"out": {
8891
"file": "package.json",
89-
"path": ["dependencies.react-native-quick-crypto"]
92+
"path": [
93+
"dependencies.react-native-quick-crypto"
94+
]
9095
}
9196
}
9297
}

example/src/benchmarks/benchmarks.ts

+30-65
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,57 @@
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[] = [];
10+
notes?: Record<string, string>;
1611

17-
constructor(name: string) {
12+
constructor(
13+
name: string,
14+
benchmarks: BenchFn[],
15+
notes?: Record<string, string>,
16+
) {
1817
this.name = name;
1918
this.enabled = false;
2019
this.state = 'idle';
21-
this.benchmarks = [];
20+
this.benchmarks = benchmarks;
2221
this.results = [];
23-
}
24-
25-
addBenchmark(imported: ImportedBenchmark) {
26-
this.benchmarks.push(new Benchmark(imported));
22+
this.notes = notes;
2723
}
2824

2925
addResult(result: BenchmarkResult) {
3026
this.results.push(result);
3127
}
3228

33-
run(multiplier: number = 1) {
29+
async run() {
3430
this.results = [];
35-
this.benchmarks.forEach(benchmark => {
36-
benchmark.run(this, multiplier);
31+
const promises = this.benchmarks.map(async benchFn => {
32+
const b = await benchFn();
33+
await b.run();
34+
this.processResults(b);
35+
this.state = 'done';
3736
});
37+
await Promise.all(promises);
3838
}
39-
}
4039

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-
}
40+
processResults = (b: Bench): void => {
41+
const tasks = b.tasks;
42+
const us = tasks.find(t => t.name === 'rnqc');
43+
const themTasks = tasks.filter(t => t.name !== 'rnqc');
5344

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 = {
45+
themTasks.map(them => {
46+
const notes = this.notes?.[them.name] ?? '';
47+
this.addResult({
6148
errorMsg: undefined,
6249
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);
50+
notes,
51+
benchName: b.name,
52+
them: them.result,
53+
us: us?.result,
54+
});
7255
});
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;
9156
};
9257
}

example/src/benchmarks/ed/ed25519.ts

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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+
const message = 'hello world';
8+
const buffer = Buffer.from(message);
9+
const ab = buffer.buffer;
10+
const arr = new Uint8Array(buffer);
11+
12+
const ed25519_sign_verify_async: BenchFn = async () => {
13+
// rnqc setup
14+
const ed = new rnqc.Ed('ed25519', {});
15+
await ed.generateKeyPair();
16+
17+
// noble setup
18+
const noblePrivateKey = noble.utils.randomPrivateKey();
19+
const noblePublicKey = noble.getPublicKey(noblePrivateKey);
20+
21+
const bench = new Bench({
22+
name: 'ed25519 sign/verify (async)',
23+
time: TIME_MS,
24+
});
25+
26+
bench.add('rnqc', async () => {
27+
const signature = await ed.sign(ab);
28+
const verified = await ed.verify(signature, ab);
29+
if (!verified) {
30+
throw new Error('Signature verification failed');
31+
}
32+
});
33+
34+
bench.add('@noble/curves/ed25519', () => {
35+
const signature = noble.sign(arr, noblePrivateKey);
36+
const verified = noble.verify(signature, arr, noblePublicKey);
37+
if (!verified) {
38+
throw new Error('Signature verification failed');
39+
}
40+
});
41+
42+
bench.warmupTime = 100;
43+
return bench;
44+
};
45+
46+
const ed25519_sign_verify_sync: BenchFn = () => {
47+
// rnqc setup
48+
const ed = new rnqc.Ed('ed25519', {});
49+
ed.generateKeyPairSync();
50+
51+
// noble setup
52+
const noblePrivateKey = noble.utils.randomPrivateKey();
53+
const noblePublicKey = noble.getPublicKey(noblePrivateKey);
54+
55+
const bench = new Bench({
56+
name: 'ed25519 sign/verify (sync)',
57+
time: TIME_MS,
58+
});
59+
60+
bench.add('rnqc', () => {
61+
const signature = ed.signSync(ab);
62+
const verified = ed.verifySync(signature, ab);
63+
if (!verified) {
64+
throw new Error('Signature verification failed');
65+
}
66+
});
67+
68+
bench.add('@noble/curves/ed25519', () => {
69+
const signature = noble.sign(arr, noblePrivateKey);
70+
const verified = noble.verify(signature, arr, noblePublicKey);
71+
if (!verified) {
72+
throw new Error('Signature verification failed');
73+
}
74+
});
75+
76+
bench.warmupTime = 100;
77+
return bench;
78+
};
79+
80+
export default [ed25519_sign_verify_async, ed25519_sign_verify_sync];

0 commit comments

Comments
 (0)