mirror of
https://github.com/hanzoai/jose.git
synced 2026-09-24 17:00:01 +00:00
146 lines
4.6 KiB
TypeScript
146 lines
4.6 KiB
TypeScript
function unusable(name: string | number, prop = 'algorithm.name') {
|
|
return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`)
|
|
}
|
|
|
|
function isAlgorithm<T = KeyAlgorithm>(algorithm: any, name: string): algorithm is T {
|
|
return algorithm.name === name
|
|
}
|
|
|
|
function getHashLength(hash: KeyAlgorithm) {
|
|
return parseInt(hash.name.slice(4), 10)
|
|
}
|
|
|
|
function getNamedCurve(alg: string) {
|
|
switch (alg) {
|
|
case 'ES256':
|
|
return 'P-256'
|
|
case 'ES384':
|
|
return 'P-384'
|
|
case 'ES512':
|
|
return 'P-521'
|
|
default:
|
|
throw new Error('unreachable')
|
|
}
|
|
}
|
|
|
|
function checkUsage(key: CryptoKey, usages: KeyUsage[]) {
|
|
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)
|
|
}
|
|
}
|
|
|
|
export function checkSigCryptoKey(key: CryptoKey, alg: string, ...usages: KeyUsage[]) {
|
|
switch (alg) {
|
|
case 'HS256':
|
|
case 'HS384':
|
|
case 'HS512': {
|
|
if (!isAlgorithm<HmacKeyAlgorithm>(key.algorithm, 'HMAC')) throw unusable('HMAC')
|
|
const expected = parseInt(alg.slice(2), 10)
|
|
const actual = getHashLength(key.algorithm.hash)
|
|
if (actual !== expected) throw unusable(`SHA-${expected}`, 'algorithm.hash')
|
|
break
|
|
}
|
|
case 'RS256':
|
|
case 'RS384':
|
|
case 'RS512': {
|
|
if (!isAlgorithm<RsaHashedKeyAlgorithm>(key.algorithm, 'RSASSA-PKCS1-v1_5'))
|
|
throw unusable('RSASSA-PKCS1-v1_5')
|
|
const expected = parseInt(alg.slice(2), 10)
|
|
const actual = getHashLength(key.algorithm.hash)
|
|
if (actual !== expected) throw unusable(`SHA-${expected}`, 'algorithm.hash')
|
|
break
|
|
}
|
|
case 'PS256':
|
|
case 'PS384':
|
|
case 'PS512': {
|
|
if (!isAlgorithm<RsaHashedKeyAlgorithm>(key.algorithm, 'RSA-PSS')) throw unusable('RSA-PSS')
|
|
const expected = parseInt(alg.slice(2), 10)
|
|
const actual = getHashLength(key.algorithm.hash)
|
|
if (actual !== expected) throw unusable(`SHA-${expected}`, 'algorithm.hash')
|
|
break
|
|
}
|
|
case 'EdDSA': {
|
|
if (key.algorithm.name !== 'Ed25519' && key.algorithm.name !== 'Ed448') {
|
|
throw unusable('Ed25519 or Ed448')
|
|
}
|
|
break
|
|
}
|
|
case 'ES256':
|
|
case 'ES384':
|
|
case 'ES512': {
|
|
if (!isAlgorithm<EcKeyAlgorithm>(key.algorithm, 'ECDSA')) throw unusable('ECDSA')
|
|
const expected = getNamedCurve(alg)
|
|
const actual = key.algorithm.namedCurve
|
|
if (actual !== expected) throw unusable(expected, 'algorithm.namedCurve')
|
|
break
|
|
}
|
|
default:
|
|
throw new TypeError('CryptoKey does not support this operation')
|
|
}
|
|
|
|
checkUsage(key, usages)
|
|
}
|
|
|
|
export function checkEncCryptoKey(key: CryptoKey, alg: string, ...usages: KeyUsage[]) {
|
|
switch (alg) {
|
|
case 'A128GCM':
|
|
case 'A192GCM':
|
|
case 'A256GCM': {
|
|
if (!isAlgorithm<AesKeyAlgorithm>(key.algorithm, 'AES-GCM')) throw unusable('AES-GCM')
|
|
const expected = parseInt(alg.slice(1, 4), 10)
|
|
const actual = key.algorithm.length
|
|
if (actual !== expected) throw unusable(expected, 'algorithm.length')
|
|
break
|
|
}
|
|
case 'A128KW':
|
|
case 'A192KW':
|
|
case 'A256KW': {
|
|
if (!isAlgorithm<AesKeyAlgorithm>(key.algorithm, 'AES-KW')) throw unusable('AES-KW')
|
|
const expected = parseInt(alg.slice(1, 4), 10)
|
|
const actual = key.algorithm.length
|
|
if (actual !== expected) throw unusable(expected, 'algorithm.length')
|
|
break
|
|
}
|
|
case 'ECDH': {
|
|
switch (key.algorithm.name) {
|
|
case 'ECDH':
|
|
case 'X25519':
|
|
case 'X448':
|
|
break
|
|
default:
|
|
throw unusable('ECDH, X25519, or X448')
|
|
}
|
|
break
|
|
}
|
|
case 'PBES2-HS256+A128KW':
|
|
case 'PBES2-HS384+A192KW':
|
|
case 'PBES2-HS512+A256KW':
|
|
if (!isAlgorithm(key.algorithm, 'PBKDF2')) throw unusable('PBKDF2')
|
|
break
|
|
case 'RSA-OAEP':
|
|
case 'RSA-OAEP-256':
|
|
case 'RSA-OAEP-384':
|
|
case 'RSA-OAEP-512': {
|
|
if (!isAlgorithm<RsaHashedKeyAlgorithm>(key.algorithm, 'RSA-OAEP')) throw unusable('RSA-OAEP')
|
|
const expected = parseInt(alg.slice(9), 10) || 1
|
|
const actual = getHashLength(key.algorithm.hash)
|
|
if (actual !== expected) throw unusable(`SHA-${expected}`, 'algorithm.hash')
|
|
break
|
|
}
|
|
default:
|
|
throw new TypeError('CryptoKey does not support this operation')
|
|
}
|
|
|
|
checkUsage(key, usages)
|
|
}
|