mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
crypto: add KeyObject.prototype.toCryptoKey
PR-URL: https://github.com/nodejs/node/pull/55262 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
This commit is contained in:
parent
7af434fc19
commit
90e3e5e173
@ -2136,6 +2136,24 @@ added: v11.6.0
|
||||
For secret keys, this property represents the size of the key in bytes. This
|
||||
property is `undefined` for asymmetric keys.
|
||||
|
||||
### `keyObject.toCryptoKey(algorithm, extractable, keyUsages)`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
<!--lint disable maximum-line-length remark-lint-->
|
||||
|
||||
* `algorithm`: {AlgorithmIdentifier|RsaHashedImportParams|EcKeyImportParams|HmacImportParams}
|
||||
|
||||
<!--lint enable maximum-line-length remark-lint-->
|
||||
|
||||
* `extractable`: {boolean}
|
||||
* `keyUsages`: {string\[]} See [Key usages][].
|
||||
* Returns: {CryptoKey}
|
||||
|
||||
Converts a `KeyObject` instance to a `CryptoKey`.
|
||||
|
||||
### `keyObject.type`
|
||||
|
||||
<!-- YAML
|
||||
@ -6087,6 +6105,7 @@ See the [list of SSL OP Flags][] for details.
|
||||
[FIPS provider from OpenSSL 3]: https://www.openssl.org/docs/man3.0/man7/crypto.html#FIPS-provider
|
||||
[HTML 5.2]: https://www.w3.org/TR/html52/changes.html#features-removed
|
||||
[JWK]: https://tools.ietf.org/html/rfc7517
|
||||
[Key usages]: webcrypto.md#cryptokeyusages
|
||||
[NIST SP 800-131A]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf
|
||||
[NIST SP 800-132]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf
|
||||
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
||||
|
@ -245,7 +245,7 @@ async function aesGenerateKey(algorithm, extractable, keyUsages) {
|
||||
extractable);
|
||||
}
|
||||
|
||||
async function aesImportKey(
|
||||
function aesImportKey(
|
||||
algorithm,
|
||||
format,
|
||||
keyData,
|
||||
@ -266,6 +266,11 @@ async function aesImportKey(
|
||||
let keyObject;
|
||||
let length;
|
||||
switch (format) {
|
||||
case 'KeyObject': {
|
||||
validateKeyLength(keyData.symmetricKeySize * 8);
|
||||
keyObject = keyData;
|
||||
break;
|
||||
}
|
||||
case 'raw': {
|
||||
validateKeyLength(keyData.byteLength * 8);
|
||||
keyObject = createSecretKey(keyData);
|
||||
|
@ -197,7 +197,7 @@ function cfrgExportKey(key, format) {
|
||||
key[kKeyObject][kHandle]));
|
||||
}
|
||||
|
||||
async function cfrgImportKey(
|
||||
function cfrgImportKey(
|
||||
format,
|
||||
keyData,
|
||||
algorithm,
|
||||
@ -208,6 +208,11 @@ async function cfrgImportKey(
|
||||
let keyObject;
|
||||
const usagesSet = new SafeSet(keyUsages);
|
||||
switch (format) {
|
||||
case 'KeyObject': {
|
||||
verifyAcceptableCfrgKeyUse(name, keyData.type === 'public', usagesSet);
|
||||
keyObject = keyData;
|
||||
break;
|
||||
}
|
||||
case 'spki': {
|
||||
verifyAcceptableCfrgKeyUse(name, true, usagesSet);
|
||||
try {
|
||||
|
@ -149,7 +149,7 @@ function ecExportKey(key, format) {
|
||||
key[kKeyObject][kHandle]));
|
||||
}
|
||||
|
||||
async function ecImportKey(
|
||||
function ecImportKey(
|
||||
format,
|
||||
keyData,
|
||||
algorithm,
|
||||
@ -167,6 +167,11 @@ async function ecImportKey(
|
||||
let keyObject;
|
||||
const usagesSet = new SafeSet(keyUsages);
|
||||
switch (format) {
|
||||
case 'KeyObject': {
|
||||
verifyAcceptableEcKeyUse(name, keyData.type === 'public', usagesSet);
|
||||
keyObject = keyData;
|
||||
break;
|
||||
}
|
||||
case 'spki': {
|
||||
verifyAcceptableEcKeyUse(name, true, usagesSet);
|
||||
try {
|
||||
|
@ -6,6 +6,7 @@ const {
|
||||
ObjectDefineProperties,
|
||||
ObjectDefineProperty,
|
||||
ObjectSetPrototypeOf,
|
||||
SafeSet,
|
||||
Symbol,
|
||||
SymbolToStringTag,
|
||||
Uint8Array,
|
||||
@ -49,6 +50,8 @@ const {
|
||||
kKeyObject,
|
||||
getArrayBufferOrView,
|
||||
bigIntArrayToUnsignedBigInt,
|
||||
normalizeAlgorithm,
|
||||
hasAnyNotIn,
|
||||
} = require('internal/crypto/util');
|
||||
|
||||
const {
|
||||
@ -65,6 +68,7 @@ const {
|
||||
const {
|
||||
customInspectSymbol: kInspect,
|
||||
kEnumerableProperty,
|
||||
lazyDOMException,
|
||||
} = require('internal/util');
|
||||
|
||||
const { inspect } = require('internal/util/inspect');
|
||||
@ -148,6 +152,8 @@ const {
|
||||
},
|
||||
});
|
||||
|
||||
let webidl;
|
||||
|
||||
class SecretKeyObject extends KeyObject {
|
||||
constructor(handle) {
|
||||
super('secret', handle);
|
||||
@ -168,6 +174,51 @@ const {
|
||||
}
|
||||
return this[kHandle].export();
|
||||
}
|
||||
|
||||
toCryptoKey(algorithm, extractable, keyUsages) {
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
algorithm = normalizeAlgorithm(webidl.converters.AlgorithmIdentifier(algorithm), 'importKey');
|
||||
extractable = webidl.converters.boolean(extractable);
|
||||
keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages);
|
||||
|
||||
let result;
|
||||
switch (algorithm.name) {
|
||||
case 'HMAC':
|
||||
result = require('internal/crypto/mac')
|
||||
.hmacImportKey('KeyObject', this, algorithm, extractable, keyUsages);
|
||||
break;
|
||||
case 'AES-CTR':
|
||||
// Fall through
|
||||
case 'AES-CBC':
|
||||
// Fall through
|
||||
case 'AES-GCM':
|
||||
// Fall through
|
||||
case 'AES-KW':
|
||||
result = require('internal/crypto/aes')
|
||||
.aesImportKey(algorithm, 'KeyObject', this, extractable, keyUsages);
|
||||
break;
|
||||
case 'HKDF':
|
||||
// Fall through
|
||||
case 'PBKDF2':
|
||||
result = importGenericSecretKey(
|
||||
algorithm,
|
||||
'KeyObject',
|
||||
this,
|
||||
extractable,
|
||||
keyUsages);
|
||||
break;
|
||||
default:
|
||||
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
||||
}
|
||||
|
||||
if (result.usages.length === 0) {
|
||||
throw lazyDOMException(
|
||||
`Usages cannot be empty when importing a ${result.type} key.`,
|
||||
'SyntaxError');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const kAsymmetricKeyType = Symbol('kAsymmetricKeyType');
|
||||
@ -209,6 +260,51 @@ const {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
toCryptoKey(algorithm, extractable, keyUsages) {
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
algorithm = normalizeAlgorithm(webidl.converters.AlgorithmIdentifier(algorithm), 'importKey');
|
||||
extractable = webidl.converters.boolean(extractable);
|
||||
keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages);
|
||||
|
||||
let result;
|
||||
switch (algorithm.name) {
|
||||
case 'RSASSA-PKCS1-v1_5':
|
||||
// Fall through
|
||||
case 'RSA-PSS':
|
||||
// Fall through
|
||||
case 'RSA-OAEP':
|
||||
result = require('internal/crypto/rsa')
|
||||
.rsaImportKey('KeyObject', this, algorithm, extractable, keyUsages);
|
||||
break;
|
||||
case 'ECDSA':
|
||||
// Fall through
|
||||
case 'ECDH':
|
||||
result = require('internal/crypto/ec')
|
||||
.ecImportKey('KeyObject', this, algorithm, extractable, keyUsages);
|
||||
break;
|
||||
case 'Ed25519':
|
||||
// Fall through
|
||||
case 'Ed448':
|
||||
// Fall through
|
||||
case 'X25519':
|
||||
// Fall through
|
||||
case 'X448':
|
||||
result = require('internal/crypto/cfrg')
|
||||
.cfrgImportKey('KeyObject', this, algorithm, extractable, keyUsages);
|
||||
break;
|
||||
default:
|
||||
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
||||
}
|
||||
|
||||
if (result.type === 'private' && result.usages.length === 0) {
|
||||
throw lazyDOMException(
|
||||
`Usages cannot be empty when importing a ${result.type} key.`,
|
||||
'SyntaxError');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class PublicKeyObject extends AsymmetricKeyObject {
|
||||
@ -801,6 +897,68 @@ function isCryptoKey(obj) {
|
||||
return obj != null && obj[kKeyObject] !== undefined;
|
||||
}
|
||||
|
||||
function importGenericSecretKey(
|
||||
{ name, length },
|
||||
format,
|
||||
keyData,
|
||||
extractable,
|
||||
keyUsages) {
|
||||
const usagesSet = new SafeSet(keyUsages);
|
||||
if (extractable)
|
||||
throw lazyDOMException(`${name} keys are not extractable`, 'SyntaxError');
|
||||
|
||||
if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
|
||||
throw lazyDOMException(
|
||||
`Unsupported key usage for a ${name} key`,
|
||||
'SyntaxError');
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case 'KeyObject': {
|
||||
if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
|
||||
throw lazyDOMException(
|
||||
`Unsupported key usage for a ${name} key`,
|
||||
'SyntaxError');
|
||||
}
|
||||
|
||||
const checkLength = keyData.symmetricKeySize * 8;
|
||||
|
||||
// The Web Crypto spec allows for key lengths that are not multiples of
|
||||
// 8. We don't. Our check here is stricter than that defined by the spec
|
||||
// in that we require that algorithm.length match keyData.length * 8 if
|
||||
// algorithm.length is specified.
|
||||
if (length !== undefined && length !== checkLength) {
|
||||
throw lazyDOMException('Invalid key length', 'DataError');
|
||||
}
|
||||
return new InternalCryptoKey(keyData, { name }, keyUsages, false);
|
||||
}
|
||||
case 'raw': {
|
||||
if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
|
||||
throw lazyDOMException(
|
||||
`Unsupported key usage for a ${name} key`,
|
||||
'SyntaxError');
|
||||
}
|
||||
|
||||
const checkLength = keyData.byteLength * 8;
|
||||
|
||||
// The Web Crypto spec allows for key lengths that are not multiples of
|
||||
// 8. We don't. Our check here is stricter than that defined by the spec
|
||||
// in that we require that algorithm.length match keyData.length * 8 if
|
||||
// algorithm.length is specified.
|
||||
if (length !== undefined && length !== checkLength) {
|
||||
throw lazyDOMException('Invalid key length', 'DataError');
|
||||
}
|
||||
|
||||
const keyObject = createSecretKey(keyData);
|
||||
return new InternalCryptoKey(keyObject, { name }, keyUsages, false);
|
||||
}
|
||||
}
|
||||
|
||||
throw lazyDOMException(
|
||||
`Unable to import ${name} key with format ${format}`,
|
||||
'NotSupportedError');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
// Public API.
|
||||
createSecretKey,
|
||||
@ -822,4 +980,5 @@ module.exports = {
|
||||
PrivateKeyObject,
|
||||
isKeyObject,
|
||||
isCryptoKey,
|
||||
importGenericSecretKey,
|
||||
};
|
||||
|
@ -82,7 +82,7 @@ function getAlgorithmName(hash) {
|
||||
}
|
||||
}
|
||||
|
||||
async function hmacImportKey(
|
||||
function hmacImportKey(
|
||||
format,
|
||||
keyData,
|
||||
algorithm,
|
||||
@ -96,6 +96,24 @@ async function hmacImportKey(
|
||||
}
|
||||
let keyObject;
|
||||
switch (format) {
|
||||
case 'KeyObject': {
|
||||
const checkLength = keyData.symmetricKeySize * 8;
|
||||
|
||||
if (checkLength === 0 || algorithm.length === 0)
|
||||
throw lazyDOMException('Zero-length key is not supported', 'DataError');
|
||||
|
||||
// The Web Crypto spec allows for key lengths that are not multiples of
|
||||
// 8. We don't. Our check here is stricter than that defined by the spec
|
||||
// in that we require that algorithm.length match keyData.length * 8 if
|
||||
// algorithm.length is specified.
|
||||
if (algorithm.length !== undefined &&
|
||||
algorithm.length !== checkLength) {
|
||||
throw lazyDOMException('Invalid key length', 'DataError');
|
||||
}
|
||||
|
||||
keyObject = keyData;
|
||||
break;
|
||||
}
|
||||
case 'raw': {
|
||||
const checkLength = keyData.byteLength * 8;
|
||||
|
||||
|
@ -200,7 +200,7 @@ function rsaExportKey(key, format) {
|
||||
kRsaVariants[key.algorithm.name]));
|
||||
}
|
||||
|
||||
async function rsaImportKey(
|
||||
function rsaImportKey(
|
||||
format,
|
||||
keyData,
|
||||
algorithm,
|
||||
@ -209,6 +209,11 @@ async function rsaImportKey(
|
||||
const usagesSet = new SafeSet(keyUsages);
|
||||
let keyObject;
|
||||
switch (format) {
|
||||
case 'KeyObject': {
|
||||
verifyAcceptableRsaKeyUse(algorithm.name, keyData.type === 'public', usagesSet);
|
||||
keyObject = keyData;
|
||||
break;
|
||||
}
|
||||
case 'spki': {
|
||||
verifyAcceptableRsaKeyUse(algorithm.name, true, usagesSet);
|
||||
try {
|
||||
|
@ -7,7 +7,6 @@ const {
|
||||
ObjectDefineProperties,
|
||||
ReflectApply,
|
||||
ReflectConstruct,
|
||||
SafeSet,
|
||||
StringPrototypeRepeat,
|
||||
SymbolToStringTag,
|
||||
} = primordials;
|
||||
@ -31,8 +30,7 @@ const {
|
||||
|
||||
const {
|
||||
CryptoKey,
|
||||
InternalCryptoKey,
|
||||
createSecretKey,
|
||||
importGenericSecretKey,
|
||||
} = require('internal/crypto/keys');
|
||||
|
||||
const {
|
||||
@ -41,7 +39,6 @@ const {
|
||||
|
||||
const {
|
||||
getBlockSize,
|
||||
hasAnyNotIn,
|
||||
normalizeAlgorithm,
|
||||
normalizeHashName,
|
||||
validateMaxBufferLength,
|
||||
@ -521,50 +518,6 @@ async function exportKey(format, key) {
|
||||
'Export format is unsupported', 'NotSupportedError');
|
||||
}
|
||||
|
||||
async function importGenericSecretKey(
|
||||
{ name, length },
|
||||
format,
|
||||
keyData,
|
||||
extractable,
|
||||
keyUsages) {
|
||||
const usagesSet = new SafeSet(keyUsages);
|
||||
if (extractable)
|
||||
throw lazyDOMException(`${name} keys are not extractable`, 'SyntaxError');
|
||||
|
||||
if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
|
||||
throw lazyDOMException(
|
||||
`Unsupported key usage for a ${name} key`,
|
||||
'SyntaxError');
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case 'raw': {
|
||||
if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
|
||||
throw lazyDOMException(
|
||||
`Unsupported key usage for a ${name} key`,
|
||||
'SyntaxError');
|
||||
}
|
||||
|
||||
const checkLength = keyData.byteLength * 8;
|
||||
|
||||
// The Web Crypto spec allows for key lengths that are not multiples of
|
||||
// 8. We don't. Our check here is stricter than that defined by the spec
|
||||
// in that we require that algorithm.length match keyData.length * 8 if
|
||||
// algorithm.length is specified.
|
||||
if (length !== undefined && length !== checkLength) {
|
||||
throw lazyDOMException('Invalid key length', 'DataError');
|
||||
}
|
||||
|
||||
const keyObject = createSecretKey(keyData);
|
||||
return new InternalCryptoKey(keyObject, { name }, keyUsages, false);
|
||||
}
|
||||
}
|
||||
|
||||
throw lazyDOMException(
|
||||
`Unable to import ${name} key with format ${format}`,
|
||||
'NotSupportedError');
|
||||
}
|
||||
|
||||
async function importKey(
|
||||
format,
|
||||
keyData,
|
||||
@ -606,13 +559,13 @@ async function importKey(
|
||||
case 'RSA-PSS':
|
||||
// Fall through
|
||||
case 'RSA-OAEP':
|
||||
result = await require('internal/crypto/rsa')
|
||||
result = require('internal/crypto/rsa')
|
||||
.rsaImportKey(format, keyData, algorithm, extractable, keyUsages);
|
||||
break;
|
||||
case 'ECDSA':
|
||||
// Fall through
|
||||
case 'ECDH':
|
||||
result = await require('internal/crypto/ec')
|
||||
result = require('internal/crypto/ec')
|
||||
.ecImportKey(format, keyData, algorithm, extractable, keyUsages);
|
||||
break;
|
||||
case 'Ed25519':
|
||||
@ -622,11 +575,11 @@ async function importKey(
|
||||
case 'X25519':
|
||||
// Fall through
|
||||
case 'X448':
|
||||
result = await require('internal/crypto/cfrg')
|
||||
result = require('internal/crypto/cfrg')
|
||||
.cfrgImportKey(format, keyData, algorithm, extractable, keyUsages);
|
||||
break;
|
||||
case 'HMAC':
|
||||
result = await require('internal/crypto/mac')
|
||||
result = require('internal/crypto/mac')
|
||||
.hmacImportKey(format, keyData, algorithm, extractable, keyUsages);
|
||||
break;
|
||||
case 'AES-CTR':
|
||||
@ -636,13 +589,13 @@ async function importKey(
|
||||
case 'AES-GCM':
|
||||
// Fall through
|
||||
case 'AES-KW':
|
||||
result = await require('internal/crypto/aes')
|
||||
result = require('internal/crypto/aes')
|
||||
.aesImportKey(algorithm, format, keyData, extractable, keyUsages);
|
||||
break;
|
||||
case 'HKDF':
|
||||
// Fall through
|
||||
case 'PBKDF2':
|
||||
result = await importGenericSecretKey(
|
||||
result = importGenericSecretKey(
|
||||
algorithm,
|
||||
format,
|
||||
keyData,
|
||||
|
182
test/parallel/test-crypto-key-objects-to-crypto-key.js
Normal file
182
test/parallel/test-crypto-key-objects-to-crypto-key.js
Normal file
@ -0,0 +1,182 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const assert = require('assert');
|
||||
const {
|
||||
createSecretKey,
|
||||
KeyObject,
|
||||
randomBytes,
|
||||
generateKeyPairSync,
|
||||
} = require('crypto');
|
||||
|
||||
function assertCryptoKey(cryptoKey, keyObject, algorithm, extractable, usages) {
|
||||
assert.strictEqual(cryptoKey instanceof CryptoKey, true);
|
||||
assert.strictEqual(cryptoKey.type, keyObject.type);
|
||||
assert.strictEqual(cryptoKey.algorithm.name, algorithm);
|
||||
assert.strictEqual(cryptoKey.extractable, extractable);
|
||||
assert.deepStrictEqual(cryptoKey.usages, usages);
|
||||
assert.strictEqual(keyObject.equals(KeyObject.from(cryptoKey)), true);
|
||||
}
|
||||
|
||||
{
|
||||
for (const length of [128, 192, 256]) {
|
||||
const aes = createSecretKey(randomBytes(length >> 3));
|
||||
for (const algorithm of ['AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-KW']) {
|
||||
const usages = algorithm === 'AES-KW' ? ['wrapKey', 'unwrapKey'] : ['encrypt', 'decrypt'];
|
||||
for (const extractable of [true, false]) {
|
||||
const cryptoKey = aes.toCryptoKey(algorithm, extractable, usages);
|
||||
assertCryptoKey(cryptoKey, aes, algorithm, extractable, usages);
|
||||
assert.strictEqual(cryptoKey.algorithm.length, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const pbkdf2 = createSecretKey(randomBytes(16));
|
||||
const algorithm = 'PBKDF2';
|
||||
const usages = ['deriveBits'];
|
||||
assert.throws(() => pbkdf2.toCryptoKey(algorithm, true, usages), {
|
||||
name: 'SyntaxError',
|
||||
message: 'PBKDF2 keys are not extractable'
|
||||
});
|
||||
assert.throws(() => pbkdf2.toCryptoKey(algorithm, false, ['wrapKey']), {
|
||||
name: 'SyntaxError',
|
||||
message: 'Unsupported key usage for a PBKDF2 key'
|
||||
});
|
||||
const cryptoKey = pbkdf2.toCryptoKey(algorithm, false, usages);
|
||||
assertCryptoKey(cryptoKey, pbkdf2, algorithm, false, usages);
|
||||
assert.strictEqual(cryptoKey.algorithm.length, undefined);
|
||||
}
|
||||
|
||||
{
|
||||
for (const length of [128, 192, 256]) {
|
||||
const hmac = createSecretKey(randomBytes(length >> 3));
|
||||
const algorithm = 'HMAC';
|
||||
const usages = ['sign', 'verify'];
|
||||
|
||||
assert.throws(() => {
|
||||
createSecretKey(Buffer.alloc(0)).toCryptoKey({ name: algorithm, hash: 'SHA-256' }, true, usages);
|
||||
}, {
|
||||
name: 'DataError',
|
||||
message: 'Zero-length key is not supported',
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
hmac.toCryptoKey({
|
||||
name: algorithm,
|
||||
hash: 'SHA-256',
|
||||
}, true, []);
|
||||
}, {
|
||||
name: 'SyntaxError',
|
||||
message: 'Usages cannot be empty when importing a secret key.'
|
||||
});
|
||||
|
||||
for (const hash of ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512']) {
|
||||
for (const extractable of [true, false]) {
|
||||
assert.throws(() => {
|
||||
hmac.toCryptoKey({ name: algorithm, hash: 'SHA-256', length: 0 }, true, usages);
|
||||
}, {
|
||||
name: 'DataError',
|
||||
message: 'Zero-length key is not supported',
|
||||
});
|
||||
const cryptoKey = hmac.toCryptoKey({ name: algorithm, hash }, extractable, usages);
|
||||
assertCryptoKey(cryptoKey, hmac, algorithm, extractable, usages);
|
||||
assert.strictEqual(cryptoKey.algorithm.length, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for (const algorithm of ['Ed25519', 'Ed448', 'X25519', 'X448']) {
|
||||
const { publicKey, privateKey } = generateKeyPairSync(algorithm.toLowerCase());
|
||||
assert.throws(() => {
|
||||
publicKey.toCryptoKey(algorithm === 'Ed25519' ? 'X25519' : 'Ed25519', true, []);
|
||||
}, {
|
||||
name: 'DataError',
|
||||
message: 'Invalid key type'
|
||||
});
|
||||
for (const key of [publicKey, privateKey]) {
|
||||
let usages;
|
||||
if (algorithm.startsWith('E')) {
|
||||
usages = key.type === 'public' ? ['verify'] : ['sign'];
|
||||
} else {
|
||||
usages = key.type === 'public' ? [] : ['deriveBits'];
|
||||
}
|
||||
for (const extractable of [true, false]) {
|
||||
const cryptoKey = key.toCryptoKey(algorithm, extractable, usages);
|
||||
assertCryptoKey(cryptoKey, key, algorithm, extractable, usages);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const { publicKey, privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 });
|
||||
for (const key of [publicKey, privateKey]) {
|
||||
for (const algorithm of ['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'RSA-OAEP']) {
|
||||
let usages;
|
||||
if (algorithm === 'RSA-OAEP') {
|
||||
usages = key.type === 'public' ? ['encrypt', 'wrapKey'] : ['decrypt', 'unwrapKey'];
|
||||
} else {
|
||||
usages = key.type === 'public' ? ['verify'] : ['sign'];
|
||||
}
|
||||
for (const extractable of [true, false]) {
|
||||
for (const hash of ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512']) {
|
||||
const cryptoKey = key.toCryptoKey({
|
||||
name: algorithm,
|
||||
hash
|
||||
}, extractable, usages);
|
||||
assertCryptoKey(cryptoKey, key, algorithm, extractable, usages);
|
||||
assert.strictEqual(cryptoKey.algorithm.hash.name, hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for (const namedCurve of ['P-256', 'P-384', 'P-521']) {
|
||||
const { publicKey, privateKey } = generateKeyPairSync('ec', { namedCurve });
|
||||
assert.throws(() => {
|
||||
privateKey.toCryptoKey({
|
||||
name: 'ECDH',
|
||||
namedCurve,
|
||||
}, true, []);
|
||||
}, {
|
||||
name: 'SyntaxError',
|
||||
message: 'Usages cannot be empty when importing a private key.'
|
||||
});
|
||||
assert.throws(() => {
|
||||
publicKey.toCryptoKey({
|
||||
name: 'ECDH',
|
||||
namedCurve: namedCurve === 'P-256' ? 'P-384' : 'P-256'
|
||||
}, true, []);
|
||||
}, {
|
||||
name: 'DataError',
|
||||
message: 'Named curve mismatch'
|
||||
});
|
||||
for (const key of [publicKey, privateKey]) {
|
||||
for (const algorithm of ['ECDH', 'ECDSA']) {
|
||||
let usages;
|
||||
if (algorithm === 'ECDH') {
|
||||
usages = key.type === 'public' ? [] : ['deriveBits'];
|
||||
} else {
|
||||
usages = key.type === 'public' ? ['verify'] : ['sign'];
|
||||
}
|
||||
for (const extractable of [true, false]) {
|
||||
const cryptoKey = key.toCryptoKey({
|
||||
name: algorithm,
|
||||
namedCurve
|
||||
}, extractable, usages);
|
||||
assertCryptoKey(cryptoKey, key, algorithm, extractable, usages);
|
||||
assert.strictEqual(cryptoKey.algorithm.namedCurve, namedCurve);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,11 @@ const { subtle } = globalThis.crypto;
|
||||
subtle.importKey('not valid', keyData, {}, false, ['wrapKey']), {
|
||||
code: 'ERR_INVALID_ARG_VALUE'
|
||||
});
|
||||
await assert.rejects(
|
||||
subtle.importKey('KeyObject', keyData, {}, false, ['wrapKey']), {
|
||||
message: /'KeyObject' is not a valid enum value of type KeyFormat/,
|
||||
code: 'ERR_INVALID_ARG_VALUE'
|
||||
});
|
||||
await assert.rejects(
|
||||
subtle.importKey('raw', 1, {}, false, ['deriveBits']), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
|
Loading…
Reference in New Issue
Block a user