@@ -112,6 +112,9 @@ export type JWSAlgorithm =
112112 | 'ES512'
113113 | 'PS512'
114114 | 'RS512'
115+ | 'ML-DSA-44'
116+ | 'ML-DSA-65'
117+ | 'ML-DSA-87'
115118 // Deprecated
116119 | 'EdDSA'
117120
@@ -126,6 +129,7 @@ export interface JWK {
126129 readonly crv ?: string
127130 readonly x ?: string
128131 readonly y ?: string
132+ readonly pub ?: string
129133
130134 readonly [ parameter : string ] : JsonValue | undefined
131135}
@@ -1043,6 +1047,44 @@ function OPE(message: string, code?: string, cause?: unknown) {
10431047 return new OperationProcessingError ( message , { code, cause } )
10441048}
10451049
1050+ async function calculateJwkThumbprint ( jwk : JWK ) : Promise < string > {
1051+ let components : JsonObject
1052+ switch ( jwk . kty ) {
1053+ case 'EC' :
1054+ components = {
1055+ crv : jwk . crv ,
1056+ kty : jwk . kty ,
1057+ x : jwk . x ,
1058+ y : jwk . y ,
1059+ }
1060+ break
1061+ case 'OKP' :
1062+ components = {
1063+ crv : jwk . crv ,
1064+ kty : jwk . kty ,
1065+ x : jwk . x ,
1066+ }
1067+ break
1068+ case 'AKP' :
1069+ components = {
1070+ alg : jwk . alg ,
1071+ kty : jwk . kty ,
1072+ pub : jwk . pub ,
1073+ }
1074+ break
1075+ case 'RSA' :
1076+ components = {
1077+ e : jwk . e ,
1078+ kty : jwk . kty ,
1079+ n : jwk . n ,
1080+ }
1081+ break
1082+ default :
1083+ throw new UnsupportedOperationError ( 'unsupported JWK key type' , { cause : jwk } )
1084+ }
1085+ return b64u ( await crypto . subtle . digest ( 'SHA-256' , buf ( JSON . stringify ( components ) ) ) )
1086+ }
1087+
10461088function assertCryptoKey ( key : unknown , it : string ) : asserts key is CryptoKey {
10471089 if ( ! ( key instanceof CryptoKey ) ) {
10481090 throw CodedTypeError ( `${ it } must be a CryptoKey` , ERR_INVALID_ARG_TYPE )
@@ -1591,7 +1633,11 @@ function keyToJws(key: CryptoKey) {
15911633 return rsAlg ( key )
15921634 case 'ECDSA' :
15931635 return esAlg ( key )
1594- case 'Ed25519' : // Fall through
1636+ case 'Ed25519' :
1637+ case 'ML-DSA-44' :
1638+ case 'ML-DSA-65' :
1639+ case 'ML-DSA-87' :
1640+ return key . algorithm . name
15951641 case 'EdDSA' :
15961642 return 'Ed25519'
15971643 default :
@@ -2244,24 +2290,7 @@ class DPoPHandler implements DPoPHandle {
22442290 async calculateThumbprint ( ) {
22452291 if ( ! this . #jkt) {
22462292 const jwk = await crypto . subtle . exportKey ( 'jwk' , this . #publicKey)
2247- let components : JsonValue
2248- switch ( jwk . kty ) {
2249- case 'EC' :
2250- components = { crv : jwk . crv , kty : jwk . kty , x : jwk . x , y : jwk . y }
2251- break
2252- case 'OKP' :
2253- components = { crv : jwk . crv , kty : jwk . kty , x : jwk . x }
2254- break
2255- case 'RSA' :
2256- components = { e : jwk . e , kty : jwk . kty , n : jwk . n }
2257- break
2258- default :
2259- throw new UnsupportedOperationError ( 'unsupported JWK' , { cause : { jwk } } )
2260- }
2261-
2262- this . #jkt ||= b64u (
2263- await crypto . subtle . digest ( { name : 'SHA-256' } , buf ( JSON . stringify ( components ) ) ) ,
2264- )
2293+ this . #jkt ||= await calculateJwkThumbprint ( jwk as JWK )
22652294 }
22662295
22672296 return this . #jkt
@@ -3047,6 +3076,9 @@ async function getPublicSigKeyFromIssuerJwksUri(
30473076 case 'Ed' :
30483077 kty = 'OKP'
30493078 break
3079+ case 'ML' :
3080+ kty = 'AKP'
3081+ break
30503082 default :
30513083 throw new UnsupportedOperationError ( 'unsupported JWS algorithm' , { cause : { alg } } )
30523084 }
@@ -4713,6 +4745,9 @@ function supported(alg: string) {
47134745 case 'RS512' :
47144746 case 'Ed25519' :
47154747 case 'EdDSA' :
4748+ case 'ML-DSA-44' :
4749+ case 'ML-DSA-65' :
4750+ case 'ML-DSA-87' :
47164751 return true
47174752 default :
47184753 return false
@@ -4775,6 +4810,9 @@ function keyToSubtle(key: CryptoKey): AlgorithmIdentifier | RsaPssParams | Ecdsa
47754810 case 'RSASSA-PKCS1-v1_5' :
47764811 checkRsaKeyAlgorithm ( key )
47774812 return key . algorithm . name
4813+ case 'ML-DSA-44' :
4814+ case 'ML-DSA-65' :
4815+ case 'ML-DSA-87' :
47784816 case 'Ed25519' :
47794817 return key . algorithm . name
47804818 }
@@ -4985,8 +5023,13 @@ export async function validateJwtAuthResponse(
49855023 return validateAuthResponse ( as , client , result , expectedState )
49865024}
49875025
5026+ interface CShakeParams {
5027+ name : string
5028+ length : number
5029+ }
5030+
49885031async function idTokenHash ( data : string , header : CompactJWSHeaderParameters , claimName : string ) {
4989- let algorithm : string
5032+ let algorithm : string | CShakeParams
49905033 switch ( header . alg ) {
49915034 case 'RS256' : // Fall through
49925035 case 'PS256' : // Fall through
@@ -5005,6 +5048,11 @@ async function idTokenHash(data: string, header: CompactJWSHeaderParameters, cla
50055048 case 'EdDSA' :
50065049 algorithm = 'SHA-512'
50075050 break
5051+ case 'ML-DSA-44' :
5052+ case 'ML-DSA-65' :
5053+ case 'ML-DSA-87' :
5054+ algorithm = { name : 'cSHAKE256' , length : 512 }
5055+ break
50085056 default :
50095057 throw new UnsupportedOperationError (
50105058 `unsupported JWS algorithm for ${ claimName } calculation` ,
@@ -5563,9 +5611,13 @@ function algToSubtle(alg: string): RsaHashedImportParams | EcKeyImportParams | A
55635611 return { name : 'ECDSA' , namedCurve : `P-${ alg . slice ( - 3 ) } ` }
55645612 case 'ES512' :
55655613 return { name : 'ECDSA' , namedCurve : 'P-521' }
5566- case 'Ed25519' :
55675614 case 'EdDSA' :
55685615 return 'Ed25519'
5616+ case 'Ed25519' :
5617+ case 'ML-DSA-44' :
5618+ case 'ML-DSA-65' :
5619+ case 'ML-DSA-87' :
5620+ return alg
55695621 default :
55705622 throw new UnsupportedOperationError ( 'unsupported JWS algorithm' , { cause : { alg } } )
55715623 }
@@ -5970,34 +6022,7 @@ async function validateDPoP(
59706022 }
59716023
59726024 {
5973- let components : JWK
5974- switch ( proof . header . jwk ! . kty ) {
5975- case 'EC' :
5976- components = {
5977- crv : proof . header . jwk ! . crv ,
5978- kty : proof . header . jwk ! . kty ,
5979- x : proof . header . jwk ! . x ,
5980- y : proof . header . jwk ! . y ,
5981- }
5982- break
5983- case 'OKP' :
5984- components = {
5985- crv : proof . header . jwk ! . crv ,
5986- kty : proof . header . jwk ! . kty ,
5987- x : proof . header . jwk ! . x ,
5988- }
5989- break
5990- case 'RSA' :
5991- components = {
5992- e : proof . header . jwk ! . e ,
5993- kty : proof . header . jwk ! . kty ,
5994- n : proof . header . jwk ! . n ,
5995- }
5996- break
5997- default :
5998- throw new UnsupportedOperationError ( 'unsupported JWK key type' , { cause : proof . header . jwk } )
5999- }
6000- const expected = b64u ( await crypto . subtle . digest ( 'SHA-256' , buf ( JSON . stringify ( components ) ) ) )
6025+ const expected = await calculateJwkThumbprint ( proof . header . jwk ! )
60016026
60026027 if ( accessTokenClaims . cnf . jkt !== expected ) {
60036028 throw OPE ( 'JWT Access Token confirmation mismatch' , JWT_CLAIM_COMPARISON , {
0 commit comments