@@ -26,6 +26,7 @@ const {
26
26
const {
27
27
validateObject,
28
28
validateOneOf,
29
+ validateString,
29
30
} = require ( 'internal/validators' ) ;
30
31
31
32
const {
@@ -38,6 +39,7 @@ const {
38
39
ERR_OPERATION_FAILED ,
39
40
ERR_CRYPTO_JWK_UNSUPPORTED_CURVE ,
40
41
ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE ,
42
+ ERR_CRYPTO_INVALID_JWK ,
41
43
}
42
44
} = require ( 'internal/errors' ) ;
43
45
@@ -65,6 +67,8 @@ const {
65
67
66
68
const { inspect } = require ( 'internal/util/inspect' ) ;
67
69
70
+ const { Buffer } = require ( 'buffer' ) ;
71
+
68
72
const kAlgorithm = Symbol ( 'kAlgorithm' ) ;
69
73
const kExtractable = Symbol ( 'kExtractable' ) ;
70
74
const kKeyType = Symbol ( 'kKeyType' ) ;
@@ -413,6 +417,111 @@ function getKeyTypes(allowKeyObject, bufferOnly = false) {
413
417
return types ;
414
418
}
415
419
420
+ function getKeyObjectHandleFromJwk ( key , ctx ) {
421
+ validateObject ( key , 'key' ) ;
422
+ key = { ...key } ;
423
+ validateOneOf (
424
+ key . kty , 'key.kty' , [ 'RSA' , 'EC' , 'OKP' ] ) ;
425
+ const isPublic = ctx === kConsumePublic || ctx === kCreatePublic ;
426
+
427
+ if ( key . kty === 'OKP' ) {
428
+ validateString ( key . crv , 'key.crv' ) ;
429
+ validateOneOf (
430
+ key . crv , 'key.crv' , [ 'Ed25519' , 'Ed448' , 'X25519' , 'X448' ] ) ;
431
+ validateString ( key . x , 'key.x' ) ;
432
+ if ( ! isPublic ) {
433
+ validateString ( key . d , 'key.d' ) ;
434
+ }
435
+
436
+ let keyData ;
437
+ if ( isPublic )
438
+ keyData = Buffer . from ( key . x , 'base64' ) ;
439
+ else
440
+ keyData = Buffer . from ( key . d , 'base64' ) ;
441
+
442
+ switch ( key . crv ) {
443
+ case 'Ed25519' :
444
+ case 'X25519' :
445
+ if ( keyData . byteLength !== 32 ) {
446
+ throw new ERR_CRYPTO_INVALID_JWK ( ) ;
447
+ }
448
+ break ;
449
+ case 'Ed448' :
450
+ if ( keyData . byteLength !== 57 ) {
451
+ throw new ERR_CRYPTO_INVALID_JWK ( ) ;
452
+ }
453
+ break ;
454
+ case 'X448' :
455
+ if ( keyData . byteLength !== 56 ) {
456
+ throw new ERR_CRYPTO_INVALID_JWK ( ) ;
457
+ }
458
+ break ;
459
+ }
460
+
461
+ const handle = new KeyObjectHandle ( ) ;
462
+ if ( isPublic ) {
463
+ handle . initEDRaw (
464
+ `NODE-${ key . crv . toUpperCase ( ) } ` ,
465
+ keyData ,
466
+ kKeyTypePublic ) ;
467
+ } else {
468
+ handle . initEDRaw (
469
+ `NODE-${ key . crv . toUpperCase ( ) } ` ,
470
+ keyData ,
471
+ kKeyTypePrivate ) ;
472
+ }
473
+
474
+ return handle ;
475
+ }
476
+
477
+ if ( key . kty === 'EC' ) {
478
+ validateString ( key . crv , 'key.crv' ) ;
479
+ validateOneOf (
480
+ key . crv , 'key.crv' , [ 'P-256' , 'secp256k1' , 'P-384' , 'P-521' ] ) ;
481
+ validateString ( key . x , 'key.x' ) ;
482
+ validateString ( key . y , 'key.y' ) ;
483
+
484
+ if ( isPublic ) {
485
+ delete key . d ;
486
+ } else {
487
+ validateString ( key . d , 'key.d' ) ;
488
+ }
489
+
490
+ const handle = new KeyObjectHandle ( ) ;
491
+ const type = handle . initJwk ( key , key . crv ) ;
492
+ if ( type === undefined )
493
+ throw new ERR_CRYPTO_INVALID_JWK ( ) ;
494
+
495
+ return handle ;
496
+ }
497
+
498
+ // RSA
499
+ validateString ( key . n , 'key.n' ) ;
500
+ validateString ( key . e , 'key.e' ) ;
501
+ if ( isPublic ) {
502
+ delete key . d ;
503
+ delete key . p ;
504
+ delete key . q ;
505
+ delete key . dp ;
506
+ delete key . dq ;
507
+ delete key . qi ;
508
+ } else {
509
+ validateString ( key . d , 'key.d' ) ;
510
+ validateString ( key . p , 'key.p' ) ;
511
+ validateString ( key . q , 'key.q' ) ;
512
+ validateString ( key . dp , 'key.dp' ) ;
513
+ validateString ( key . dq , 'key.dq' ) ;
514
+ validateString ( key . qi , 'key.qi' ) ;
515
+ }
516
+
517
+ const handle = new KeyObjectHandle ( ) ;
518
+ const type = handle . initJwk ( key ) ;
519
+ if ( type === undefined )
520
+ throw new ERR_CRYPTO_INVALID_JWK ( ) ;
521
+
522
+ return handle ;
523
+ }
524
+
416
525
function prepareAsymmetricKey ( key , ctx ) {
417
526
if ( isKeyObject ( key ) ) {
418
527
// Best case: A key object, as simple as that.
@@ -423,13 +532,15 @@ function prepareAsymmetricKey(key, ctx) {
423
532
// Expect PEM by default, mostly for backward compatibility.
424
533
return { format : kKeyFormatPEM , data : getArrayBufferOrView ( key , 'key' ) } ;
425
534
} else if ( typeof key === 'object' ) {
426
- const { key : data , encoding } = key ;
535
+ const { key : data , encoding, format } = key ;
427
536
// The 'key' property can be a KeyObject as well to allow specifying
428
537
// additional options such as padding along with the key.
429
538
if ( isKeyObject ( data ) )
430
539
return { data : getKeyObjectHandle ( data , ctx ) } ;
431
540
else if ( isCryptoKey ( data ) )
432
541
return { data : getKeyObjectHandle ( data [ kKeyObject ] , ctx ) } ;
542
+ else if ( isJwk ( data ) && format === 'jwk' )
543
+ return { data : getKeyObjectHandleFromJwk ( data , ctx ) , format : 'jwk' } ;
433
544
// Either PEM or DER using PKCS#1 or SPKI.
434
545
if ( ! isStringOrBuffer ( data ) ) {
435
546
throw new ERR_INVALID_ARG_TYPE (
@@ -494,16 +605,26 @@ function createSecretKey(key, encoding) {
494
605
function createPublicKey ( key ) {
495
606
const { format, type, data, passphrase } =
496
607
prepareAsymmetricKey ( key , kCreatePublic ) ;
497
- const handle = new KeyObjectHandle ( ) ;
498
- handle . init ( kKeyTypePublic , data , format , type , passphrase ) ;
608
+ let handle ;
609
+ if ( format === 'jwk' ) {
610
+ handle = data ;
611
+ } else {
612
+ handle = new KeyObjectHandle ( ) ;
613
+ handle . init ( kKeyTypePublic , data , format , type , passphrase ) ;
614
+ }
499
615
return new PublicKeyObject ( handle ) ;
500
616
}
501
617
502
618
function createPrivateKey ( key ) {
503
619
const { format, type, data, passphrase } =
504
620
prepareAsymmetricKey ( key , kCreatePrivate ) ;
505
- const handle = new KeyObjectHandle ( ) ;
506
- handle . init ( kKeyTypePrivate , data , format , type , passphrase ) ;
621
+ let handle ;
622
+ if ( format === 'jwk' ) {
623
+ handle = data ;
624
+ } else {
625
+ handle = new KeyObjectHandle ( ) ;
626
+ handle . init ( kKeyTypePrivate , data , format , type , passphrase ) ;
627
+ }
507
628
return new PrivateKeyObject ( handle ) ;
508
629
}
509
630
@@ -609,6 +730,10 @@ function isCryptoKey(obj) {
609
730
return obj != null && obj [ kKeyObject ] !== undefined ;
610
731
}
611
732
733
+ function isJwk ( obj ) {
734
+ return obj != null && obj . kty !== undefined ;
735
+ }
736
+
612
737
module . exports = {
613
738
// Public API.
614
739
createSecretKey,
0 commit comments