Skip to content

Commit 1e10f9b

Browse files
authored
fix: update ncrypto submodule and expand wrap/unwrap coverage (#914)
1 parent 2e091fa commit 1e10f9b

7 files changed

Lines changed: 249 additions & 39 deletions

File tree

.docs/implementation-coverage.md

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -435,39 +435,39 @@ These ciphers are **not available in Node.js** but are provided by RNQC via libs
435435
### wrapping algorithms
436436
| Algorithm | Status |
437437
| ------------------- | :----: |
438-
| `AES-CBC` | |
439-
| `AES-CTR` | |
438+
| `AES-CBC` | |
439+
| `AES-CTR` | |
440440
| `AES-GCM` ||
441441
| `AES-KW` ||
442442
| `AES-OCB` ||
443443
| `ChaCha20-Poly1305` ||
444-
| `RSA-OAEP` | |
444+
| `RSA-OAEP` | |
445445

446446
### unwrapped key algorithms
447447
| Algorithm | Status |
448448
| --------- | :----: |
449-
| `AES-CBC` | |
450-
| `AES-CTR` | |
451-
| `AES-GCM` | |
452-
| `AES-KW` | |
449+
| `AES-CBC` | |
450+
| `AES-CTR` | |
451+
| `AES-GCM` | |
452+
| `AES-KW` | |
453453
| `AES-OCB` ||
454-
| `ChaCha20-Poly1305` | |
455-
| `ECDH` | |
456-
| `ECDSA` | |
457-
| `Ed25519` | |
458-
| `Ed448` | |
459-
| `HMAC` | |
460-
| `ML-DSA-44` | |
461-
| `ML-DSA-65` | |
462-
| `ML-DSA-87` | |
454+
| `ChaCha20-Poly1305` | |
455+
| `ECDH` | |
456+
| `ECDSA` | |
457+
| `Ed25519` | |
458+
| `Ed448` | |
459+
| `HMAC` | |
460+
| `ML-DSA-44` | |
461+
| `ML-DSA-65` | |
462+
| `ML-DSA-87` | |
463463
| `ML-KEM-512` ||
464464
| `ML-KEM-768` ||
465465
| `ML-KEM-1024` ||
466-
| `RSA-OAEP` | |
467-
| `RSA-PSS` | |
468-
| `RSASSA-PKCS1-v1_5` | |
469-
| `X25519` | |
470-
| `X448` | |
466+
| `RSA-OAEP` | |
467+
| `RSA-PSS` | |
468+
| `RSASSA-PKCS1-v1_5` | |
469+
| `X25519` | |
470+
| `X448` | |
471471

472472
## `subtle.verify`
473473
| Algorithm | Status |
@@ -487,10 +487,10 @@ These ciphers are **not available in Node.js** but are provided by RNQC via libs
487487
### wrapping algorithms
488488
| Algorithm | Status |
489489
| ------------------- | :----: |
490-
| `AES-CBC` | |
491-
| `AES-CTR` | |
490+
| `AES-CBC` | |
491+
| `AES-CTR` | |
492492
| `AES-GCM` ||
493493
| `AES-KW` ||
494494
| `AES-OCB` ||
495495
| `ChaCha20-Poly1305` ||
496-
| `RSA-OAEP` | |
496+
| `RSA-OAEP` | |

.gitmodules

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33
url = https://github.com/BLAKE3-team/BLAKE3.git
44
[submodule "packages/react-native-quick-crypto/deps/ncrypto"]
55
path = packages/react-native-quick-crypto/deps/ncrypto
6-
url = https://github.com/boorad/ncrypto.git
7-
branch = fix/use-BN_GENCB_get_arg
6+
url = https://github.com/nodejs/ncrypto.git

example/ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2811,7 +2811,7 @@ SPEC CHECKSUMS:
28112811
MMKVCore: f2dd4c9befea04277a55e84e7812f930537993df
28122812
NitroMmkv: afbc5b2fbf963be567c6c545aa1efcf6a9cec68e
28132813
NitroModules: 11bba9d065af151eae51e38a6425e04c3b223ff3
2814-
QuickCrypto: ac4d2eead1de738bcf06f565c4ab31db817f88c1
2814+
QuickCrypto: e6f07c200580a5958c7233dbfed34e6df99b3891
28152815
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
28162816
RCTDeprecation: c4b9e2fd0ab200e3af72b013ed6113187c607077
28172817
RCTRequired: e97dd5dafc1db8094e63bc5031e0371f092ae92a

example/src/tests/subtle/wrap_unwrap.ts

Lines changed: 219 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import { test } from '../util';
22
import { expect } from 'chai';
33
import { subtle, getRandomValues } from 'react-native-quick-crypto';
4-
import { CryptoKey } from 'react-native-quick-crypto';
5-
6-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
7-
const subtleAny = subtle as any;
4+
import type { CryptoKey, CryptoKeyPair } from 'react-native-quick-crypto';
85

96
const SUITE = 'subtle.wrapKey/unwrapKey';
107

@@ -22,14 +19,14 @@ test(SUITE, 'wrap/unwrap AES-256 with AES-KW', async () => {
2219
['wrapKey', 'unwrapKey'],
2320
);
2421

25-
const wrapped = await subtleAny.wrapKey(
22+
const wrapped = await subtle.wrapKey(
2623
'raw',
2724
keyToWrap as CryptoKey,
2825
wrappingKey as CryptoKey,
2926
{ name: 'AES-KW' },
3027
);
3128

32-
const unwrapped = await subtleAny.unwrapKey(
29+
const unwrapped = await subtle.unwrapKey(
3330
'raw',
3431
wrapped,
3532
wrappingKey as CryptoKey,
@@ -76,14 +73,14 @@ test(SUITE, 'wrap/unwrap with AES-GCM', async () => {
7673

7774
const iv = getRandomValues(new Uint8Array(12));
7875

79-
const wrapped = await subtleAny.wrapKey(
76+
const wrapped = await subtle.wrapKey(
8077
'raw',
8178
keyToWrap as CryptoKey,
8279
wrappingKey as CryptoKey,
8380
{ name: 'AES-GCM', iv },
8481
);
8582

86-
const unwrapped = await subtleAny.unwrapKey(
83+
const unwrapped = await subtle.unwrapKey(
8784
'raw',
8885
wrapped,
8986
wrappingKey as CryptoKey,
@@ -125,14 +122,14 @@ test(SUITE, 'wrap/unwrap JWK format', async () => {
125122
['wrapKey', 'unwrapKey'],
126123
);
127124

128-
const wrapped = await subtleAny.wrapKey(
125+
const wrapped = await subtle.wrapKey(
129126
'jwk',
130127
keyToWrap as CryptoKey,
131128
wrappingKey as CryptoKey,
132129
{ name: 'AES-KW' },
133130
);
134131

135-
const unwrapped = await subtleAny.unwrapKey(
132+
const unwrapped = await subtle.unwrapKey(
136133
'jwk',
137134
wrapped,
138135
wrappingKey as CryptoKey,
@@ -149,3 +146,215 @@ test(SUITE, 'wrap/unwrap JWK format', async () => {
149146
Buffer.from(exported2 as ArrayBuffer).toString('hex'),
150147
);
151148
});
149+
150+
// Test 4: Wrap/unwrap with AES-CBC
151+
test(SUITE, 'wrap/unwrap with AES-CBC', async () => {
152+
const keyToWrap = await subtle.generateKey(
153+
{ name: 'AES-GCM', length: 256 },
154+
true,
155+
['encrypt', 'decrypt'],
156+
);
157+
158+
const wrappingKey = await subtle.generateKey(
159+
{ name: 'AES-CBC', length: 256 },
160+
true,
161+
['wrapKey', 'unwrapKey'],
162+
);
163+
164+
const iv = getRandomValues(new Uint8Array(16));
165+
166+
const wrapped = await subtle.wrapKey(
167+
'raw',
168+
keyToWrap as CryptoKey,
169+
wrappingKey as CryptoKey,
170+
{ name: 'AES-CBC', iv },
171+
);
172+
173+
const unwrapped = await subtle.unwrapKey(
174+
'raw',
175+
wrapped,
176+
wrappingKey as CryptoKey,
177+
{ name: 'AES-CBC', iv },
178+
{ name: 'AES-GCM', length: 256 },
179+
true,
180+
['encrypt', 'decrypt'],
181+
);
182+
183+
const plaintext = getRandomValues(new Uint8Array(32));
184+
const gcmIv = getRandomValues(new Uint8Array(12));
185+
186+
const ct = await subtle.encrypt(
187+
{ name: 'AES-GCM', iv: gcmIv },
188+
keyToWrap as CryptoKey,
189+
plaintext,
190+
);
191+
192+
const pt = await subtle.decrypt(
193+
{ name: 'AES-GCM', iv: gcmIv },
194+
unwrapped as CryptoKey,
195+
ct,
196+
);
197+
198+
expect(Buffer.from(pt).toString('hex')).to.equal(
199+
Buffer.from(plaintext).toString('hex'),
200+
);
201+
});
202+
203+
// Test 5: Wrap/unwrap with AES-CTR
204+
test(SUITE, 'wrap/unwrap with AES-CTR', async () => {
205+
const keyToWrap = await subtle.generateKey(
206+
{ name: 'HMAC', hash: 'SHA-256', length: 256 },
207+
true,
208+
['sign', 'verify'],
209+
);
210+
211+
const wrappingKey = await subtle.generateKey(
212+
{ name: 'AES-CTR', length: 256 },
213+
true,
214+
['wrapKey', 'unwrapKey'],
215+
);
216+
217+
const counter = getRandomValues(new Uint8Array(16));
218+
219+
const wrapped = await subtle.wrapKey(
220+
'raw',
221+
keyToWrap as CryptoKey,
222+
wrappingKey as CryptoKey,
223+
{ name: 'AES-CTR', counter, length: 64 },
224+
);
225+
226+
const unwrapped = await subtle.unwrapKey(
227+
'raw',
228+
wrapped,
229+
wrappingKey as CryptoKey,
230+
{ name: 'AES-CTR', counter, length: 64 },
231+
{ name: 'HMAC', hash: 'SHA-256' },
232+
true,
233+
['sign', 'verify'],
234+
);
235+
236+
const data = new Uint8Array([1, 2, 3, 4]);
237+
const sig1 = await subtle.sign(
238+
{ name: 'HMAC' },
239+
keyToWrap as CryptoKey,
240+
data,
241+
);
242+
const sig2 = await subtle.sign(
243+
{ name: 'HMAC' },
244+
unwrapped as CryptoKey,
245+
data,
246+
);
247+
248+
expect(Buffer.from(sig1).toString('hex')).to.equal(
249+
Buffer.from(sig2).toString('hex'),
250+
);
251+
});
252+
253+
// Test 6: Wrap/unwrap with RSA-OAEP
254+
test(SUITE, 'wrap/unwrap with RSA-OAEP', async () => {
255+
const keyToWrap = await subtle.generateKey(
256+
{ name: 'AES-GCM', length: 128 },
257+
true,
258+
['encrypt', 'decrypt'],
259+
);
260+
261+
const rsaKeyPair = await subtle.generateKey(
262+
{
263+
name: 'RSA-OAEP',
264+
modulusLength: 2048,
265+
publicExponent: new Uint8Array([1, 0, 1]),
266+
hash: 'SHA-256',
267+
},
268+
true,
269+
['wrapKey', 'unwrapKey'],
270+
);
271+
272+
const wrapped = await subtle.wrapKey(
273+
'raw',
274+
keyToWrap as CryptoKey,
275+
(rsaKeyPair as CryptoKeyPair).publicKey as CryptoKey,
276+
{ name: 'RSA-OAEP' },
277+
);
278+
279+
const unwrapped = await subtle.unwrapKey(
280+
'raw',
281+
wrapped,
282+
(rsaKeyPair as CryptoKeyPair).privateKey as CryptoKey,
283+
{ name: 'RSA-OAEP' },
284+
{ name: 'AES-GCM', length: 128 },
285+
true,
286+
['encrypt', 'decrypt'],
287+
);
288+
289+
const plaintext = getRandomValues(new Uint8Array(32));
290+
const iv = getRandomValues(new Uint8Array(12));
291+
292+
const ct = await subtle.encrypt(
293+
{ name: 'AES-GCM', iv },
294+
keyToWrap as CryptoKey,
295+
plaintext,
296+
);
297+
298+
const pt = await subtle.decrypt(
299+
{ name: 'AES-GCM', iv },
300+
unwrapped as CryptoKey,
301+
ct,
302+
);
303+
304+
expect(Buffer.from(pt).toString('hex')).to.equal(
305+
Buffer.from(plaintext).toString('hex'),
306+
);
307+
});
308+
309+
// Test 7: Wrap/unwrap with ChaCha20-Poly1305
310+
test(SUITE, 'wrap/unwrap with ChaCha20-Poly1305', async () => {
311+
const keyToWrap = await subtle.generateKey(
312+
{ name: 'AES-GCM', length: 256 },
313+
true,
314+
['encrypt', 'decrypt'],
315+
);
316+
317+
const wrappingKey = await subtle.generateKey(
318+
{ name: 'ChaCha20-Poly1305', length: 256 },
319+
true,
320+
['wrapKey', 'unwrapKey'],
321+
);
322+
323+
const iv = getRandomValues(new Uint8Array(12));
324+
325+
const wrapped = await subtle.wrapKey(
326+
'raw',
327+
keyToWrap as CryptoKey,
328+
wrappingKey as CryptoKey,
329+
{ name: 'ChaCha20-Poly1305', iv },
330+
);
331+
332+
const unwrapped = await subtle.unwrapKey(
333+
'raw',
334+
wrapped,
335+
wrappingKey as CryptoKey,
336+
{ name: 'ChaCha20-Poly1305', iv },
337+
{ name: 'AES-GCM', length: 256 },
338+
true,
339+
['encrypt', 'decrypt'],
340+
);
341+
342+
const plaintext = getRandomValues(new Uint8Array(32));
343+
const gcmIv = getRandomValues(new Uint8Array(12));
344+
345+
const ct = await subtle.encrypt(
346+
{ name: 'AES-GCM', iv: gcmIv },
347+
keyToWrap as CryptoKey,
348+
plaintext,
349+
);
350+
351+
const pt = await subtle.decrypt(
352+
{ name: 'AES-GCM', iv: gcmIv },
353+
unwrapped as CryptoKey,
354+
ct,
355+
);
356+
357+
expect(Buffer.from(pt).toString('hex')).to.equal(
358+
Buffer.from(plaintext).toString('hex'),
359+
);
360+
});

packages/react-native-quick-crypto/QuickCrypto.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ Pod::Spec.new do |s|
112112
# implementation (C++)
113113
"cpp/**/*.{hpp,cpp}",
114114
# dependencies (C++) - ncrypto
115-
"deps/ncrypto/include/*.{h}",
115+
"deps/ncrypto/include/**/*.{h}",
116116
"deps/ncrypto/src/*.{cpp}",
117117
# dependencies (C) - exclude BLAKE3 x86 SIMD files (only use portable + NEON for ARM)
118118
"deps/blake3/c/*.{h,c}",

packages/react-native-quick-crypto/android/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ add_library(
5555
../cpp/utils/HybridUtils.cpp
5656
${BLAKE3_SOURCES}
5757
../deps/fastpbkdf2/fastpbkdf2.c
58+
../deps/ncrypto/src/aead.cpp
59+
../deps/ncrypto/src/engine.cpp
5860
../deps/ncrypto/src/ncrypto.cpp
5961
)
6062

0 commit comments

Comments
 (0)