@@ -2898,6 +2898,178 @@ for (const { name: curveName, rawSize } of edCurves) {
28982898 } ) ;
28992899}
29002900
2901+ // --- JWK validation: per-algorithm alg/crv/use mismatch (gh#1001) ---
2902+
2903+ const RSA_JWK_ALG_CASES : {
2904+ name : RSAKeyPairAlgorithm ;
2905+ hash : HashAlgorithm ;
2906+ expected : string ;
2907+ wrong : string ;
2908+ usages : KeyUsage [ ] ;
2909+ } [ ] = [
2910+ {
2911+ name : 'RSASSA-PKCS1-v1_5' ,
2912+ hash : 'SHA-256' ,
2913+ expected : 'RS256' ,
2914+ wrong : 'RS384' ,
2915+ usages : [ 'verify' ] ,
2916+ } ,
2917+ {
2918+ name : 'RSA-PSS' ,
2919+ hash : 'SHA-256' ,
2920+ expected : 'PS256' ,
2921+ wrong : 'PS384' ,
2922+ usages : [ 'verify' ] ,
2923+ } ,
2924+ {
2925+ name : 'RSA-OAEP' ,
2926+ hash : 'SHA-256' ,
2927+ expected : 'RSA-OAEP-256' ,
2928+ wrong : 'RSA-OAEP-384' ,
2929+ usages : [ 'encrypt' ] ,
2930+ } ,
2931+ ] ;
2932+
2933+ for ( const { name, hash, wrong, usages } of RSA_JWK_ALG_CASES ) {
2934+ test ( SUITE , `${ name } /${ hash } importKey rejects wrong jwk alg` , async ( ) => {
2935+ const keyPair = ( await subtle . generateKey (
2936+ {
2937+ name,
2938+ modulusLength : 2048 ,
2939+ publicExponent : new Uint8Array ( [ 1 , 0 , 1 ] ) ,
2940+ hash,
2941+ } ,
2942+ true ,
2943+ name === 'RSA-OAEP' ? [ 'encrypt' , 'decrypt' ] : [ 'sign' , 'verify' ] ,
2944+ ) ) as CryptoKeyPair ;
2945+ const jwk = ( await subtle . exportKey (
2946+ 'jwk' ,
2947+ keyPair . publicKey as CryptoKey ,
2948+ ) ) as JWK ;
2949+ await assertThrowsAsync (
2950+ async ( ) =>
2951+ await subtle . importKey (
2952+ 'jwk' ,
2953+ { ...jwk , alg : wrong } ,
2954+ { name, hash } ,
2955+ true ,
2956+ usages ,
2957+ ) ,
2958+ 'JWK "alg" does not match the requested algorithm' ,
2959+ ) ;
2960+ } ) ;
2961+ }
2962+
2963+ for ( const { name } of edCurves ) {
2964+ test ( SUITE , `${ name } importKey rejects wrong jwk crv` , async ( ) => {
2965+ const keyPair = ( await subtle . generateKey ( { name } , true , [
2966+ 'sign' ,
2967+ 'verify' ,
2968+ ] ) ) as CryptoKeyPair ;
2969+ const jwk = ( await subtle . exportKey (
2970+ 'jwk' ,
2971+ keyPair . publicKey as CryptoKey ,
2972+ ) ) as JWK ;
2973+ const wrongCrv = name === 'Ed25519' ? 'Ed448' : 'Ed25519' ;
2974+ await assertThrowsAsync (
2975+ async ( ) =>
2976+ await subtle . importKey (
2977+ 'jwk' ,
2978+ { ...jwk , crv : wrongCrv } ,
2979+ { name } ,
2980+ true ,
2981+ [ 'verify' ] ,
2982+ ) ,
2983+ 'JWK "crv" Parameter and algorithm name mismatch' ,
2984+ ) ;
2985+ } ) ;
2986+
2987+ test ( SUITE , `${ name } importKey rejects wrong jwk alg` , async ( ) => {
2988+ const keyPair = ( await subtle . generateKey ( { name } , true , [
2989+ 'sign' ,
2990+ 'verify' ,
2991+ ] ) ) as CryptoKeyPair ;
2992+ const jwk = ( await subtle . exportKey (
2993+ 'jwk' ,
2994+ keyPair . publicKey as CryptoKey ,
2995+ ) ) as JWK ;
2996+ await assertThrowsAsync (
2997+ async ( ) =>
2998+ await subtle . importKey (
2999+ 'jwk' ,
3000+ { ...jwk , alg : 'RS256' } ,
3001+ { name } ,
3002+ true ,
3003+ [ 'verify' ] ,
3004+ ) ,
3005+ 'JWK "alg" does not match the requested algorithm' ,
3006+ ) ;
3007+ } ) ;
3008+
3009+ test ( SUITE , `${ name } importKey accepts jwk alg "EdDSA"` , async ( ) => {
3010+ const keyPair = ( await subtle . generateKey ( { name } , true , [
3011+ 'sign' ,
3012+ 'verify' ,
3013+ ] ) ) as CryptoKeyPair ;
3014+ const jwk = ( await subtle . exportKey (
3015+ 'jwk' ,
3016+ keyPair . publicKey as CryptoKey ,
3017+ ) ) as JWK ;
3018+ const imported = await subtle . importKey (
3019+ 'jwk' ,
3020+ { ...jwk , alg : 'EdDSA' } ,
3021+ { name } ,
3022+ true ,
3023+ [ 'verify' ] ,
3024+ ) ;
3025+ expect ( imported . algorithm . name ) . to . equal ( name ) ;
3026+ } ) ;
3027+
3028+ test ( SUITE , `${ name } importKey rejects wrong jwk kty` , async ( ) => {
3029+ const keyPair = ( await subtle . generateKey ( { name } , true , [
3030+ 'sign' ,
3031+ 'verify' ,
3032+ ] ) ) as CryptoKeyPair ;
3033+ const jwk = ( await subtle . exportKey (
3034+ 'jwk' ,
3035+ keyPair . publicKey as CryptoKey ,
3036+ ) ) as JWK ;
3037+ await assertThrowsAsync (
3038+ async ( ) =>
3039+ await subtle . importKey ( 'jwk' , { ...jwk , kty : 'EC' } , { name } , true , [
3040+ 'verify' ,
3041+ ] ) ,
3042+ 'Invalid JWK "kty" Parameter' ,
3043+ ) ;
3044+ } ) ;
3045+ }
3046+
3047+ const xCurves = [ 'X25519' , 'X448' ] as const ;
3048+ for ( const name of xCurves ) {
3049+ test ( SUITE , `${ name } importKey rejects wrong jwk crv` , async ( ) => {
3050+ const keyPair = ( await subtle . generateKey ( { name } , true , [
3051+ 'deriveKey' ,
3052+ 'deriveBits' ,
3053+ ] ) ) as CryptoKeyPair ;
3054+ const jwk = ( await subtle . exportKey (
3055+ 'jwk' ,
3056+ keyPair . publicKey as CryptoKey ,
3057+ ) ) as JWK ;
3058+ const wrongCrv = name === 'X25519' ? 'X448' : 'X25519' ;
3059+ await assertThrowsAsync (
3060+ async ( ) =>
3061+ await subtle . importKey (
3062+ 'jwk' ,
3063+ { ...jwk , crv : wrongCrv } ,
3064+ { name } ,
3065+ true ,
3066+ [ ] ,
3067+ ) ,
3068+ 'JWK "crv" Parameter and algorithm name mismatch' ,
3069+ ) ;
3070+ } ) ;
3071+ }
3072+
29013073// AES-OCB JWK export/import roundtrip
29023074test ( SUITE , 'AES-OCB export/import jwk' , async ( ) => {
29033075 const key = await subtle . generateKey ( { name : 'AES-OCB' , length : 256 } , true , [
0 commit comments