@@ -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' ;
912import { expect } from 'chai' ;
1013import { test , assertThrowsAsync , decodeHex } from '../util' ;
1114import { 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 - Z a - z 0 - 9 _ - ] + $ / ) ;
431+ expect ( jwk . y ) . to . match ( / ^ [ A - Z a - z 0 - 9 _ - ] + $ / ) ;
432+ expect ( jwk . d ) . to . match ( / ^ [ A - Z a - z 0 - 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 - Z a - z 0 - 9 _ - ] + $ / ) ;
477+ expect ( jwk . y ) . to . match ( / ^ [ A - Z a - z 0 - 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
389489test ( SUITE , 'createPublicKey throws with invalid PEM' , async ( ) => {
0 commit comments