Skip to content

Commit f0b4412

Browse files
panvaaduh95
authored andcommittedOct 9, 2024
crypto: add KeyObject.prototype.toCryptoKey
PR-URL: #55262 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent c126543 commit f0b4412

File tree

10 files changed

+415
-59
lines changed

10 files changed

+415
-59
lines changed
 

‎doc/api/crypto.md

+19
Original file line numberDiff line numberDiff line change
@@ -2134,6 +2134,24 @@ added: v11.6.0
21342134
For secret keys, this property represents the size of the key in bytes. This
21352135
property is `undefined` for asymmetric keys.
21362136

2137+
### `keyObject.toCryptoKey(algorithm, extractable, keyUsages)`
2138+
2139+
<!-- YAML
2140+
added: REPLACEME
2141+
-->
2142+
2143+
<!--lint disable maximum-line-length remark-lint-->
2144+
2145+
* `algorithm`: {AlgorithmIdentifier|RsaHashedImportParams|EcKeyImportParams|HmacImportParams}
2146+
2147+
<!--lint enable maximum-line-length remark-lint-->
2148+
2149+
* `extractable`: {boolean}
2150+
* `keyUsages`: {string\[]} See [Key usages][].
2151+
* Returns: {CryptoKey}
2152+
2153+
Converts a `KeyObject` instance to a `CryptoKey`.
2154+
21372155
### `keyObject.type`
21382156

21392157
<!-- YAML
@@ -6084,6 +6102,7 @@ See the [list of SSL OP Flags][] for details.
60846102
[FIPS provider from OpenSSL 3]: https://www.openssl.org/docs/man3.0/man7/crypto.html#FIPS-provider
60856103
[HTML 5.2]: https://www.w3.org/TR/html52/changes.html#features-removed
60866104
[JWK]: https://tools.ietf.org/html/rfc7517
6105+
[Key usages]: webcrypto.md#cryptokeyusages
60876106
[NIST SP 800-131A]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf
60886107
[NIST SP 800-132]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf
60896108
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf

‎lib/internal/crypto/aes.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ async function aesGenerateKey(algorithm, extractable, keyUsages) {
245245
extractable);
246246
}
247247

248-
async function aesImportKey(
248+
function aesImportKey(
249249
algorithm,
250250
format,
251251
keyData,
@@ -266,6 +266,11 @@ async function aesImportKey(
266266
let keyObject;
267267
let length;
268268
switch (format) {
269+
case 'KeyObject': {
270+
validateKeyLength(keyData.symmetricKeySize * 8);
271+
keyObject = keyData;
272+
break;
273+
}
269274
case 'raw': {
270275
validateKeyLength(keyData.byteLength * 8);
271276
keyObject = createSecretKey(keyData);

‎lib/internal/crypto/cfrg.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ function cfrgExportKey(key, format) {
197197
key[kKeyObject][kHandle]));
198198
}
199199

200-
async function cfrgImportKey(
200+
function cfrgImportKey(
201201
format,
202202
keyData,
203203
algorithm,
@@ -208,6 +208,11 @@ async function cfrgImportKey(
208208
let keyObject;
209209
const usagesSet = new SafeSet(keyUsages);
210210
switch (format) {
211+
case 'KeyObject': {
212+
verifyAcceptableCfrgKeyUse(name, keyData.type === 'public', usagesSet);
213+
keyObject = keyData;
214+
break;
215+
}
211216
case 'spki': {
212217
verifyAcceptableCfrgKeyUse(name, true, usagesSet);
213218
try {

‎lib/internal/crypto/ec.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ function ecExportKey(key, format) {
149149
key[kKeyObject][kHandle]));
150150
}
151151

152-
async function ecImportKey(
152+
function ecImportKey(
153153
format,
154154
keyData,
155155
algorithm,
@@ -167,6 +167,11 @@ async function ecImportKey(
167167
let keyObject;
168168
const usagesSet = new SafeSet(keyUsages);
169169
switch (format) {
170+
case 'KeyObject': {
171+
verifyAcceptableEcKeyUse(name, keyData.type === 'public', usagesSet);
172+
keyObject = keyData;
173+
break;
174+
}
170175
case 'spki': {
171176
verifyAcceptableEcKeyUse(name, true, usagesSet);
172177
try {

‎lib/internal/crypto/keys.js

+159
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const {
66
ObjectDefineProperties,
77
ObjectDefineProperty,
88
ObjectSetPrototypeOf,
9+
SafeSet,
910
Symbol,
1011
SymbolToStringTag,
1112
Uint8Array,
@@ -49,6 +50,8 @@ const {
4950
kKeyObject,
5051
getArrayBufferOrView,
5152
bigIntArrayToUnsignedBigInt,
53+
normalizeAlgorithm,
54+
hasAnyNotIn,
5255
} = require('internal/crypto/util');
5356

5457
const {
@@ -65,6 +68,7 @@ const {
6568
const {
6669
customInspectSymbol: kInspect,
6770
kEnumerableProperty,
71+
lazyDOMException,
6872
} = require('internal/util');
6973

7074
const { inspect } = require('internal/util/inspect');
@@ -148,6 +152,8 @@ const {
148152
},
149153
});
150154

155+
let webidl;
156+
151157
class SecretKeyObject extends KeyObject {
152158
constructor(handle) {
153159
super('secret', handle);
@@ -168,6 +174,51 @@ const {
168174
}
169175
return this[kHandle].export();
170176
}
177+
178+
toCryptoKey(algorithm, extractable, keyUsages) {
179+
webidl ??= require('internal/crypto/webidl');
180+
algorithm = normalizeAlgorithm(webidl.converters.AlgorithmIdentifier(algorithm), 'importKey');
181+
extractable = webidl.converters.boolean(extractable);
182+
keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages);
183+
184+
let result;
185+
switch (algorithm.name) {
186+
case 'HMAC':
187+
result = require('internal/crypto/mac')
188+
.hmacImportKey('KeyObject', this, algorithm, extractable, keyUsages);
189+
break;
190+
case 'AES-CTR':
191+
// Fall through
192+
case 'AES-CBC':
193+
// Fall through
194+
case 'AES-GCM':
195+
// Fall through
196+
case 'AES-KW':
197+
result = require('internal/crypto/aes')
198+
.aesImportKey(algorithm, 'KeyObject', this, extractable, keyUsages);
199+
break;
200+
case 'HKDF':
201+
// Fall through
202+
case 'PBKDF2':
203+
result = importGenericSecretKey(
204+
algorithm,
205+
'KeyObject',
206+
this,
207+
extractable,
208+
keyUsages);
209+
break;
210+
default:
211+
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
212+
}
213+
214+
if (result.usages.length === 0) {
215+
throw lazyDOMException(
216+
`Usages cannot be empty when importing a ${result.type} key.`,
217+
'SyntaxError');
218+
}
219+
220+
return result;
221+
}
171222
}
172223

173224
const kAsymmetricKeyType = Symbol('kAsymmetricKeyType');
@@ -209,6 +260,51 @@ const {
209260
return {};
210261
}
211262
}
263+
264+
toCryptoKey(algorithm, extractable, keyUsages) {
265+
webidl ??= require('internal/crypto/webidl');
266+
algorithm = normalizeAlgorithm(webidl.converters.AlgorithmIdentifier(algorithm), 'importKey');
267+
extractable = webidl.converters.boolean(extractable);
268+
keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages);
269+
270+
let result;
271+
switch (algorithm.name) {
272+
case 'RSASSA-PKCS1-v1_5':
273+
// Fall through
274+
case 'RSA-PSS':
275+
// Fall through
276+
case 'RSA-OAEP':
277+
result = require('internal/crypto/rsa')
278+
.rsaImportKey('KeyObject', this, algorithm, extractable, keyUsages);
279+
break;
280+
case 'ECDSA':
281+
// Fall through
282+
case 'ECDH':
283+
result = require('internal/crypto/ec')
284+
.ecImportKey('KeyObject', this, algorithm, extractable, keyUsages);
285+
break;
286+
case 'Ed25519':
287+
// Fall through
288+
case 'Ed448':
289+
// Fall through
290+
case 'X25519':
291+
// Fall through
292+
case 'X448':
293+
result = require('internal/crypto/cfrg')
294+
.cfrgImportKey('KeyObject', this, algorithm, extractable, keyUsages);
295+
break;
296+
default:
297+
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
298+
}
299+
300+
if (result.type === 'private' && result.usages.length === 0) {
301+
throw lazyDOMException(
302+
`Usages cannot be empty when importing a ${result.type} key.`,
303+
'SyntaxError');
304+
}
305+
306+
return result;
307+
}
212308
}
213309

214310
class PublicKeyObject extends AsymmetricKeyObject {
@@ -801,6 +897,68 @@ function isCryptoKey(obj) {
801897
return obj != null && obj[kKeyObject] !== undefined;
802898
}
803899

900+
function importGenericSecretKey(
901+
{ name, length },
902+
format,
903+
keyData,
904+
extractable,
905+
keyUsages) {
906+
const usagesSet = new SafeSet(keyUsages);
907+
if (extractable)
908+
throw lazyDOMException(`${name} keys are not extractable`, 'SyntaxError');
909+
910+
if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
911+
throw lazyDOMException(
912+
`Unsupported key usage for a ${name} key`,
913+
'SyntaxError');
914+
}
915+
916+
switch (format) {
917+
case 'KeyObject': {
918+
if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
919+
throw lazyDOMException(
920+
`Unsupported key usage for a ${name} key`,
921+
'SyntaxError');
922+
}
923+
924+
const checkLength = keyData.symmetricKeySize * 8;
925+
926+
// The Web Crypto spec allows for key lengths that are not multiples of
927+
// 8. We don't. Our check here is stricter than that defined by the spec
928+
// in that we require that algorithm.length match keyData.length * 8 if
929+
// algorithm.length is specified.
930+
if (length !== undefined && length !== checkLength) {
931+
throw lazyDOMException('Invalid key length', 'DataError');
932+
}
933+
return new InternalCryptoKey(keyData, { name }, keyUsages, false);
934+
}
935+
case 'raw': {
936+
if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
937+
throw lazyDOMException(
938+
`Unsupported key usage for a ${name} key`,
939+
'SyntaxError');
940+
}
941+
942+
const checkLength = keyData.byteLength * 8;
943+
944+
// The Web Crypto spec allows for key lengths that are not multiples of
945+
// 8. We don't. Our check here is stricter than that defined by the spec
946+
// in that we require that algorithm.length match keyData.length * 8 if
947+
// algorithm.length is specified.
948+
if (length !== undefined && length !== checkLength) {
949+
throw lazyDOMException('Invalid key length', 'DataError');
950+
}
951+
952+
const keyObject = createSecretKey(keyData);
953+
return new InternalCryptoKey(keyObject, { name }, keyUsages, false);
954+
}
955+
}
956+
957+
throw lazyDOMException(
958+
`Unable to import ${name} key with format ${format}`,
959+
'NotSupportedError');
960+
}
961+
804962
module.exports = {
805963
// Public API.
806964
createSecretKey,
@@ -822,4 +980,5 @@ module.exports = {
822980
PrivateKeyObject,
823981
isKeyObject,
824982
isCryptoKey,
983+
importGenericSecretKey,
825984
};

‎lib/internal/crypto/mac.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ function getAlgorithmName(hash) {
8282
}
8383
}
8484

85-
async function hmacImportKey(
85+
function hmacImportKey(
8686
format,
8787
keyData,
8888
algorithm,
@@ -96,6 +96,24 @@ async function hmacImportKey(
9696
}
9797
let keyObject;
9898
switch (format) {
99+
case 'KeyObject': {
100+
const checkLength = keyData.symmetricKeySize * 8;
101+
102+
if (checkLength === 0 || algorithm.length === 0)
103+
throw lazyDOMException('Zero-length key is not supported', 'DataError');
104+
105+
// The Web Crypto spec allows for key lengths that are not multiples of
106+
// 8. We don't. Our check here is stricter than that defined by the spec
107+
// in that we require that algorithm.length match keyData.length * 8 if
108+
// algorithm.length is specified.
109+
if (algorithm.length !== undefined &&
110+
algorithm.length !== checkLength) {
111+
throw lazyDOMException('Invalid key length', 'DataError');
112+
}
113+
114+
keyObject = keyData;
115+
break;
116+
}
99117
case 'raw': {
100118
const checkLength = keyData.byteLength * 8;
101119

‎lib/internal/crypto/rsa.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ function rsaExportKey(key, format) {
200200
kRsaVariants[key.algorithm.name]));
201201
}
202202

203-
async function rsaImportKey(
203+
function rsaImportKey(
204204
format,
205205
keyData,
206206
algorithm,
@@ -209,6 +209,11 @@ async function rsaImportKey(
209209
const usagesSet = new SafeSet(keyUsages);
210210
let keyObject;
211211
switch (format) {
212+
case 'KeyObject': {
213+
verifyAcceptableRsaKeyUse(algorithm.name, keyData.type === 'public', usagesSet);
214+
keyObject = keyData;
215+
break;
216+
}
212217
case 'spki': {
213218
verifyAcceptableRsaKeyUse(algorithm.name, true, usagesSet);
214219
try {

‎lib/internal/crypto/webcrypto.js

+7-54
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ const {
88
ObjectDefineProperty,
99
ReflectApply,
1010
ReflectConstruct,
11-
SafeSet,
1211
StringPrototypeRepeat,
1312
SymbolToStringTag,
1413
} = primordials;
@@ -36,8 +35,7 @@ const {
3635

3736
const {
3837
CryptoKey,
39-
InternalCryptoKey,
40-
createSecretKey,
38+
importGenericSecretKey,
4139
} = require('internal/crypto/keys');
4240

4341
const {
@@ -46,7 +44,6 @@ const {
4644

4745
const {
4846
getBlockSize,
49-
hasAnyNotIn,
5047
normalizeAlgorithm,
5148
normalizeHashName,
5249
validateMaxBufferLength,
@@ -526,50 +523,6 @@ async function exportKey(format, key) {
526523
'Export format is unsupported', 'NotSupportedError');
527524
}
528525

529-
async function importGenericSecretKey(
530-
{ name, length },
531-
format,
532-
keyData,
533-
extractable,
534-
keyUsages) {
535-
const usagesSet = new SafeSet(keyUsages);
536-
if (extractable)
537-
throw lazyDOMException(`${name} keys are not extractable`, 'SyntaxError');
538-
539-
if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
540-
throw lazyDOMException(
541-
`Unsupported key usage for a ${name} key`,
542-
'SyntaxError');
543-
}
544-
545-
switch (format) {
546-
case 'raw': {
547-
if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
548-
throw lazyDOMException(
549-
`Unsupported key usage for a ${name} key`,
550-
'SyntaxError');
551-
}
552-
553-
const checkLength = keyData.byteLength * 8;
554-
555-
// The Web Crypto spec allows for key lengths that are not multiples of
556-
// 8. We don't. Our check here is stricter than that defined by the spec
557-
// in that we require that algorithm.length match keyData.length * 8 if
558-
// algorithm.length is specified.
559-
if (length !== undefined && length !== checkLength) {
560-
throw lazyDOMException('Invalid key length', 'DataError');
561-
}
562-
563-
const keyObject = createSecretKey(keyData);
564-
return new InternalCryptoKey(keyObject, { name }, keyUsages, false);
565-
}
566-
}
567-
568-
throw lazyDOMException(
569-
`Unable to import ${name} key with format ${format}`,
570-
'NotSupportedError');
571-
}
572-
573526
async function importKey(
574527
format,
575528
keyData,
@@ -611,13 +564,13 @@ async function importKey(
611564
case 'RSA-PSS':
612565
// Fall through
613566
case 'RSA-OAEP':
614-
result = await require('internal/crypto/rsa')
567+
result = require('internal/crypto/rsa')
615568
.rsaImportKey(format, keyData, algorithm, extractable, keyUsages);
616569
break;
617570
case 'ECDSA':
618571
// Fall through
619572
case 'ECDH':
620-
result = await require('internal/crypto/ec')
573+
result = require('internal/crypto/ec')
621574
.ecImportKey(format, keyData, algorithm, extractable, keyUsages);
622575
break;
623576
case 'Ed25519':
@@ -627,11 +580,11 @@ async function importKey(
627580
case 'X25519':
628581
// Fall through
629582
case 'X448':
630-
result = await require('internal/crypto/cfrg')
583+
result = require('internal/crypto/cfrg')
631584
.cfrgImportKey(format, keyData, algorithm, extractable, keyUsages);
632585
break;
633586
case 'HMAC':
634-
result = await require('internal/crypto/mac')
587+
result = require('internal/crypto/mac')
635588
.hmacImportKey(format, keyData, algorithm, extractable, keyUsages);
636589
break;
637590
case 'AES-CTR':
@@ -641,13 +594,13 @@ async function importKey(
641594
case 'AES-GCM':
642595
// Fall through
643596
case 'AES-KW':
644-
result = await require('internal/crypto/aes')
597+
result = require('internal/crypto/aes')
645598
.aesImportKey(algorithm, format, keyData, extractable, keyUsages);
646599
break;
647600
case 'HKDF':
648601
// Fall through
649602
case 'PBKDF2':
650-
result = await importGenericSecretKey(
603+
result = importGenericSecretKey(
651604
algorithm,
652605
format,
653606
keyData,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const {
9+
createSecretKey,
10+
KeyObject,
11+
randomBytes,
12+
generateKeyPairSync,
13+
} = require('crypto');
14+
15+
function assertCryptoKey(cryptoKey, keyObject, algorithm, extractable, usages) {
16+
assert.strictEqual(cryptoKey instanceof CryptoKey, true);
17+
assert.strictEqual(cryptoKey.type, keyObject.type);
18+
assert.strictEqual(cryptoKey.algorithm.name, algorithm);
19+
assert.strictEqual(cryptoKey.extractable, extractable);
20+
assert.deepStrictEqual(cryptoKey.usages, usages);
21+
assert.strictEqual(keyObject.equals(KeyObject.from(cryptoKey)), true);
22+
}
23+
24+
{
25+
for (const length of [128, 192, 256]) {
26+
const aes = createSecretKey(randomBytes(length >> 3));
27+
for (const algorithm of ['AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-KW']) {
28+
const usages = algorithm === 'AES-KW' ? ['wrapKey', 'unwrapKey'] : ['encrypt', 'decrypt'];
29+
for (const extractable of [true, false]) {
30+
const cryptoKey = aes.toCryptoKey(algorithm, extractable, usages);
31+
assertCryptoKey(cryptoKey, aes, algorithm, extractable, usages);
32+
assert.strictEqual(cryptoKey.algorithm.length, length);
33+
}
34+
}
35+
}
36+
}
37+
38+
{
39+
const pbkdf2 = createSecretKey(randomBytes(16));
40+
const algorithm = 'PBKDF2';
41+
const usages = ['deriveBits'];
42+
assert.throws(() => pbkdf2.toCryptoKey(algorithm, true, usages), {
43+
name: 'SyntaxError',
44+
message: 'PBKDF2 keys are not extractable'
45+
});
46+
assert.throws(() => pbkdf2.toCryptoKey(algorithm, false, ['wrapKey']), {
47+
name: 'SyntaxError',
48+
message: 'Unsupported key usage for a PBKDF2 key'
49+
});
50+
const cryptoKey = pbkdf2.toCryptoKey(algorithm, false, usages);
51+
assertCryptoKey(cryptoKey, pbkdf2, algorithm, false, usages);
52+
assert.strictEqual(cryptoKey.algorithm.length, undefined);
53+
}
54+
55+
{
56+
for (const length of [128, 192, 256]) {
57+
const hmac = createSecretKey(randomBytes(length >> 3));
58+
const algorithm = 'HMAC';
59+
const usages = ['sign', 'verify'];
60+
61+
assert.throws(() => {
62+
createSecretKey(Buffer.alloc(0)).toCryptoKey({ name: algorithm, hash: 'SHA-256' }, true, usages);
63+
}, {
64+
name: 'DataError',
65+
message: 'Zero-length key is not supported',
66+
});
67+
68+
assert.throws(() => {
69+
hmac.toCryptoKey({
70+
name: algorithm,
71+
hash: 'SHA-256',
72+
}, true, []);
73+
}, {
74+
name: 'SyntaxError',
75+
message: 'Usages cannot be empty when importing a secret key.'
76+
});
77+
78+
for (const hash of ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512']) {
79+
for (const extractable of [true, false]) {
80+
assert.throws(() => {
81+
hmac.toCryptoKey({ name: algorithm, hash: 'SHA-256', length: 0 }, true, usages);
82+
}, {
83+
name: 'DataError',
84+
message: 'Zero-length key is not supported',
85+
});
86+
const cryptoKey = hmac.toCryptoKey({ name: algorithm, hash }, extractable, usages);
87+
assertCryptoKey(cryptoKey, hmac, algorithm, extractable, usages);
88+
assert.strictEqual(cryptoKey.algorithm.length, length);
89+
}
90+
}
91+
}
92+
}
93+
94+
{
95+
for (const algorithm of ['Ed25519', 'Ed448', 'X25519', 'X448']) {
96+
const { publicKey, privateKey } = generateKeyPairSync(algorithm.toLowerCase());
97+
assert.throws(() => {
98+
publicKey.toCryptoKey(algorithm === 'Ed25519' ? 'X25519' : 'Ed25519', true, []);
99+
}, {
100+
name: 'DataError',
101+
message: 'Invalid key type'
102+
});
103+
for (const key of [publicKey, privateKey]) {
104+
let usages;
105+
if (algorithm.startsWith('E')) {
106+
usages = key.type === 'public' ? ['verify'] : ['sign'];
107+
} else {
108+
usages = key.type === 'public' ? [] : ['deriveBits'];
109+
}
110+
for (const extractable of [true, false]) {
111+
const cryptoKey = key.toCryptoKey(algorithm, extractable, usages);
112+
assertCryptoKey(cryptoKey, key, algorithm, extractable, usages);
113+
}
114+
}
115+
}
116+
}
117+
118+
{
119+
const { publicKey, privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 });
120+
for (const key of [publicKey, privateKey]) {
121+
for (const algorithm of ['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'RSA-OAEP']) {
122+
let usages;
123+
if (algorithm === 'RSA-OAEP') {
124+
usages = key.type === 'public' ? ['encrypt', 'wrapKey'] : ['decrypt', 'unwrapKey'];
125+
} else {
126+
usages = key.type === 'public' ? ['verify'] : ['sign'];
127+
}
128+
for (const extractable of [true, false]) {
129+
for (const hash of ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512']) {
130+
const cryptoKey = key.toCryptoKey({
131+
name: algorithm,
132+
hash
133+
}, extractable, usages);
134+
assertCryptoKey(cryptoKey, key, algorithm, extractable, usages);
135+
assert.strictEqual(cryptoKey.algorithm.hash.name, hash);
136+
}
137+
}
138+
}
139+
}
140+
}
141+
142+
{
143+
for (const namedCurve of ['P-256', 'P-384', 'P-521']) {
144+
const { publicKey, privateKey } = generateKeyPairSync('ec', { namedCurve });
145+
assert.throws(() => {
146+
privateKey.toCryptoKey({
147+
name: 'ECDH',
148+
namedCurve,
149+
}, true, []);
150+
}, {
151+
name: 'SyntaxError',
152+
message: 'Usages cannot be empty when importing a private key.'
153+
});
154+
assert.throws(() => {
155+
publicKey.toCryptoKey({
156+
name: 'ECDH',
157+
namedCurve: namedCurve === 'P-256' ? 'P-384' : 'P-256'
158+
}, true, []);
159+
}, {
160+
name: 'DataError',
161+
message: 'Named curve mismatch'
162+
});
163+
for (const key of [publicKey, privateKey]) {
164+
for (const algorithm of ['ECDH', 'ECDSA']) {
165+
let usages;
166+
if (algorithm === 'ECDH') {
167+
usages = key.type === 'public' ? [] : ['deriveBits'];
168+
} else {
169+
usages = key.type === 'public' ? ['verify'] : ['sign'];
170+
}
171+
for (const extractable of [true, false]) {
172+
const cryptoKey = key.toCryptoKey({
173+
name: algorithm,
174+
namedCurve
175+
}, extractable, usages);
176+
assertCryptoKey(cryptoKey, key, algorithm, extractable, usages);
177+
assert.strictEqual(cryptoKey.algorithm.namedCurve, namedCurve);
178+
}
179+
}
180+
}
181+
}
182+
}

‎test/parallel/test-webcrypto-export-import.js

+5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ const { subtle } = globalThis.crypto;
2121
subtle.importKey('not valid', keyData, {}, false, ['wrapKey']), {
2222
code: 'ERR_INVALID_ARG_VALUE'
2323
});
24+
await assert.rejects(
25+
subtle.importKey('KeyObject', keyData, {}, false, ['wrapKey']), {
26+
message: /'KeyObject' is not a valid enum value of type KeyFormat/,
27+
code: 'ERR_INVALID_ARG_VALUE'
28+
});
2429
await assert.rejects(
2530
subtle.importKey('raw', 1, {}, false, ['deriveBits']), {
2631
code: 'ERR_INVALID_ARG_TYPE'

0 commit comments

Comments
 (0)
Please sign in to comment.