@@ -36,8 +36,8 @@ export class ConcatKDF {
3636}
3737
3838export type JWEHeader = {
39- readonly alg : 'ECDH-ES' | 'PBES2-HS512+A256KW' ,
40- readonly enc : 'A256GCM' | 'A128GCM' ,
39+ readonly alg : 'ECDH-ES' | 'PBES2-HS512+A256KW' | 'A256KW' ,
40+ readonly enc : 'A256GCM' | 'A128GCM' , // A128GCM for testing only, as we use test vectors with 128 bit keys
4141 readonly apu ?: string ,
4242 readonly apv ?: string ,
4343 readonly epk ?: JsonWebKey ,
@@ -97,7 +97,7 @@ export class JWEParser {
9797 * @throws {UnwrapKeyError } if decryption failed (wrong password?)
9898 */
9999 public async decryptPbes2 ( password : string ) : Promise < any > {
100- if ( this . header . alg != 'PBES2-HS512+A256KW' || /* this.header.enc != 'A256GCM' || */ ! this . header . p2s || ! this . header . p2c ) {
100+ if ( this . header . alg != 'PBES2-HS512+A256KW' || this . header . enc != 'A256GCM' || ! this . header . p2s || ! this . header . p2c ) {
101101 throw new Error ( 'unsupported alg or enc' ) ;
102102 }
103103 const saltInput = base64url . parse ( this . header . p2s , { loose : true } ) ;
@@ -110,6 +110,24 @@ export class JWEParser {
110110 }
111111 }
112112
113+ /**
114+ * Decrypts the JWE, assuming alg == A256KW and enc == A256GCM.
115+ * @param kek The key used to wrap the CEK
116+ * @returns Decrypted payload
117+ * @throws {UnwrapKeyError } if decryption failed (wrong kek?)
118+ */
119+ public async decryptA256kw ( kek : CryptoKey ) : Promise < any > {
120+ if ( this . header . alg != 'A256KW' || this . header . enc != 'A256GCM' ) {
121+ throw new Error ( 'unsupported alg or enc' ) ;
122+ }
123+ try {
124+ const cek = crypto . subtle . unwrapKey ( 'raw' , this . encryptedKey , kek , 'AES-KW' , { name : 'AES-GCM' , length : 256 } , false , [ 'decrypt' ] ) ;
125+ return this . decrypt ( await cek ) ;
126+ } catch ( error ) {
127+ throw new UnwrapKeyError ( error ) ;
128+ }
129+ }
130+
113131 private async decrypt ( cek : CryptoKey ) : Promise < any > {
114132 const utf8enc = new TextEncoder ( ) ;
115133 const m = new Uint8Array ( this . ciphertext . length + this . tag . length ) ;
@@ -180,6 +198,22 @@ export class JWEBuilder {
180198 return new JWEBuilder ( header , encryptedKey , cek ) ;
181199 }
182200
201+ /**
202+ * Prepares a new JWE using alg: A256KW and enc: A256GCM.
203+ *
204+ * @param kek The key used to wrap the CEK
205+ * @returns A new JWEBuilder ready to encrypt the payload
206+ */
207+ public static a256kw ( kek : CryptoKey ) : JWEBuilder {
208+ const header = ( async ( ) => < JWEHeader > {
209+ alg : 'A256KW' ,
210+ enc : 'A256GCM'
211+ } ) ( ) ;
212+ const cek = crypto . subtle . generateKey ( { name : 'AES-GCM' , length : 256 } , true , [ 'encrypt' ] ) ;
213+ const encryptedKey = ( async ( ) => new Uint8Array ( await crypto . subtle . wrapKey ( 'raw' , await cek , kek , 'AES-KW' ) ) ) ( ) ;
214+ return new JWEBuilder ( header , encryptedKey , cek ) ;
215+ }
216+
183217 /**
184218 * Builds the JWE.
185219 * @param payload Payload to be encrypted
0 commit comments