@@ -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,122 @@ function getKeyTypes(allowKeyObject, bufferOnly = false) {
413
417
return types ;
414
418
}
415
419
420
+ function getKeyObjectHandleFromJwk ( key , ctx ) {
421
+ validateObject ( key , 'key' ) ;
422
+ validateOneOf (
423
+ key . kty , 'key.kty' , [ 'RSA' , 'EC' , 'OKP' ] ) ;
424
+ const isPublic = ctx === kConsumePublic || ctx === kCreatePublic ;
425
+
426
+ if ( key . kty === 'OKP' ) {
427
+ validateString ( key . crv , 'key.crv' ) ;
428
+ validateOneOf (
429
+ key . crv , 'key.crv' , [ 'Ed25519' , 'Ed448' , 'X25519' , 'X448' ] ) ;
430
+ validateString ( key . x , 'key.x' ) ;
431
+
432
+ if ( ! isPublic )
433
+ validateString ( key . d , 'key.d' ) ;
434
+
435
+ let keyData ;
436
+ if ( isPublic )
437
+ keyData = Buffer . from ( key . x , 'base64' ) ;
438
+ else
439
+ keyData = Buffer . from ( key . d , 'base64' ) ;
440
+
441
+ switch ( key . crv ) {
442
+ case 'Ed25519' :
443
+ case 'X25519' :
444
+ if ( keyData . byteLength !== 32 ) {
445
+ throw new ERR_CRYPTO_INVALID_JWK ( ) ;
446
+ }
447
+ break ;
448
+ case 'Ed448' :
449
+ if ( keyData . byteLength !== 57 ) {
450
+ throw new ERR_CRYPTO_INVALID_JWK ( ) ;
451
+ }
452
+ break ;
453
+ case 'X448' :
454
+ if ( keyData . byteLength !== 56 ) {
455
+ throw new ERR_CRYPTO_INVALID_JWK ( ) ;
456
+ }
457
+ break ;
458
+ }
459
+
460
+ const handle = new KeyObjectHandle ( ) ;
461
+ if ( isPublic ) {
462
+ handle . initEDRaw (
463
+ `NODE-${ key . crv . toUpperCase ( ) } ` ,
464
+ keyData ,
465
+ kKeyTypePublic ) ;
466
+ } else {
467
+ handle . initEDRaw (
468
+ `NODE-${ key . crv . toUpperCase ( ) } ` ,
469
+ keyData ,
470
+ kKeyTypePrivate ) ;
471
+ }
472
+
473
+ return handle ;
474
+ }
475
+
476
+ if ( key . kty === 'EC' ) {
477
+ validateString ( key . crv , 'key.crv' ) ;
478
+ validateOneOf (
479
+ key . crv , 'key.crv' , [ 'P-256' , 'secp256k1' , 'P-384' , 'P-521' ] ) ;
480
+ validateString ( key . x , 'key.x' ) ;
481
+ validateString ( key . y , 'key.y' ) ;
482
+
483
+ const jwk = {
484
+ kty : key . kty ,
485
+ crv : key . crv ,
486
+ x : key . x ,
487
+ y : key . y
488
+ } ;
489
+
490
+ if ( ! isPublic ) {
491
+ validateString ( key . d , 'key.d' ) ;
492
+ jwk . d = key . d ;
493
+ }
494
+
495
+ const handle = new KeyObjectHandle ( ) ;
496
+ const type = handle . initJwk ( jwk , jwk . crv ) ;
497
+ if ( type === undefined )
498
+ throw new ERR_CRYPTO_INVALID_JWK ( ) ;
499
+
500
+ return handle ;
501
+ }
502
+
503
+ // RSA
504
+ validateString ( key . n , 'key.n' ) ;
505
+ validateString ( key . e , 'key.e' ) ;
506
+
507
+ const jwk = {
508
+ kty : key . kty ,
509
+ n : key . n ,
510
+ e : key . e
511
+ } ;
512
+
513
+ if ( ! isPublic ) {
514
+ validateString ( key . d , 'key.d' ) ;
515
+ validateString ( key . p , 'key.p' ) ;
516
+ validateString ( key . q , 'key.q' ) ;
517
+ validateString ( key . dp , 'key.dp' ) ;
518
+ validateString ( key . dq , 'key.dq' ) ;
519
+ validateString ( key . qi , 'key.qi' ) ;
520
+ jwk . d = key . d ;
521
+ jwk . p = key . p ;
522
+ jwk . q = key . q ;
523
+ jwk . dp = key . dp ;
524
+ jwk . dq = key . dq ;
525
+ jwk . qi = key . qi ;
526
+ }
527
+
528
+ const handle = new KeyObjectHandle ( ) ;
529
+ const type = handle . initJwk ( jwk ) ;
530
+ if ( type === undefined )
531
+ throw new ERR_CRYPTO_INVALID_JWK ( ) ;
532
+
533
+ return handle ;
534
+ }
535
+
416
536
function prepareAsymmetricKey ( key , ctx ) {
417
537
if ( isKeyObject ( key ) ) {
418
538
// Best case: A key object, as simple as that.
@@ -423,13 +543,15 @@ function prepareAsymmetricKey(key, ctx) {
423
543
// Expect PEM by default, mostly for backward compatibility.
424
544
return { format : kKeyFormatPEM , data : getArrayBufferOrView ( key , 'key' ) } ;
425
545
} else if ( typeof key === 'object' ) {
426
- const { key : data , encoding } = key ;
546
+ const { key : data , encoding, format } = key ;
427
547
// The 'key' property can be a KeyObject as well to allow specifying
428
548
// additional options such as padding along with the key.
429
549
if ( isKeyObject ( data ) )
430
550
return { data : getKeyObjectHandle ( data , ctx ) } ;
431
551
else if ( isCryptoKey ( data ) )
432
552
return { data : getKeyObjectHandle ( data [ kKeyObject ] , ctx ) } ;
553
+ else if ( isJwk ( data ) && format === 'jwk' )
554
+ return { data : getKeyObjectHandleFromJwk ( data , ctx ) , format : 'jwk' } ;
433
555
// Either PEM or DER using PKCS#1 or SPKI.
434
556
if ( ! isStringOrBuffer ( data ) ) {
435
557
throw new ERR_INVALID_ARG_TYPE (
@@ -494,16 +616,26 @@ function createSecretKey(key, encoding) {
494
616
function createPublicKey ( key ) {
495
617
const { format, type, data, passphrase } =
496
618
prepareAsymmetricKey ( key , kCreatePublic ) ;
497
- const handle = new KeyObjectHandle ( ) ;
498
- handle . init ( kKeyTypePublic , data , format , type , passphrase ) ;
619
+ let handle ;
620
+ if ( format === 'jwk' ) {
621
+ handle = data ;
622
+ } else {
623
+ handle = new KeyObjectHandle ( ) ;
624
+ handle . init ( kKeyTypePublic , data , format , type , passphrase ) ;
625
+ }
499
626
return new PublicKeyObject ( handle ) ;
500
627
}
501
628
502
629
function createPrivateKey ( key ) {
503
630
const { format, type, data, passphrase } =
504
631
prepareAsymmetricKey ( key , kCreatePrivate ) ;
505
- const handle = new KeyObjectHandle ( ) ;
506
- handle . init ( kKeyTypePrivate , data , format , type , passphrase ) ;
632
+ let handle ;
633
+ if ( format === 'jwk' ) {
634
+ handle = data ;
635
+ } else {
636
+ handle = new KeyObjectHandle ( ) ;
637
+ handle . init ( kKeyTypePrivate , data , format , type , passphrase ) ;
638
+ }
507
639
return new PrivateKeyObject ( handle ) ;
508
640
}
509
641
@@ -609,6 +741,10 @@ function isCryptoKey(obj) {
609
741
return obj != null && obj [ kKeyObject ] !== undefined ;
610
742
}
611
743
744
+ function isJwk ( obj ) {
745
+ return obj != null && obj . kty !== undefined ;
746
+ }
747
+
612
748
module . exports = {
613
749
// Public API.
614
750
createSecretKey,
0 commit comments