From 70bd4ae6b2e6ba94bbe0b3dc1a17b2990af3a18b Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Thu, 3 Sep 2020 18:55:43 +0200 Subject: [PATCH] refactor: encrypt APIs unprotectedHeader and aad arguments swapped BREAKING CHANGE: jose.JWE.Encrypt constructor aad and unprotectedHeader arguments swapped places BREAKING CHANGE: jose.JWE.encrypt.flattened header (unprotectedHeader) and aad arguments swapped places BREAKING CHANGE: jose.JWE.encrypt.general header (unprotectedHeader) and aad arguments swapped places --- docs/README.md | 12 ++++++------ lib/jwe/encrypt.js | 3 +-- lib/jwe/index.js | 5 ++--- ...luding_additional_authentication_data.test.js | 4 ++-- ..._11.protecting_specific_header_fields.test.js | 4 ++-- .../5_12.protecting_content_only.test.js | 4 ++-- ..._13.encrypting_to_multiple_recipients.test.js | 2 +- test/jwe/complete.test.js | 16 ++++++++-------- test/jwe/crit.test.js | 2 +- test/jwe/sanity.test.js | 16 ++++++++-------- types/index.d.ts | 6 +++--- 11 files changed, 36 insertions(+), 38 deletions(-) diff --git a/docs/README.md b/docs/README.md index c714c378cd..e03f96ac15 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1350,11 +1350,11 @@ JWS.verify(general, keystore, { complete: true }) - [Class: <JWE.Encrypt>](#class-jweencrypt) - - [new JWE.Encrypt(cleartext[, protected[, unprotected[, aad]]])](#new-jweencryptcleartext-protected-unprotected-aad) + - [new JWE.Encrypt(cleartext[, protected[, aad[, unprotected]]])](#new-jweencryptcleartext-protected-aad-unprotected) - [encrypt.recipient(key[, header])](#encryptrecipientkey-header) - [encrypt.encrypt(serialization)](#encryptencryptserialization) - [JWE.encrypt(cleartext, key[, protected])](#jweencryptcleartext-key-protected) -- [JWE.encrypt.flattened(cleartext, key[, protected[, unprotected[, aad]]])](#jweencryptflattenedcleartext-key-protected-unprotected-aad) +- [JWE.encrypt.flattened(cleartext, key[, protected[, aad[, unprotected]]])](#jweencryptflattenedcleartext-key-protected-aad-unprotected) - [JWE.decrypt(jwe, keyOrStore[, options])](#jwedecryptjwe-keyorstore-options) @@ -1377,15 +1377,15 @@ General JWE JSON Serialization Syntax. --- -#### `new JWE.Encrypt(cleartext[, protected[, unprotected[, aad]]])` +#### `new JWE.Encrypt(cleartext[, protected[, aad[, unprotected]]])` Creates a new Encrypt object for the provided cleartext with optional Protected and Unprotected Headers and Additional Authenticated Data. - `cleartext`: `` | `` The cleartext that will be encrypted. - `protected`: `` JWE Protected Header -- `unprotected`: `` JWE Shared Unprotected Header - `aad`: `` | `` JWE Additional Authenticated Data +- `unprotected`: `` JWE Shared Unprotected Header - Returns: `` --- @@ -1432,7 +1432,7 @@ Protected Header or inferred from the provided `` instance. --- -#### `JWE.encrypt.flattened(cleartext, key[, protected[, unprotected[, aad]]])` +#### `JWE.encrypt.flattened(cleartext, key[, protected[, aad[, unprotected]]])` Performs the encryption operation and 'flattened' JWE serialization of the result. The Algorithm that will be used to wrap or derive the Content Encryption Key (CEK) is either provided as part of @@ -1443,8 +1443,8 @@ the combined JWE Header or inferred from the provided `` instance. compatible input also works. `` instances are recommended for performance purposes when re-using the same key for every operation. - `protected`: `` JWE Protected Header -- `unprotected`: `` JWE Shared Unprotected Header - `aad`: `` | `` JWE Additional Authenticated Data +- `unprotected`: `` JWE Shared Unprotected Header - Returns: `` --- diff --git a/lib/jwe/encrypt.js b/lib/jwe/encrypt.js index ccd76ae274..9851f392bc 100644 --- a/lib/jwe/encrypt.js +++ b/lib/jwe/encrypt.js @@ -18,8 +18,7 @@ const validateHeaders = require('./validate_headers') const PROCESS_RECIPIENT = Symbol('PROCESS_RECIPIENT') class Encrypt { - // TODO: in v2.x swap unprotectedHeader and aad - constructor (cleartext, protectedHeader, unprotectedHeader, aad) { + constructor (cleartext, protectedHeader, aad, unprotectedHeader) { if (!Buffer.isBuffer(cleartext) && typeof cleartext !== 'string') { throw new TypeError('cleartext argument must be a Buffer or a string') } diff --git a/lib/jwe/index.js b/lib/jwe/index.js index 89d218602d..68bf292967 100644 --- a/lib/jwe/index.js +++ b/lib/jwe/index.js @@ -1,9 +1,8 @@ const Encrypt = require('./encrypt') const decrypt = require('./decrypt') -// TODO: in v2.x swap unprotectedHeader and aad -const single = (serialization, cleartext, key, protectedHeader, unprotectedHeader, aad) => { - return new Encrypt(cleartext, protectedHeader, unprotectedHeader, aad) +const single = (serialization, cleartext, key, protectedHeader, aad, unprotectedHeader) => { + return new Encrypt(cleartext, protectedHeader, aad, unprotectedHeader) .recipient(key) .encrypt(serialization) } diff --git a/test/cookbook/5_10.including_additional_authentication_data.test.js b/test/cookbook/5_10.including_additional_authentication_data.test.js index 6fc17385a0..edf7bfd0a6 100644 --- a/test/cookbook/5_10.including_additional_authentication_data.test.js +++ b/test/cookbook/5_10.including_additional_authentication_data.test.js @@ -20,13 +20,13 @@ const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - flattened encrypt`, t => { - const res = JWE.encrypt.flattened(plaintext, key, prot, undefined, aad) + const res = JWE.encrypt.flattened(plaintext, key, prot, aad) verifiers.flattened(t, res, recipe.output.json_flat) t.deepEqual(JWE.decrypt(res, key), Buffer.from(plaintext)) }) test(`${recipe.title} - general encrypt`, t => { - const res = JWE.encrypt.general(plaintext, key, prot, undefined, aad) + const res = JWE.encrypt.general(plaintext, key, prot, aad) verifiers.general(t, res, recipe.output.json) t.deepEqual(JWE.decrypt(res, key), Buffer.from(plaintext)) }) diff --git a/test/cookbook/5_11.protecting_specific_header_fields.test.js b/test/cookbook/5_11.protecting_specific_header_fields.test.js index 5300e19755..2b8cb8668c 100644 --- a/test/cookbook/5_11.protecting_specific_header_fields.test.js +++ b/test/cookbook/5_11.protecting_specific_header_fields.test.js @@ -20,13 +20,13 @@ const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - flattened encrypt`, t => { - const res = JWE.encrypt.flattened(plaintext, key, prot, unprotected) + const res = JWE.encrypt.flattened(plaintext, key, prot, undefined, unprotected) verifiers.flattened(t, res, recipe.output.json_flat) t.deepEqual(JWE.decrypt(res, key), Buffer.from(plaintext)) }) test(`${recipe.title} - general encrypt`, t => { - const res = JWE.encrypt.general(plaintext, key, prot, unprotected) + const res = JWE.encrypt.general(plaintext, key, prot, undefined, unprotected) verifiers.general(t, res, recipe.output.json) t.deepEqual(JWE.decrypt(res, key), Buffer.from(plaintext)) }) diff --git a/test/cookbook/5_12.protecting_content_only.test.js b/test/cookbook/5_12.protecting_content_only.test.js index 965e63ff1d..8b2f1d9814 100644 --- a/test/cookbook/5_12.protecting_content_only.test.js +++ b/test/cookbook/5_12.protecting_content_only.test.js @@ -20,13 +20,13 @@ const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - flattened encrypt`, t => { - const res = JWE.encrypt.flattened(plaintext, key, undefined, unprotected) + const res = JWE.encrypt.flattened(plaintext, key, undefined, undefined, unprotected) verifiers.flattened(t, res, recipe.output.json_flat) t.deepEqual(JWE.decrypt(res, key), Buffer.from(plaintext)) }) test(`${recipe.title} - general encrypt`, t => { - const res = JWE.encrypt.general(plaintext, key, undefined, unprotected) + const res = JWE.encrypt.general(plaintext, key, undefined, undefined, unprotected) verifiers.general(t, res, recipe.output.json) t.deepEqual(JWE.decrypt(res, key), Buffer.from(plaintext)) }) diff --git a/test/cookbook/5_13.encrypting_to_multiple_recipients.test.js b/test/cookbook/5_13.encrypting_to_multiple_recipients.test.js index 30f93763ca..ceab0c6a0a 100644 --- a/test/cookbook/5_13.encrypting_to_multiple_recipients.test.js +++ b/test/cookbook/5_13.encrypting_to_multiple_recipients.test.js @@ -23,7 +23,7 @@ keys.forEach(({ kty }) => { }) test(`${recipe.title} - general encrypt`, t => { - const jwe = new JWE.Encrypt(plaintext, prot, unprotected) + const jwe = new JWE.Encrypt(plaintext, prot, undefined, unprotected) keys.forEach((key, i) => { jwe.recipient(key, recipients[i].header) }) diff --git a/test/jwe/complete.test.js b/test/jwe/complete.test.js index 72fe1ce4ce..9931245e62 100644 --- a/test/jwe/complete.test.js +++ b/test/jwe/complete.test.js @@ -36,16 +36,16 @@ const complete = (t, jwe, k, ...keys) => { test('compact', complete, () => JWE.encrypt('foo', key), 'cleartext', 'protected', 'key', 'cek') test('flattened', complete, () => JWE.encrypt.flattened('foo', key), 'cleartext', 'protected', 'key', 'cek') -test('flattened w/ aad', complete, () => JWE.encrypt.flattened('foo', key, undefined, undefined, 'bar'), 'cleartext', 'protected', 'aad', 'key', 'cek') -test('flattened w/ unprotected', complete, () => JWE.encrypt.flattened('foo', key, undefined, { foo: 'bar' }), 'cleartext', 'protected', 'unprotected', 'key', 'cek') +test('flattened w/ aad', complete, () => JWE.encrypt.flattened('foo', key, undefined, 'bar'), 'cleartext', 'protected', 'aad', 'key', 'cek') +test('flattened w/ unprotected', complete, () => JWE.encrypt.flattened('foo', key, undefined, undefined, { foo: 'bar' }), 'cleartext', 'protected', 'unprotected', 'key', 'cek') test('flattened w/ header', complete, () => { const enc = new JWE.Encrypt('foo') enc.recipient(key, { foo: 'bar' }) return enc.encrypt('flattened') }, 'cleartext', 'protected', 'header', 'key', 'cek') test('general', complete, () => JWE.encrypt.general('foo', key), 'cleartext', 'protected', 'key', 'cek') -test('general w/ aad', complete, () => JWE.encrypt.general('foo', key, undefined, undefined, 'bar'), 'cleartext', 'protected', 'aad', 'key', 'cek') -test('general w/ unprotected', complete, () => JWE.encrypt.general('foo', key, undefined, { foo: 'bar' }), 'cleartext', 'protected', 'unprotected', 'key', 'cek') +test('general w/ aad', complete, () => JWE.encrypt.general('foo', key, undefined, 'bar'), 'cleartext', 'protected', 'aad', 'key', 'cek') +test('general w/ unprotected', complete, () => JWE.encrypt.general('foo', key, undefined, undefined, { foo: 'bar' }), 'cleartext', 'protected', 'unprotected', 'key', 'cek') test('general w/ header', complete, () => { const enc = new JWE.Encrypt('foo') enc.recipient(key, { foo: 'bar' }) @@ -54,16 +54,16 @@ test('general w/ header', complete, () => { test('with keystore > compact', complete, () => JWE.encrypt('foo', key), ks, 'cleartext', 'protected', 'key', 'cek') test('with keystore > flattened', complete, () => JWE.encrypt.flattened('foo', key), ks, 'cleartext', 'protected', 'key', 'cek') -test('with keystore > flattened w/ aad', complete, () => JWE.encrypt.flattened('foo', key, undefined, undefined, 'bar'), ks, 'cleartext', 'protected', 'aad', 'key', 'cek') -test('with keystore > flattened w/ unprotected', complete, () => JWE.encrypt.flattened('foo', key, undefined, { foo: 'bar' }), ks, 'cleartext', 'protected', 'unprotected', 'key', 'cek') +test('with keystore > flattened w/ aad', complete, () => JWE.encrypt.flattened('foo', key, undefined, 'bar'), ks, 'cleartext', 'protected', 'aad', 'key', 'cek') +test('with keystore > flattened w/ unprotected', complete, () => JWE.encrypt.flattened('foo', key, undefined, undefined, { foo: 'bar' }), ks, 'cleartext', 'protected', 'unprotected', 'key', 'cek') test('with keystore > flattened w/ header', complete, () => { const enc = new JWE.Encrypt('foo') enc.recipient(key, { foo: 'bar' }) return enc.encrypt('flattened') }, ks, 'cleartext', 'protected', 'header', 'key', 'cek') test('with keystore > general', complete, () => JWE.encrypt.general('foo', key), ks, 'cleartext', 'protected', 'key', 'cek') -test('with keystore > general w/ aad', complete, () => JWE.encrypt.general('foo', key, undefined, undefined, 'bar'), ks, 'cleartext', 'protected', 'aad', 'key', 'cek') -test('with keystore > general w/ unprotected', complete, () => JWE.encrypt.general('foo', key, undefined, { foo: 'bar' }), ks, 'cleartext', 'protected', 'unprotected', 'key', 'cek') +test('with keystore > general w/ aad', complete, () => JWE.encrypt.general('foo', key, undefined, 'bar'), ks, 'cleartext', 'protected', 'aad', 'key', 'cek') +test('with keystore > general w/ unprotected', complete, () => JWE.encrypt.general('foo', key, undefined, undefined, { foo: 'bar' }), ks, 'cleartext', 'protected', 'unprotected', 'key', 'cek') test('with keystore > general w/ header', complete, () => { const enc = new JWE.Encrypt('foo') enc.recipient(key, { foo: 'bar' }) diff --git a/test/jwe/crit.test.js b/test/jwe/crit.test.js index 8f91ebaddf..8f167ce82b 100644 --- a/test/jwe/crit.test.js +++ b/test/jwe/crit.test.js @@ -31,7 +31,7 @@ test('crit must be present', t => { test('crit must be integrity protected', t => { const k = generateSync('oct') t.throws(() => { - JWE.encrypt.flattened('foo', k, undefined, { crit: [UNDEFINED] }) + JWE.encrypt.flattened('foo', k, undefined, undefined, { crit: [UNDEFINED] }) }, { instanceOf: errors.JWEInvalid, code: 'ERR_JWE_INVALID', message: '"crit" Header Parameter MUST be integrity protected when present' }) const jws = JWE.encrypt.flattened('foo', k) jws.header = { crit: [UNDEFINED] } diff --git a/test/jwe/sanity.test.js b/test/jwe/sanity.test.js index 6c90504d1e..3409da2152 100644 --- a/test/jwe/sanity.test.js +++ b/test/jwe/sanity.test.js @@ -127,7 +127,7 @@ test('JWE no alg specified (single rsa), with protected header', t => { test('JWE no alg specified (single rsa), with unprotected header', t => { const k = generateSync('RSA') - const encrypt = new JWE.Encrypt('foo', undefined, { enc: 'A256CBC-HS512' }) + const encrypt = new JWE.Encrypt('foo', undefined, undefined, { enc: 'A256CBC-HS512' }) encrypt.recipient(k) const jwe = encrypt.encrypt('flattened') @@ -279,7 +279,7 @@ test('JWE encrypt protectedHeader rejects non objects if provided', t => { test('JWE encrypt unprotectedHeader rejects non objects if provided', t => { ;[[], false, true, null, Infinity, 0, Buffer.from('foo')].forEach((val) => { t.throws(() => { - new JWE.Encrypt('foo', undefined, val) // eslint-disable-line no-new + new JWE.Encrypt('foo', undefined, undefined, val) // eslint-disable-line no-new }, { instanceOf: TypeError, message: 'unprotectedHeader argument must be a plain object when provided' }) }) }) @@ -297,7 +297,7 @@ test('JWE encrypt per-recipient header rejects non objects if provided', t => { test('JWE encrypt aad rejects non buffers and non strings', t => { ;[[], false, true, null, Infinity, 0].forEach((val) => { t.throws(() => { - new JWE.Encrypt('foo', undefined, undefined, val) // eslint-disable-line no-new + new JWE.Encrypt('foo', undefined, val) // eslint-disable-line no-new }, { instanceOf: TypeError, message: 'aad argument must be a Buffer or a string when provided' }) }) }) @@ -332,14 +332,14 @@ test('JWE compact does not support multiple recipients', t => { test('JWE compact does not support unprotected header', t => { const k = generateSync('oct') t.throws(() => { - JWE.encrypt('foo', k, undefined, { foo: 1 }) + JWE.encrypt('foo', k, undefined, undefined, { foo: 1 }) }, { instanceOf: errors.JWEInvalid, code: 'ERR_JWE_INVALID', message: 'JWE Compact Serialization doesn\'t support multiple recipients, JWE unprotected headers or AAD' }) }) test('JWE compact does not support aad', t => { const k = generateSync('oct') t.throws(() => { - JWE.encrypt('foo', k, undefined, undefined, 'aad') + JWE.encrypt('foo', k, undefined, 'aad') }, { instanceOf: errors.JWEInvalid, code: 'ERR_JWE_INVALID', message: 'JWE Compact Serialization doesn\'t support multiple recipients, JWE unprotected headers or AAD' }) }) @@ -417,7 +417,7 @@ test('JWE EC ECDH-ES is only usable with a single recipient', t => { test('JWE prot, unprot and per-recipient headers must be disjoint', t => { const k = generateSync('oct') t.throws(() => { - const encrypt = new JWE.Encrypt('foo', { foo: 1 }, { foo: 2 }) + const encrypt = new JWE.Encrypt('foo', { foo: 1 }, undefined, { foo: 2 }) encrypt.recipient(k) encrypt.encrypt('flattened') }, { instanceOf: errors.JWEInvalid, code: 'ERR_JWE_INVALID', message: 'JWE Shared Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint' }) @@ -427,7 +427,7 @@ test('JWE prot, unprot and per-recipient headers must be disjoint', t => { encrypt.encrypt('flattened') }, { instanceOf: errors.JWEInvalid, code: 'ERR_JWE_INVALID', message: 'JWE Shared Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint' }) t.throws(() => { - const encrypt = new JWE.Encrypt('foo', undefined, { foo: 1 }) + const encrypt = new JWE.Encrypt('foo', undefined, undefined, { foo: 1 }) encrypt.recipient(k, { foo: 2 }) encrypt.encrypt('flattened') }, { instanceOf: errors.JWEInvalid, code: 'ERR_JWE_INVALID', message: 'JWE Shared Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint' }) @@ -508,7 +508,7 @@ test('JWE "zip" must be integrity protected', t => { const k = generateSync('oct') t.throws(() => { - JWE.encrypt.flattened('foo', k, undefined, { zip: 'DEF' }) + JWE.encrypt.flattened('foo', k, undefined, undefined, { zip: 'DEF' }) }, { instanceOf: errors.JWEInvalid, code: 'ERR_JWE_INVALID', message: '"zip" Header Parameter MUST be integrity protected' }) }) diff --git a/types/index.d.ts b/types/index.d.ts index e4839fe295..3c02c9ac5f 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -382,7 +382,7 @@ export namespace JWE { } class Encrypt { - constructor(cleartext: string | Buffer, protected?: object, unprotected?: object, aad?: string); + constructor(cleartext: string | Buffer, protected?: object, aad?: string, unprotected?: object); recipient(key: ProduceKeyInput, header?: object): void; @@ -393,8 +393,8 @@ export namespace JWE { function encrypt(payload: string | Buffer, key: ProduceKeyInput, protected?: object): string; namespace encrypt { - function flattened(payload: string | Buffer, key: ProduceKeyInput, protected?: object, header?: object, aad?: string): FlattenedJWE; - function general(payload: string | Buffer, key: ProduceKeyInput, protected?: object, header?: object, aad?: string): GeneralJWE; + function flattened(payload: string | Buffer, key: ProduceKeyInput, protected?: object, aad?: string, header?: object): FlattenedJWE; + function general(payload: string | Buffer, key: ProduceKeyInput, protected?: object, aad?: string, header?: object): GeneralJWE; } interface DecryptOptions {