Skip to content

Commit

Permalink
feat: support fully specified Ed25519 and Ed448 algorithm identifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Sep 8, 2024
1 parent a28c55f commit 1c8acf1
Show file tree
Hide file tree
Showing 13 changed files with 59 additions and 8 deletions.
14 changes: 8 additions & 6 deletions src/jwks/local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,7 @@ class LocalJWKSet<KeyLikeType extends KeyLike = KeyLike> {
candidate = jwk.key_ops.includes('verify')
}

// filter out non-applicable OKP Sub Types
if (candidate && alg === 'EdDSA') {
candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448'
}

// filter out non-applicable EC curves
// filter out non-applicable curves / sub types
if (candidate) {
switch (alg) {
case 'ES256':
Expand All @@ -121,6 +116,13 @@ class LocalJWKSet<KeyLikeType extends KeyLike = KeyLike> {
case 'ES512':
candidate = jwk.crv === 'P-521'
break
case 'Ed25519':
case 'Ed448':
candidate = jwk.crv === alg
break
case 'EdDSA':
candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448'
break
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/lib/crypto_key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ export function checkSigCryptoKey(key: CryptoKey, alg: string, ...usages: KeyUsa
}
break
}
case 'Ed25519':
case 'Ed448': {
if (!isAlgorithm(key.algorithm, alg)) throw unusable(alg)
break
}
case 'ES256':
case 'ES384':
case 'ES512': {
Expand Down
5 changes: 5 additions & 0 deletions src/runtime/browser/asn1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ const genericImport = async (
keyUsages = isPublic ? [] : ['deriveBits']
break
}
case 'Ed25519':
case 'Ed448':
algorithm = { name: alg }
keyUsages = isPublic ? ['verify'] : ['sign']
break
case 'EdDSA':
algorithm = { name: getNamedCurve(keyData) }
keyUsages = isPublic ? ['verify'] : ['sign']
Expand Down
5 changes: 5 additions & 0 deletions src/runtime/browser/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ export async function generateKeyPair(alg: string, options?: GenerateKeyPairOpti
algorithm = { name: 'ECDSA', namedCurve: 'P-521' }
keyUsages = ['sign', 'verify']
break
case 'Ed25519':
case 'Ed448':
algorithm = { name: alg }
keyUsages = ['sign', 'verify']
break
case 'EdDSA': {
keyUsages = ['sign', 'verify']
const crv = options?.crv ?? 'Ed25519'
Expand Down
5 changes: 5 additions & 0 deletions src/runtime/browser/jwk_to_key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ function subtleMapping(jwk: JWK): {
}
case 'OKP': {
switch (jwk.alg) {
case 'Ed25519':
case 'Ed448':
algorithm = { name: jwk.alg }
keyUsages = jwk.d ? ['sign'] : ['verify']
break
case 'EdDSA':
algorithm = { name: jwk.crv! }
keyUsages = jwk.d ? ['sign'] : ['verify']
Expand Down
3 changes: 3 additions & 0 deletions src/runtime/browser/subtle_dsa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export default function subtleDsa(alg: string, algorithm: KeyAlgorithm | EcKeyAl
case 'ES384':
case 'ES512':
return { hash, name: 'ECDSA', namedCurve: (<EcKeyAlgorithm>algorithm).namedCurve }
case 'Ed25519':
case 'Ed448':
return { name: alg }
case 'EdDSA':
return { name: algorithm.name }
default:
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/node/dsa_digest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export default function dsaDigest(alg: string) {
case 'ES512':
return 'sha512'

case 'Ed25519':
case 'Ed448':
case 'EdDSA':
return undefined

Expand Down
4 changes: 4 additions & 0 deletions src/runtime/node/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ export async function generateKeyPair(alg: string, options?: GenerateKeyPairOpti
return generate('ec', { namedCurve: 'P-384' })
case 'ES512':
return generate('ec', { namedCurve: 'P-521' })
case 'Ed25519':
return generate('ed25519')
case 'Ed448':
return generate('ed448')
case 'EdDSA': {
switch (options?.crv) {
case undefined:
Expand Down
10 changes: 10 additions & 0 deletions src/runtime/node/node_key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ const ecCurveAlgMap = new Map([

export default function keyForCrypto(alg: string, key: KeyObject): KeyObject | SignKeyObjectInput {
switch (alg) {
case 'Ed25519':
case 'Ed448':
if (key.asymmetricKeyType !== alg.toLowerCase()) {
throw new TypeError(
`Invalid key for this operation, its asymmetricKeyType must be ${alg.toLowerCase()}`,
)
}

return key

case 'EdDSA':
if (!['ed25519', 'ed448'].includes(key.asymmetricKeyType!)) {
throw new TypeError(
Expand Down
2 changes: 1 addition & 1 deletion tap/cookbook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default (QUnit: QUnit, lib: typeof jose, keys: typeof jose) => {
if (vector.input.alg === 'ES512') {
return !env.isDeno
}
if (vector.input.alg === 'EdDSA') {
if (vector.input.key?.crv === 'Ed25519') {
return !env.isBlink
}
return true
Expand Down
4 changes: 3 additions & 1 deletion tap/jwk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ export default (QUnit: QUnit, lib: typeof jose, keys: typeof jose) => {
],
['ECDH-ES', KEYS.X448.jwk, env.isNode || env.isEdgeRuntime],
['EdDSA', KEYS.Ed25519.jwk, !env.isBlink],
['Ed25519', KEYS.Ed25519.jwk, !env.isBlink],
['EdDSA', KEYS.Ed448.jwk, env.isNode || env.isEdgeRuntime],
['Ed448', KEYS.Ed448.jwk, env.isNode || env.isEdgeRuntime],
['ES256', KEYS.P256.jwk, true],
['ES256K', KEYS.secp256k1.jwk, lib.cryptoRuntime === 'node:crypto' && !env.isElectron],
['ES384', KEYS.P384.jwk, true],
Expand Down Expand Up @@ -52,7 +54,7 @@ export default (QUnit: QUnit, lib: typeof jose, keys: typeof jose) => {
result = '[not supported] '
}
result += `${alg} `
if (alg === 'EdDSA' || alg === 'ECDH-ES') {
if (alg === 'EdDSA' || alg === 'Ed25519' || alg === 'Ed448' || alg === 'ECDH-ES') {
result += `${jwk.crv} `
}
result += jwk.d ? 'Private' : 'Public'
Expand Down
2 changes: 2 additions & 0 deletions tap/jws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ export default (QUnit: QUnit, lib: typeof jose, keys: typeof jose) => {
type Vector = [string, boolean] | [string, boolean, jose.GenerateKeyPairOptions]
const algorithms: Vector[] = [
['EdDSA', !env.isBlink],
['Ed25519', !env.isBlink],
['EdDSA', env.isNode || env.isEdgeRuntime, { crv: 'Ed448' }],
['Ed448', env.isNode || env.isEdgeRuntime],
['ES256', true],
[
'ES256K',
Expand Down
6 changes: 6 additions & 0 deletions tap/pem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ export default (QUnit: QUnit, lib: typeof jose, keys: typeof jose) => {
[['EdDSA', 'Ed448'], KEYS.Ed448.pkcs8, env.isNode || env.isEdgeRuntime],
[['EdDSA', 'Ed448'], KEYS.Ed448.spki, env.isNode || env.isEdgeRuntime],
[['EdDSA', 'Ed448'], KEYS.Ed448.x509, env.isNode || env.isEdgeRuntime],
[['Ed25519', 'Ed25519'], KEYS.Ed25519.pkcs8, !env.isBlink],
[['Ed25519', 'Ed25519'], KEYS.Ed25519.spki, !env.isBlink],
[['Ed25519', 'Ed25519'], KEYS.Ed25519.x509, !env.isBlink],
[['Ed448', 'Ed448'], KEYS.Ed448.pkcs8, env.isNode || env.isEdgeRuntime],
[['Ed448', 'Ed448'], KEYS.Ed448.spki, env.isNode || env.isEdgeRuntime],
[['Ed448', 'Ed448'], KEYS.Ed448.x509, env.isNode || env.isEdgeRuntime],
]

function title(alg: string, crv: string | undefined, pem: string, works: boolean) {
Expand Down

0 comments on commit 1c8acf1

Please sign in to comment.