diff --git a/src/runtime/browser/aeskw.ts b/src/runtime/browser/aeskw.ts index 44afb3efbc..d3e86b80a6 100644 --- a/src/runtime/browser/aeskw.ts +++ b/src/runtime/browser/aeskw.ts @@ -1,6 +1,6 @@ import type { AesKwUnwrapFunction, AesKwWrapFunction } from '../interfaces.d' import bogusWebCrypto from './bogus.js' -import crypto, { isCryptoKey } from './webcrypto.js' +import crypto, { checkCryptoKey, isCryptoKey } from './webcrypto.js' import invalidKeyInput from './invalid_key_input.js' function checkKeySize(key: CryptoKey, alg: string) { @@ -9,8 +9,9 @@ function checkKeySize(key: CryptoKey, alg: string) { } } -function getCryptoKey(key: unknown, usage: KeyUsage) { +function getCryptoKey(key: unknown, alg: string, usage: KeyUsage) { if (isCryptoKey(key)) { + checkCryptoKey(key, alg, usage) return key } @@ -22,7 +23,7 @@ function getCryptoKey(key: unknown, usage: KeyUsage) { } export const wrap: AesKwWrapFunction = async (alg: string, key: unknown, cek: Uint8Array) => { - const cryptoKey = await getCryptoKey(key, 'wrapKey') + const cryptoKey = await getCryptoKey(key, alg, 'wrapKey') checkKeySize(cryptoKey, alg) @@ -37,7 +38,7 @@ export const unwrap: AesKwUnwrapFunction = async ( key: unknown, encryptedKey: Uint8Array, ) => { - const cryptoKey = await getCryptoKey(key, 'unwrapKey') + const cryptoKey = await getCryptoKey(key, alg, 'unwrapKey') checkKeySize(cryptoKey, alg) diff --git a/src/runtime/browser/check_cek_length.ts b/src/runtime/browser/check_cek_length.ts deleted file mode 100644 index caceff1005..0000000000 --- a/src/runtime/browser/check_cek_length.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { JWEInvalid, JOSENotSupported } from '../../util/errors.js' -import { isCryptoKey } from './webcrypto.js' - -const checkCekLength = (enc: string, cek: Uint8Array | CryptoKey) => { - let expected: number - switch (enc) { - case 'A128CBC-HS256': - case 'A192CBC-HS384': - case 'A256CBC-HS512': - expected = parseInt(enc.substr(-3), 10) - if (!(cek instanceof Uint8Array)) { - throw new TypeError(`${enc} content encryption requires Uint8Array as key input`) - } - break - case 'A128GCM': - case 'A192GCM': - case 'A256GCM': - expected = parseInt(enc.substr(1, 3), 10) - break - default: - throw new JOSENotSupported( - `Content Encryption Algorithm ${enc} is not supported either by JOSE or your javascript runtime`, - ) - } - - if (cek instanceof Uint8Array) { - if (cek.length << 3 !== expected) { - throw new JWEInvalid('Invalid Content Encryption Key length') - } - return - } - - // CryptoKey - if (isCryptoKey(cek)) { - const { length } = cek.algorithm - if (length !== expected) { - throw new JWEInvalid('Invalid Content Encryption Key length') - } - return - } - - throw new TypeError('Invalid Content Encryption Key type') -} - -export default checkCekLength diff --git a/src/runtime/browser/decrypt.ts b/src/runtime/browser/decrypt.ts index cc0a4edf61..6c259b411d 100644 --- a/src/runtime/browser/decrypt.ts +++ b/src/runtime/browser/decrypt.ts @@ -2,20 +2,22 @@ import { concat, uint64be } from '../../lib/buffer_utils.js' import type { DecryptFunction } from '../interfaces.d' import checkIvLength from '../../lib/check_iv_length.js' -import checkCekLength from './check_cek_length.js' import timingSafeEqual from './timing_safe_equal.js' import { JOSENotSupported, JWEDecryptionFailed } from '../../util/errors.js' -import crypto, { isCryptoKey } from './webcrypto.js' +import crypto, { checkCryptoKey, isCryptoKey } from './webcrypto.js' import invalidKeyInput from './invalid_key_input.js' async function cbcDecrypt( enc: string, - cek: Uint8Array, + cek: Uint8Array | CryptoKey, ciphertext: Uint8Array, iv: Uint8Array, tag: Uint8Array, aad: Uint8Array, ) { + if (!(cek instanceof Uint8Array)) { + throw new TypeError(invalidKeyInput(cek, 'Uint8Array')) + } const keySize = parseInt(enc.substr(1, 3), 10) const encKey = await crypto.subtle.importKey( 'raw', @@ -66,16 +68,20 @@ async function cbcDecrypt( } async function gcmDecrypt( + enc: string, cek: Uint8Array | CryptoKey, ciphertext: Uint8Array, iv: Uint8Array, tag: Uint8Array, aad: Uint8Array, ) { - const encKey = - cek instanceof Uint8Array - ? await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, ['decrypt']) - : cek + let encKey: CryptoKey + if (cek instanceof Uint8Array) { + encKey = await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, ['decrypt']) + } else { + checkCryptoKey(cek, enc, 'decrypt') + encKey = cek + } try { return new Uint8Array( @@ -108,18 +114,17 @@ const decrypt: DecryptFunction = async ( throw new TypeError(invalidKeyInput(cek, 'CryptoKey', 'Uint8Array')) } - checkCekLength(enc, cek) checkIvLength(enc, iv) switch (enc) { case 'A128CBC-HS256': case 'A192CBC-HS384': case 'A256CBC-HS512': - return cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) + return cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) case 'A128GCM': case 'A192GCM': case 'A256GCM': - return gcmDecrypt(cek, ciphertext, iv, tag, aad) + return gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) default: throw new JOSENotSupported('Unsupported JWE Content Encryption Algorithm') } diff --git a/src/runtime/browser/ecdhes.ts b/src/runtime/browser/ecdhes.ts index b524dac24a..aa1c07a973 100644 --- a/src/runtime/browser/ecdhes.ts +++ b/src/runtime/browser/ecdhes.ts @@ -4,7 +4,7 @@ import type { GenerateEpkFunction, } from '../interfaces.d' import { encoder, concat, uint32be, lengthAndInput, concatKdf } from '../../lib/buffer_utils.js' -import crypto, { isCryptoKey } from './webcrypto.js' +import crypto, { checkCryptoKey, isCryptoKey } from './webcrypto.js' import digest from './digest.js' import invalidKeyInput from './invalid_key_input.js' @@ -19,9 +19,11 @@ export const deriveKey: EcdhESDeriveKeyFunction = async ( if (!isCryptoKey(publicKey)) { throw new TypeError(invalidKeyInput(publicKey, 'CryptoKey')) } + checkCryptoKey(publicKey, 'ECDH-ES') if (!isCryptoKey(privateKey)) { throw new TypeError(invalidKeyInput(privateKey, 'CryptoKey')) } + checkCryptoKey(privateKey, 'ECDH-ES', 'deriveBits', 'deriveKey') const value = concat( lengthAndInput(encoder.encode(algorithm)), diff --git a/src/runtime/browser/encrypt.ts b/src/runtime/browser/encrypt.ts index d9f59a84ff..2f741e5779 100644 --- a/src/runtime/browser/encrypt.ts +++ b/src/runtime/browser/encrypt.ts @@ -1,18 +1,20 @@ import { concat, uint64be } from '../../lib/buffer_utils.js' import type { EncryptFunction } from '../interfaces.d' import checkIvLength from '../../lib/check_iv_length.js' -import checkCekLength from './check_cek_length.js' -import crypto, { isCryptoKey } from './webcrypto.js' +import crypto, { checkCryptoKey, isCryptoKey } from './webcrypto.js' import invalidKeyInput from './invalid_key_input.js' import { JOSENotSupported } from '../../util/errors.js' async function cbcEncrypt( enc: string, plaintext: Uint8Array, - cek: Uint8Array, + cek: Uint8Array | CryptoKey, iv: Uint8Array, aad: Uint8Array, ) { + if (!(cek instanceof Uint8Array)) { + throw new TypeError(invalidKeyInput(cek, 'Uint8Array')) + } const keySize = parseInt(enc.substr(1, 3), 10) const encKey = await crypto.subtle.importKey( 'raw', @@ -52,15 +54,19 @@ async function cbcEncrypt( } async function gcmEncrypt( + enc: string, plaintext: Uint8Array, cek: Uint8Array | CryptoKey, iv: Uint8Array, aad: Uint8Array, ) { - const encKey = - cek instanceof Uint8Array - ? await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, ['encrypt']) - : cek + let encKey: CryptoKey + if (cek instanceof Uint8Array) { + encKey = await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, ['encrypt']) + } else { + checkCryptoKey(cek, enc, 'encrypt') + encKey = cek + } const encrypted = new Uint8Array( await crypto.subtle.encrypt( @@ -93,7 +99,6 @@ const encrypt: EncryptFunction = async ( throw new TypeError(invalidKeyInput(cek, 'CryptoKey', 'Uint8Array')) } - checkCekLength(enc, cek) checkIvLength(enc, iv) switch (enc) { @@ -104,7 +109,7 @@ const encrypt: EncryptFunction = async ( case 'A128GCM': case 'A192GCM': case 'A256GCM': - return gcmEncrypt(plaintext, cek, iv, aad) + return gcmEncrypt(enc, plaintext, cek, iv, aad) default: throw new JOSENotSupported('Unsupported JWE Content Encryption Algorithm') } diff --git a/src/runtime/browser/get_sign_verify_key.ts b/src/runtime/browser/get_sign_verify_key.ts index 28320a9c81..5b65536b89 100644 --- a/src/runtime/browser/get_sign_verify_key.ts +++ b/src/runtime/browser/get_sign_verify_key.ts @@ -1,8 +1,9 @@ -import crypto, { isCryptoKey } from './webcrypto.js' +import crypto, { checkCryptoKey, isCryptoKey } from './webcrypto.js' import invalidKeyInput from './invalid_key_input.js' export default function getCryptoKey(alg: string, key: unknown, usage: KeyUsage) { if (isCryptoKey(key)) { + checkCryptoKey(key, alg, usage) return key } diff --git a/src/runtime/browser/pbes2kw.ts b/src/runtime/browser/pbes2kw.ts index 544837e77d..819ded7113 100644 --- a/src/runtime/browser/pbes2kw.ts +++ b/src/runtime/browser/pbes2kw.ts @@ -4,15 +4,16 @@ import { p2s as concatSalt } from '../../lib/buffer_utils.js' import { encode as base64url } from './base64url.js' import { wrap, unwrap } from './aeskw.js' import checkP2s from '../../lib/check_p2s.js' -import crypto, { isCryptoKey } from './webcrypto.js' +import crypto, { checkCryptoKey, isCryptoKey } from './webcrypto.js' import invalidKeyInput from './invalid_key_input.js' -function getCryptoKey(key: unknown) { +function getCryptoKey(key: unknown, alg: string) { if (key instanceof Uint8Array) { return crypto.subtle.importKey('raw', key, 'PBKDF2', false, ['deriveBits']) } if (isCryptoKey(key)) { + checkCryptoKey(key, alg, 'deriveBits', 'deriveKey') return key } @@ -41,7 +42,7 @@ export const encrypt: Pbes2KWEncryptFunction = async ( name: 'AES-KW', } - const cryptoKey = await getCryptoKey(key) + const cryptoKey = await getCryptoKey(key, alg) let derived: CryptoKey | Uint8Array if (cryptoKey.usages.includes('deriveBits')) { @@ -79,7 +80,7 @@ export const decrypt: Pbes2KWDecryptFunction = async ( name: 'AES-KW', } - const cryptoKey = await getCryptoKey(key) + const cryptoKey = await getCryptoKey(key, alg) let derived: CryptoKey | Uint8Array if (cryptoKey.usages.includes('deriveBits')) { diff --git a/src/runtime/browser/rsaes.ts b/src/runtime/browser/rsaes.ts index 96c950a256..fc3479af23 100644 --- a/src/runtime/browser/rsaes.ts +++ b/src/runtime/browser/rsaes.ts @@ -1,7 +1,7 @@ import type { RsaEsDecryptFunction, RsaEsEncryptFunction } from '../interfaces.d' import subtleAlgorithm from './subtle_rsaes.js' import bogusWebCrypto from './bogus.js' -import crypto, { isCryptoKey } from './webcrypto.js' +import crypto, { checkCryptoKey, isCryptoKey } from './webcrypto.js' import checkKeyLength from './check_key_length.js' import invalidKeyInput from './invalid_key_input.js' @@ -9,6 +9,7 @@ export const encrypt: RsaEsEncryptFunction = async (alg: string, key: unknown, c if (!isCryptoKey(key)) { throw new TypeError(invalidKeyInput(key, 'CryptoKey')) } + checkCryptoKey(key, alg, 'encrypt', 'wrapKey') checkKeyLength(alg, key) if (key.usages.includes('encrypt')) { @@ -36,6 +37,7 @@ export const decrypt: RsaEsDecryptFunction = async ( if (!isCryptoKey(key)) { throw new TypeError(invalidKeyInput(key, 'CryptoKey')) } + checkCryptoKey(key, alg, 'decrypt', 'unwrapKey') checkKeyLength(alg, key) if (key.usages.includes('decrypt')) { diff --git a/src/runtime/browser/webcrypto.ts b/src/runtime/browser/webcrypto.ts index 1ec3cb9162..6e2ff35418 100644 --- a/src/runtime/browser/webcrypto.ts +++ b/src/runtime/browser/webcrypto.ts @@ -8,3 +8,192 @@ export function isCryptoKey(key: unknown): key is CryptoKey { } return key != null && key instanceof globalThis.CryptoKey } + +function getHashLength(hash: KeyAlgorithm) { + return parseInt(hash?.name.substr(4), 10) +} + +function getNamedCurve(alg: string) { + switch (alg) { + case 'ES256': + return 'P-256' + case 'ES384': + return 'P-384' + case 'ES512': + return 'P-521' + } +} + +export function checkCryptoKey(key: CryptoKey, alg?: string, ...usages: KeyUsage[]) { + switch (alg) { + case undefined: + break + case 'HS256': + case 'HS384': + case 'HS512': { + if (key.algorithm.name !== 'HMAC') { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.name must be HMAC.`, + ) + } + + const expected = parseInt(alg.substr(2), 10) + const actual = getHashLength((key.algorithm).hash) + if (actual !== expected) { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.hash must be SHA-${expected}.`, + ) + } + break + } + case 'RS256': + case 'RS384': + case 'RS512': { + if (key.algorithm.name !== 'RSASSA-PKCS1-v1_5') { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.name must be RSASSA-PKCS1-v1_5.`, + ) + } + + const expected = parseInt(alg.substr(2), 10) + const actual = getHashLength((key.algorithm).hash) + if (actual !== expected) { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.hash must be SHA-${expected}.`, + ) + } + break + } + case 'PS256': + case 'PS384': + case 'PS512': { + if (key.algorithm.name !== 'RSA-PSS') { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.name must be RSA-PSS.`, + ) + } + + const expected = parseInt(alg.substr(2), 10) + const actual = getHashLength((key.algorithm).hash) + if (actual !== expected) { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.hash must be SHA-${expected}.`, + ) + } + break + } + case 'EdDSA': { + if (key.algorithm.name !== 'NODE-ED25519' && key.algorithm.name !== 'NODE-ED448') { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.name must be NODE-ED25519 or NODE-ED448.`, + ) + } + break + } + case 'ES256': + case 'ES384': + case 'ES512': { + if (key.algorithm.name !== 'ECDSA') { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.name must be ECDSA.`, + ) + } + + const expected = getNamedCurve(alg) + const actual = (key.algorithm).namedCurve + if (actual !== expected) { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.namedCurve must be ${expected}.`, + ) + } + break + } + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': { + if (key.algorithm.name !== 'AES-GCM') { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.name must be AES-GCM.`, + ) + } + + const expected = parseInt(alg.substr(1, 3), 10) + const actual = (key.algorithm).length + if (actual !== expected) { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.length must be ${expected}.`, + ) + } + break + } + case 'A128KW': + case 'A192KW': + case 'A256KW': { + if (key.algorithm.name !== 'AES-KW') { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.name must be AES-KW.`, + ) + } + + const expected = parseInt(alg.substr(1, 3), 10) + const actual = (key.algorithm).length + if (actual !== expected) { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.length must be ${expected}.`, + ) + } + break + } + case 'ECDH-ES': + if (key.algorithm.name !== 'ECDH') { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.name must be ECDH.`, + ) + } + break + case 'PBES2-HS256+A128KW': + case 'PBES2-HS384+A192KW': + case 'PBES2-HS512+A256KW': + if (key.algorithm.name !== 'PBKDF2') { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.name must be PBKDF2.`, + ) + } + break + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': { + if (key.algorithm.name !== 'RSA-OAEP') { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.name must be RSA-OAEP.`, + ) + } + + const expected = parseInt(alg.substr(9), 10) || 1 + const actual = getHashLength((key.algorithm).hash) + if (actual !== expected) { + throw new TypeError( + `CryptoKey does not support this operation, its algorithm.hash must be SHA-${expected}.`, + ) + } + break + } + default: + throw new TypeError('CryptoKey does not support this operation') + } + + if (usages.length && !usages.some((expected) => key.usages.includes(expected))) { + let msg = 'CryptoKey does not support this operation, its usages must include ' + if (usages.length > 2) { + const last = usages.pop() + msg += `one of ${usages.join(', ')}, or ${last}.` + } else if (usages.length === 2) { + msg += `one of ${usages[0]} or ${usages[1]}.` + } else { + msg += `${usages[0]}.` + } + + throw new TypeError(msg) + } +} diff --git a/src/runtime/node/aeskw.ts b/src/runtime/node/aeskw.ts index 2b478be530..74265e65e3 100644 --- a/src/runtime/node/aeskw.ts +++ b/src/runtime/node/aeskw.ts @@ -23,7 +23,7 @@ function ensureKeyObject(key: unknown, alg: string, usage: KeyUsage) { return getSecretKey(key) } if (isCryptoKey(key)) { - return getKeyObject(key, alg, new Set([usage])) + return getKeyObject(key, alg, usage) } throw new TypeError(invalidKeyInput(key, 'KeyObject', 'CryptoKey', 'Uint8Array')) diff --git a/src/runtime/node/decrypt.ts b/src/runtime/node/decrypt.ts index 7e8646443a..19b823acdd 100644 --- a/src/runtime/node/decrypt.ts +++ b/src/runtime/node/decrypt.ts @@ -98,7 +98,7 @@ const decrypt: DecryptFunction = async ( ) => { let key: KeyObject | Uint8Array if (isCryptoKey(cek)) { - key = getKeyObject(cek, enc, new Set(['decrypt'])) + key = getKeyObject(cek, enc, 'decrypt') } else if (cek instanceof Uint8Array || isKeyObject(cek)) { key = cek } else { diff --git a/src/runtime/node/ecdhes.ts b/src/runtime/node/ecdhes.ts index 9e5bc9057d..56fd9415dc 100644 --- a/src/runtime/node/ecdhes.ts +++ b/src/runtime/node/ecdhes.ts @@ -36,7 +36,7 @@ export const deriveKey: EcdhESDeriveKeyFunction = async ( let privateKey: KeyObject if (isCryptoKey(privateKee)) { - privateKey = getKeyObject(privateKee, 'ECDH-ES', new Set(['deriveBits', 'deriveKey'])) + privateKey = getKeyObject(privateKee, 'ECDH-ES', 'deriveBits', 'deriveKey') } else if (isKeyObject(privateKee)) { privateKey = privateKee } else { diff --git a/src/runtime/node/encrypt.ts b/src/runtime/node/encrypt.ts index ef15c02bd1..bde6933a72 100644 --- a/src/runtime/node/encrypt.ts +++ b/src/runtime/node/encrypt.ts @@ -75,7 +75,7 @@ const encrypt: EncryptFunction = async ( ) => { let key: KeyObject | Uint8Array if (isCryptoKey(cek)) { - key = getKeyObject(cek, enc, new Set(['encrypt'])) + key = getKeyObject(cek, enc, 'encrypt') } else if (cek instanceof Uint8Array || isKeyObject(cek)) { key = cek } else { diff --git a/src/runtime/node/get_sign_verify_key.ts b/src/runtime/node/get_sign_verify_key.ts index 3de8665f8e..6e229f5e76 100644 --- a/src/runtime/node/get_sign_verify_key.ts +++ b/src/runtime/node/get_sign_verify_key.ts @@ -14,7 +14,7 @@ export default function getSignVerifyKey(alg: string, key: unknown, usage: KeyUs return key } if (isCryptoKey(key)) { - return getKeyObject(key, alg, new Set([usage])) + return getKeyObject(key, alg, usage) } throw new TypeError(invalidKeyInput(key, 'KeyObject', 'CryptoKey', 'Uint8Array')) } diff --git a/src/runtime/node/pbes2kw.ts b/src/runtime/node/pbes2kw.ts index 5abf831f25..b4826ba01b 100644 --- a/src/runtime/node/pbes2kw.ts +++ b/src/runtime/node/pbes2kw.ts @@ -20,7 +20,7 @@ function getPassword(key: unknown, alg: string) { return key } if (isCryptoKey(key)) { - return getKeyObject(key, alg, new Set(['deriveBits', 'deriveKey'])).export() + return getKeyObject(key, alg, 'deriveBits', 'deriveKey').export() } throw new TypeError(invalidKeyInput(key, 'KeyObject', 'CryptoKey', 'Uint8Array')) } diff --git a/src/runtime/node/rsaes.ts b/src/runtime/node/rsaes.ts index 219a0b68bd..075fe38bf8 100644 --- a/src/runtime/node/rsaes.ts +++ b/src/runtime/node/rsaes.ts @@ -46,7 +46,7 @@ function ensureKeyObject(key: unknown, alg: string, ...usages: KeyUsage[]) { return key } if (isCryptoKey(key)) { - return getKeyObject(key, alg, new Set(usages)) + return getKeyObject(key, alg, ...usages) } throw new TypeError(invalidKeyInput(key, 'KeyObject', 'CryptoKey')) } diff --git a/src/runtime/node/webcrypto.ts b/src/runtime/node/webcrypto.ts index 91091658ee..c8916b7f45 100644 --- a/src/runtime/node/webcrypto.ts +++ b/src/runtime/node/webcrypto.ts @@ -26,7 +26,7 @@ function getNamedCurve(alg: string) { } } -export function getKeyObject(key: CryptoKey, alg?: string, usage?: Set) { +export function checkCryptoKey(key: CryptoKey, alg?: string, ...usages: KeyUsage[]) { switch (alg) { case undefined: break @@ -185,8 +185,7 @@ export function getKeyObject(key: CryptoKey, alg?: string, usage?: Set throw new TypeError('CryptoKey does not support this operation') } - if (usage && !key.usages.find(Set.prototype.has.bind(usage))) { - const usages = [...usage] + if (usages.length && !usages.some((expected) => key.usages.includes(expected))) { let msg = 'CryptoKey does not support this operation, its usages must include ' if (usages.length > 2) { const last = usages.pop() @@ -194,11 +193,14 @@ export function getKeyObject(key: CryptoKey, alg?: string, usage?: Set } else if (usages.length === 2) { msg += `one of ${usages[0]} or ${usages[1]}.` } else { - msg += ` ${usages[0]}.` + msg += `${usages[0]}.` } throw new TypeError(msg) } +} +export function getKeyObject(key: CryptoKey, alg?: string, ...usages: KeyUsage[]) { + checkCryptoKey(key, alg, ...usages) return crypto.KeyObject.from(key) }