Skip to content

Commit 955b935

Browse files
authored
refactor: migrate from deprecated EC_KEY APIs to OpenSSL 3.x EVP parameter APIs (#935)
1 parent 8e4c9e7 commit 955b935

12 files changed

Lines changed: 444 additions & 302 deletions

File tree

example/src/tests/ecdh/ecdh_tests.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,69 @@ test(SUITE, 'should work with string input', () => {
6969
assert.isOk(secret);
7070
});
7171

72+
test(SUITE, 'should set private key and compute secret for P-384', () => {
73+
const alice = crypto.createECDH('secp384r1');
74+
alice.generateKeys();
75+
const priv = alice.getPrivateKey();
76+
77+
const alice2 = crypto.createECDH('secp384r1');
78+
alice2.setPrivateKey(priv);
79+
80+
assert.strictEqual(
81+
alice.getPublicKey().toString('hex'),
82+
alice2.getPublicKey().toString('hex'),
83+
);
84+
85+
const bob = crypto.createECDH('secp384r1');
86+
bob.generateKeys();
87+
88+
const secret1 = alice.computeSecret(bob.getPublicKey());
89+
const secret2 = alice2.computeSecret(bob.getPublicKey());
90+
assert.strictEqual(secret1.toString('hex'), secret2.toString('hex'));
91+
});
92+
93+
test(SUITE, 'should set private key and compute secret for P-521', () => {
94+
const alice = crypto.createECDH('secp521r1');
95+
alice.generateKeys();
96+
const priv = alice.getPrivateKey();
97+
98+
const alice2 = crypto.createECDH('secp521r1');
99+
alice2.setPrivateKey(priv);
100+
101+
assert.strictEqual(
102+
alice.getPublicKey().toString('hex'),
103+
alice2.getPublicKey().toString('hex'),
104+
);
105+
106+
const bob = crypto.createECDH('secp521r1');
107+
bob.generateKeys();
108+
109+
const secret1 = alice.computeSecret(bob.getPublicKey());
110+
const secret2 = alice2.computeSecret(bob.getPublicKey());
111+
assert.strictEqual(secret1.toString('hex'), secret2.toString('hex'));
112+
});
113+
114+
test(SUITE, 'should set private key and compute secret for secp256k1', () => {
115+
const alice = crypto.createECDH('secp256k1');
116+
alice.generateKeys();
117+
const priv = alice.getPrivateKey();
118+
119+
const alice2 = crypto.createECDH('secp256k1');
120+
alice2.setPrivateKey(priv);
121+
122+
assert.strictEqual(
123+
alice.getPublicKey().toString('hex'),
124+
alice2.getPublicKey().toString('hex'),
125+
);
126+
127+
const bob = crypto.createECDH('secp256k1');
128+
bob.generateKeys();
129+
130+
const secret1 = alice.computeSecret(bob.getPublicKey());
131+
const secret2 = alice2.computeSecret(bob.getPublicKey());
132+
assert.strictEqual(secret1.toString('hex'), secret2.toString('hex'));
133+
});
134+
72135
test(SUITE, 'getCurves - should return array of supported curves', () => {
73136
const curves = getCurves();
74137
assert.isArray(curves);

example/src/tests/keys/create_keys.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import {
55
createPublicKey,
66
generateKeyPair,
77
randomBytes,
8+
sign,
9+
verify,
810
} from 'react-native-quick-crypto';
11+
import type { JWK } from 'react-native-quick-crypto';
912
import { expect } from 'chai';
1013
import { test, assertThrowsAsync, decodeHex } from '../util';
1114
import { rsaPrivateKeyPem, rsaPublicKeyPem } from './fixtures';
@@ -384,6 +387,103 @@ test(
384387
},
385388
);
386389

390+
// --- JWK EC Round-Trip Tests ---
391+
392+
const EC_CURVES = [
393+
{ namedCurve: 'P-256', crv: 'P-256', hash: 'SHA256' },
394+
{ namedCurve: 'P-384', crv: 'P-384', hash: 'SHA384' },
395+
{ namedCurve: 'P-521', crv: 'P-521', hash: 'SHA512' },
396+
] as const;
397+
398+
for (const { namedCurve, crv, hash } of EC_CURVES) {
399+
test(
400+
SUITE,
401+
`JWK EC ${crv} private key round-trip: generate -> export JWK -> import JWK -> sign/verify`,
402+
async () => {
403+
const { privateKey: privPem, publicKey: pubPem } = await new Promise<{
404+
privateKey: string;
405+
publicKey: string;
406+
}>((resolve, reject) => {
407+
generateKeyPair(
408+
'ec',
409+
{
410+
namedCurve,
411+
publicKeyEncoding: { type: 'spki', format: 'pem' },
412+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
413+
},
414+
(err, pubKey, privKey) => {
415+
if (err) reject(err);
416+
else
417+
resolve({
418+
privateKey: privKey as string,
419+
publicKey: pubKey as string,
420+
});
421+
},
422+
);
423+
});
424+
425+
const privKey = createPrivateKey(privPem);
426+
const jwk = privKey.export({ format: 'jwk' }) as JWK;
427+
428+
expect(jwk.kty).to.equal('EC');
429+
expect(jwk.crv).to.equal(crv);
430+
expect(jwk.x).to.match(/^[A-Za-z0-9_-]+$/);
431+
expect(jwk.y).to.match(/^[A-Za-z0-9_-]+$/);
432+
expect(jwk.d).to.match(/^[A-Za-z0-9_-]+$/);
433+
434+
const reimported = createPrivateKey({ key: jwk, format: 'jwk' });
435+
expect(reimported.type).to.equal('private');
436+
expect(reimported.asymmetricKeyType).to.equal('ec');
437+
438+
const sig = sign(hash, 'test data', reimported);
439+
const pubKey = createPublicKey(pubPem);
440+
const valid = verify(hash, 'test data', pubKey, sig);
441+
expect(valid).to.equal(true);
442+
},
443+
);
444+
445+
test(
446+
SUITE,
447+
`JWK EC ${crv} public key round-trip: generate -> export JWK -> import JWK`,
448+
async () => {
449+
const { publicKey: pubPem } = await new Promise<{
450+
privateKey: string;
451+
publicKey: string;
452+
}>((resolve, reject) => {
453+
generateKeyPair(
454+
'ec',
455+
{
456+
namedCurve,
457+
publicKeyEncoding: { type: 'spki', format: 'pem' },
458+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
459+
},
460+
(err, pubKey, privKey) => {
461+
if (err) reject(err);
462+
else
463+
resolve({
464+
privateKey: privKey as string,
465+
publicKey: pubKey as string,
466+
});
467+
},
468+
);
469+
});
470+
471+
const pubKey = createPublicKey(pubPem);
472+
const jwk = pubKey.export({ format: 'jwk' }) as JWK;
473+
474+
expect(jwk.kty).to.equal('EC');
475+
expect(jwk.crv).to.equal(crv);
476+
expect(jwk.x).to.match(/^[A-Za-z0-9_-]+$/);
477+
expect(jwk.y).to.match(/^[A-Za-z0-9_-]+$/);
478+
expect(jwk.d).to.equal(undefined);
479+
480+
const reimported = createPublicKey({ key: jwk, format: 'jwk' });
481+
expect(reimported.type).to.equal('public');
482+
expect(reimported.asymmetricKeyType).to.equal('ec');
483+
},
484+
);
485+
}
486+
387487
// --- Error Cases ---
388488

389489
test(SUITE, 'createPublicKey throws with invalid PEM', async () => {

example/src/tests/keys/sign_verify_oneshot.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,92 @@ test(SUITE, 'ECDSA P-256 with IEEE-P1363 encoding', async () => {
199199
expect(isValid).to.equal(true);
200200
});
201201

202+
test(SUITE, 'ECDSA P-384 with IEEE-P1363 encoding', async () => {
203+
const { privateKey, publicKey } = await new Promise<{
204+
privateKey: string;
205+
publicKey: string;
206+
}>((resolve, reject) => {
207+
generateKeyPair(
208+
'ec',
209+
{
210+
namedCurve: 'P-384',
211+
publicKeyEncoding: { type: 'spki', format: 'pem' },
212+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
213+
},
214+
(err, pubKey, privKey) => {
215+
if (err) reject(err);
216+
else
217+
resolve({
218+
privateKey: privKey as string,
219+
publicKey: pubKey as string,
220+
});
221+
},
222+
);
223+
});
224+
225+
const signature = sign('SHA384', testData, {
226+
key: privateKey,
227+
dsaEncoding: 'ieee-p1363',
228+
});
229+
230+
expect(signature.length).to.equal(96);
231+
232+
const isValid = verify(
233+
'SHA384',
234+
testData,
235+
{
236+
key: publicKey,
237+
dsaEncoding: 'ieee-p1363',
238+
},
239+
signature,
240+
);
241+
242+
expect(isValid).to.equal(true);
243+
});
244+
245+
test(SUITE, 'ECDSA P-521 with IEEE-P1363 encoding', async () => {
246+
const { privateKey, publicKey } = await new Promise<{
247+
privateKey: string;
248+
publicKey: string;
249+
}>((resolve, reject) => {
250+
generateKeyPair(
251+
'ec',
252+
{
253+
namedCurve: 'P-521',
254+
publicKeyEncoding: { type: 'spki', format: 'pem' },
255+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
256+
},
257+
(err, pubKey, privKey) => {
258+
if (err) reject(err);
259+
else
260+
resolve({
261+
privateKey: privKey as string,
262+
publicKey: pubKey as string,
263+
});
264+
},
265+
);
266+
});
267+
268+
const signature = sign('SHA512', testData, {
269+
key: privateKey,
270+
dsaEncoding: 'ieee-p1363',
271+
});
272+
273+
expect(signature.length).to.equal(132);
274+
275+
const isValid = verify(
276+
'SHA512',
277+
testData,
278+
{
279+
key: publicKey,
280+
dsaEncoding: 'ieee-p1363',
281+
},
282+
signature,
283+
);
284+
285+
expect(isValid).to.equal(true);
286+
});
287+
202288
// --- Ed25519 Tests ---
203289

204290
test(SUITE, 'Ed25519 sign and verify', async () => {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ add_library(
5858
../cpp/sign/HybridSignHandle.cpp
5959
../cpp/sign/HybridVerifyHandle.cpp
6060
../cpp/utils/HybridUtils.cpp
61+
../cpp/utils/QuickCryptoUtils.cpp
6162
${BLAKE3_SOURCES}
6263
../deps/fastpbkdf2/fastpbkdf2.c
6364
../deps/ncrypto/src/aead.cpp

0 commit comments

Comments
 (0)