diff --git a/CHANGELOG.md b/CHANGELOG.md index 34a2fa67..f6acac84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. The format ## Table of Contents - [Unreleased](#unreleased) +- [1.9.25 - 2025-12-09](#1925---2025-12-09) - [1.9.24 - 2025-12-09](#1924---2025-12-09) - [1.9.23 - 2025-12-08](#1923---2025-12-08) - [1.9.22 - 2025-12-05](#1922---2025-12-04) @@ -197,6 +198,16 @@ All notable changes to this project will be documented in this file. The format --- +## [1.9.25] - 2025-12-09 + +### Added +- Documentation disclaimer for our specific AESGCM implementation of padding for additional authenticated data (AAD) and ciphertext. + +### Removed +- Removed support for additional authenticated data (AAD) padding in AESGCM. + +--- + ## [1.9.24] - 2025-12-09 ### Fixed diff --git a/docs/reference/primitives.md b/docs/reference/primitives.md index 1124e97b..ac7e4d3b 100644 --- a/docs/reference/primitives.md +++ b/docs/reference/primitives.md @@ -4958,20 +4958,16 @@ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions]( --- ## Functions -| | -| --- | -| [AES](#function-aes) | -| [AESGCM](#function-aesgcm) | -| [AESGCMDecrypt](#function-aesgcmdecrypt) | -| [assertValidHex](#function-assertvalidhex) | -| [base64ToArray](#function-base64toarray) | -| [ghash](#function-ghash) | -| [normalizeHex](#function-normalizehex) | -| [pbkdf2](#function-pbkdf2) | -| [red](#function-red) | -| [toArray](#function-toarray) | -| [toBase64](#function-tobase64) | -| [verifyNotNull](#function-verifynotnull) | +| | | +| --- | --- | +| [AES](#function-aes) | [pbkdf2](#function-pbkdf2) | +| [AESGCM](#function-aesgcm) | [realHtonl](#function-realhtonl) | +| [AESGCMDecrypt](#function-aesgcmdecrypt) | [red](#function-red) | +| [assertValidHex](#function-assertvalidhex) | [swapBytes32](#function-swapbytes32) | +| [base64ToArray](#function-base64toarray) | [toArray](#function-toarray) | +| [ghash](#function-ghash) | [toBase64](#function-tobase64) | +| [htonl](#function-htonl) | [verifyNotNull](#function-verifynotnull) | +| [normalizeHex](#function-normalizehex) | | Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables) @@ -4988,8 +4984,54 @@ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions]( --- ### Function: AESGCM -```ts -export function AESGCM(plainText: number[], additionalAuthenticatedData: number[], initializationVector: number[], key: number[]): { +SECURITY NOTE – NON-STANDARD AES-GCM PADDING + +This implementation intentionally deviates from NIST SP 800-38D’s AES-GCM +specification in how the GHASH input is formed when the additional +authenticated data (AAD) or ciphertext length is zero. + +In the standard, AAD and ciphertext are each padded with the minimum number +of zero bytes required to reach a multiple of 16 bytes; when the length is +already a multiple of 16 (including the case length = 0), no padding block +is added. In this implementation, when AAD.length === 0 or ciphertext.length +=== 0, an extra 16-byte block of zeros is appended before the length fields +are processed. The same formatting logic is used symmetrically in both +AESGCM (encryption) and AESGCMDecrypt (decryption). + +As a result: + - Authentication tags produced here are NOT compatible with tags produced + by standards-compliant AES-GCM implementations in the cases where AAD + or ciphertext are empty. + - Ciphertexts generated by this code must be decrypted by this exact + implementation (or one that reproduces the same GHASH formatting), and + must not be mixed with ciphertexts produced by a strictly standard + AES-GCM library. + +Cryptographic impact: this change alters only the encoding of the message +that is input to GHASH; it does not change the block cipher, key derivation, +IV handling, or the basic “encrypt-then-MAC over (AAD, ciphertext, lengths)” +structure of AES-GCM. Under the usual assumptions that AES is a secure block +cipher and GHASH with a secret subkey is a secure polynomial MAC, this +variant continues to provide confidentiality and integrity for data encrypted +and decrypted consistently with this implementation. We are not aware of any +attack that exploits the presence of this extra zero block when AAD or +ciphertext are empty. + +However, this padding behavior is non-compliant with NIST SP 800-38D and has +not been analyzed as extensively as standard AES-GCM. Code that requires +strict standards compliance or interoperability with external AES-GCM +implementations SHOULD NOT use this module as-is. Any future migration to a +fully compliant AES-GCM encoding will require a compatibility strategy, as +existing ciphertexts produced by this implementation will otherwise become +undecryptable. + +This non-standard padding behavior is retained intentionally for backward +compatibility: existing ciphertexts in production were generated with this +encoding, and changing it would render previously encrypted data +undecryptable by newer versions of the library. + +```ts +export function AESGCM(plainText: number[], initializationVector: number[], key: number[]): { result: number[]; authenticationTag: number[]; } @@ -5001,7 +5043,7 @@ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions]( ### Function: AESGCMDecrypt ```ts -export function AESGCMDecrypt(cipherText: number[], additionalAuthenticatedData: number[], initializationVector: number[], authenticationTag: number[], key: number[]): number[] | null +export function AESGCMDecrypt(cipherText: number[], initializationVector: number[], authenticationTag: number[], key: number[]): number[] | null ``` Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables) @@ -5033,6 +5075,15 @@ export function ghash(input: number[], hashSubKey: number[]): number[] Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables) +--- +### Function: htonl + +```ts +export function htonl(w: number): number +``` + +Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables) + --- ### Function: normalizeHex @@ -5070,6 +5121,42 @@ Argument Details Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables) +--- +### Function: realHtonl + +Converts a 32-bit unsigned integer from host byte order to network byte order. + +Unlike the legacy `htonl()` implementation (which always swapped bytes), +this function behaves like the traditional C `htonl()`: + +- On **little-endian** machines → performs a byte swap. +- On **big-endian** machines → returns the value unchanged. + +This function is provided to resolve TOB-20, which identified that the +previous `htonl()` implementation had a misleading name and did not match +platform-dependent semantics. + +Example + +```ts +realHtonl(0x11223344) // → 0x44332211 on little-endian systems +``` + +```ts +export function realHtonl(w: number): number +``` + +Returns + +The value converted to network byte order. + +Argument Details + ++ **w** + + A 32-bit unsigned integer. + +Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables) + --- ### Function: red @@ -5079,6 +5166,41 @@ export function red(x: bigint): bigint Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables) +--- +### Function: swapBytes32 + +Unconditionally swaps the byte order of a 32-bit unsigned integer. + +This function performs a strict 32-bit byte swap regardless of host +endianness. It is equivalent to the behavior commonly referred to as +`bswap32` in low-level libraries. + +This function is introduced as part of TOB-20 to provide a clearly-named +alternative to `htonl()`, which was previously implemented as an +unconditional byte swap and did not match the semantics of the traditional +C `htonl()` function. + +Example + +```ts +swapBytes32(0x11223344) // → 0x44332211 +``` + +```ts +export function swapBytes32(w: number): number +``` + +Returns + +The value with its byte order reversed. + +Argument Details + ++ **w** + + A 32-bit unsigned integer. + +Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables) + --- ### Function: toArray diff --git a/package-lock.json b/package-lock.json index 764aba15..459ae56e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bsv/sdk", - "version": "1.9.24", + "version": "1.9.25", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bsv/sdk", - "version": "1.9.24", + "version": "1.9.25", "license": "SEE LICENSE IN LICENSE.txt", "devDependencies": { "@eslint/js": "^9.39.1", diff --git a/package.json b/package.json index 6e9fa4da..9e52d969 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bsv/sdk", - "version": "1.9.24", + "version": "1.9.25", "type": "module", "description": "BSV Blockchain Software Development Kit", "main": "dist/cjs/mod.js", diff --git a/src/primitives/AESGCM.ts b/src/primitives/AESGCM.ts index 894b789a..18098d38 100644 --- a/src/primitives/AESGCM.ts +++ b/src/primitives/AESGCM.ts @@ -323,9 +323,55 @@ function gctr ( return output } +/** + * SECURITY NOTE – NON-STANDARD AES-GCM PADDING + * + * This implementation intentionally deviates from NIST SP 800-38D’s AES-GCM + * specification in how the GHASH input is formed when the additional + * authenticated data (AAD) or ciphertext length is zero. + * + * In the standard, AAD and ciphertext are each padded with the minimum number + * of zero bytes required to reach a multiple of 16 bytes; when the length is + * already a multiple of 16 (including the case length = 0), no padding block + * is added. In this implementation, when AAD.length === 0 or ciphertext.length + * === 0, an extra 16-byte block of zeros is appended before the length fields + * are processed. The same formatting logic is used symmetrically in both + * AESGCM (encryption) and AESGCMDecrypt (decryption). + * + * As a result: + * - Authentication tags produced here are NOT compatible with tags produced + * by standards-compliant AES-GCM implementations in the cases where AAD + * or ciphertext are empty. + * - Ciphertexts generated by this code must be decrypted by this exact + * implementation (or one that reproduces the same GHASH formatting), and + * must not be mixed with ciphertexts produced by a strictly standard + * AES-GCM library. + * + * Cryptographic impact: this change alters only the encoding of the message + * that is input to GHASH; it does not change the block cipher, key derivation, + * IV handling, or the basic “encrypt-then-MAC over (AAD, ciphertext, lengths)” + * structure of AES-GCM. Under the usual assumptions that AES is a secure block + * cipher and GHASH with a secret subkey is a secure polynomial MAC, this + * variant continues to provide confidentiality and integrity for data encrypted + * and decrypted consistently with this implementation. We are not aware of any + * attack that exploits the presence of this extra zero block when AAD or + * ciphertext are empty. + * + * However, this padding behavior is non-compliant with NIST SP 800-38D and has + * not been analyzed as extensively as standard AES-GCM. Code that requires + * strict standards compliance or interoperability with external AES-GCM + * implementations SHOULD NOT use this module as-is. Any future migration to a + * fully compliant AES-GCM encoding will require a compatibility strategy, as + * existing ciphertexts produced by this implementation will otherwise become + * undecryptable. + * + * This non-standard padding behavior is retained intentionally for backward + * compatibility: existing ciphertexts in production were generated with this + * encoding, and changing it would render previously encrypted data + * undecryptable by newer versions of the library. + */ export function AESGCM ( plainText: number[], - additionalAuthenticatedData: number[], initializationVector: number[], key: number[] ): { result: number[], authenticationTag: number[] } { @@ -338,7 +384,7 @@ export function AESGCM ( } let preCounterBlock - let plainTag + let plainTag: number[] = [] const hashSubKey = AES(createZeroBlock(16), key) preCounterBlock = [...initializationVector] if (initializationVector.length === 12) { @@ -358,14 +404,7 @@ export function AESGCM ( const cipherText = gctr(plainText, incrementLeastSignificantThirtyTwoBits(preCounterBlock), key) - plainTag = additionalAuthenticatedData.slice() - - if (additionalAuthenticatedData.length === 0) { - plainTag = plainTag.concat(createZeroBlock(16)) - } else if (additionalAuthenticatedData.length % 16 !== 0) { - plainTag = plainTag.concat(createZeroBlock(16 - (additionalAuthenticatedData.length % 16))) - } - + plainTag = plainTag.concat(createZeroBlock(16)) plainTag = plainTag.concat(cipherText) if (cipherText.length === 0) { @@ -375,7 +414,7 @@ export function AESGCM ( } plainTag = plainTag.concat(createZeroBlock(4)) - .concat(getBytes(additionalAuthenticatedData.length * 8)) + .concat(getBytes(0)) .concat(createZeroBlock(4)).concat(getBytes(cipherText.length * 8)) return { @@ -386,7 +425,6 @@ export function AESGCM ( export function AESGCMDecrypt ( cipherText: number[], - additionalAuthenticatedData: number[], initializationVector: number[], authenticationTag: number[], key: number[] @@ -404,7 +442,7 @@ export function AESGCMDecrypt ( } let preCounterBlock - let compareTag + let compareTag: number[] = [] // Generate the hash subkey const hashSubKey = AES(createZeroBlock(16), key) @@ -425,14 +463,7 @@ export function AESGCMDecrypt ( // Decrypt to obtain the plain text const plainText = gctr(cipherText, incrementLeastSignificantThirtyTwoBits(preCounterBlock), key) - compareTag = additionalAuthenticatedData.slice() - - if (additionalAuthenticatedData.length === 0) { - compareTag = compareTag.concat(createZeroBlock(16)) - } else if (additionalAuthenticatedData.length % 16 !== 0) { - compareTag = compareTag.concat(createZeroBlock(16 - (additionalAuthenticatedData.length % 16))) - } - + compareTag = compareTag.concat(createZeroBlock(16)) compareTag = compareTag.concat(cipherText) if (cipherText.length === 0) { @@ -442,8 +473,9 @@ export function AESGCMDecrypt ( } compareTag = compareTag.concat(createZeroBlock(4)) - .concat(getBytes(additionalAuthenticatedData.length * 8)) - .concat(createZeroBlock(4)).concat(getBytes(cipherText.length * 8)) + .concat(getBytes(0)) + .concat(createZeroBlock(4)) + .concat(getBytes(cipherText.length * 8)) // Generate the authentication tag const calculatedTag = gctr(ghash(compareTag, hashSubKey), preCounterBlock, key) diff --git a/src/primitives/SymmetricKey.ts b/src/primitives/SymmetricKey.ts index feca68fb..e5365748 100644 --- a/src/primitives/SymmetricKey.ts +++ b/src/primitives/SymmetricKey.ts @@ -44,7 +44,7 @@ export default class SymmetricKey extends BigNumber { const iv = Random(32) msg = toArray(msg, enc) const keyBytes = this.toArray('be', 32) - const { result, authenticationTag } = AESGCM(msg, [], iv, keyBytes) + const { result, authenticationTag } = AESGCM(msg, iv, keyBytes) const totalLength = iv.length + result.length + authenticationTag.length const combined = new Array(totalLength) let offset = 0 @@ -89,7 +89,6 @@ export default class SymmetricKey extends BigNumber { const result = AESGCMDecrypt( ciphertext, - [], iv, messageTag, this.toArray('be', 32) diff --git a/src/primitives/__tests/AESGCM.test.ts b/src/primitives/__tests/AESGCM.test.ts index c01acbf6..f5645c43 100644 --- a/src/primitives/__tests/AESGCM.test.ts +++ b/src/primitives/__tests/AESGCM.test.ts @@ -84,7 +84,6 @@ describe('ghash', () => { describe('AESGCM', () => { it('should encrypt: Test Case 1', () => { const output = AESGCM( - [], [], toArray('000000000000000000000000', 'hex'), toArray('00000000000000000000000000000000', 'hex') @@ -99,7 +98,6 @@ describe('AESGCM', () => { it('should encrypt: Test Case 2', () => { const output = AESGCM( toArray('00000000000000000000000000000000', 'hex'), - [], toArray('000000000000000000000000', 'hex'), toArray('00000000000000000000000000000000', 'hex') ) @@ -119,7 +117,6 @@ describe('AESGCM', () => { '809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255', 'hex' ), - [], toArray('cafebabefacedbaddecaf888', 'hex'), toArray('feffe9928665731c6d6a8f9467308308', 'hex') ) @@ -136,84 +133,8 @@ describe('AESGCM', () => { ) }) - it('should encrypt: Test Case 4', () => { - const output = AESGCM( - toArray( - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956' + - '809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'hex' - ), - toArray('feedfacedeadbeeffeedfacedeadbeefabaddad2', 'hex'), - toArray('cafebabefacedbaddecaf888', 'hex'), - toArray('feffe9928665731c6d6a8f9467308308', 'hex') - ) - expect( - toArray( - '42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8' + - 'f6a5aac84aa051ba30b396a0aac973d58e091', - 'hex' - ) - ).toEqual(output.result) - expect(toArray('5bc94fbc3221a5db94fae95ae7121a47', 'hex')).toEqual( - output.authenticationTag - ) - }) - - it('should encrypt: Test Case 5', () => { - const output = AESGCM( - toArray( - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956' + - '809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'hex' - ), - toArray('feedfacedeadbeeffeedfacedeadbeefabaddad2', 'hex'), - toArray('cafebabefacedbad', 'hex'), - toArray('feffe9928665731c6d6a8f9467308308', 'hex') - ) - - expect( - toArray( - '61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b0' + - '97544d4896b424989b5e1ebac0f07c23f4598', - 'hex' - ) - ).toEqual(output.result) - expect(toArray('3612d2e79e3b0785561be14aaca2fccb', 'hex')).toEqual( - output.authenticationTag - ) - }) - - it('should encrypt: Test Case 6', () => { - const output = AESGCM( - toArray( - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956' + - '809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'hex' - ), - toArray('feedfacedeadbeeffeedfacedeadbeefabaddad2', 'hex'), - toArray( - '9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416' + - 'aedbf5a0de6a57a637b39b', - 'hex' - ), - toArray('feffe9928665731c6d6a8f9467308308', 'hex') - ) - - expect( - toArray( - '8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccd' + - 'cb281d48c7c6fd62875d2aca417034c34aee5', - 'hex' - ) - ).toEqual(output.result) - expect(toArray('619cc5aefffe0bfa462af43c1699d050', 'hex')).toEqual( - output.authenticationTag - ) - }) - it('should encrypt: Test Case 7', () => { const output = AESGCM( - [], [], toArray('000000000000000000000000', 'hex'), toArray('000000000000000000000000000000000000000000000000', 'hex') @@ -228,7 +149,6 @@ describe('AESGCM', () => { it('should encrypt: Test Case 8', () => { const output = AESGCM( toArray('00000000000000000000000000000000', 'hex'), - [], toArray('000000000000000000000000', 'hex'), toArray('000000000000000000000000000000000000000000000000', 'hex') ) @@ -248,7 +168,6 @@ describe('AESGCM', () => { '809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255', 'hex' ), - [], toArray('cafebabefacedbaddecaf888', 'hex'), toArray('feffe9928665731c6d6a8f9467308308feffe9928665731c', 'hex') ) @@ -265,85 +184,8 @@ describe('AESGCM', () => { ) }) - it('should encrypt: Test Case 10', () => { - const output = AESGCM( - toArray( - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956' + - '809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'hex' - ), - toArray('feedfacedeadbeeffeedfacedeadbeefabaddad2', 'hex'), - toArray('cafebabefacedbaddecaf888', 'hex'), - toArray('feffe9928665731c6d6a8f9467308308feffe9928665731c', 'hex') - ) - - expect( - toArray( - '3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac6' + - '19d18c84a3f4718e2448b2fe324d9ccda2710', - 'hex' - ) - ).toEqual(output.result) - expect(toArray('2519498e80f1478f37ba55bd6d27618c', 'hex')).toEqual( - output.authenticationTag - ) - }) - - it('should encrypt: Test Case 11', () => { - const output = AESGCM( - toArray( - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956' + - '809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'hex' - ), - toArray('feedfacedeadbeeffeedfacedeadbeefabaddad2', 'hex'), - toArray('cafebabefacedbad', 'hex'), - toArray('feffe9928665731c6d6a8f9467308308feffe9928665731c', 'hex') - ) - - expect( - toArray( - '0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9a471f75c66' + - '541d4d4dad1c9e93a19a58e8b473fa0f062f7', - 'hex' - ) - ).toEqual(output.result) - expect(toArray('65dcc57fcf623a24094fcca40d3533f8', 'hex')).toEqual( - output.authenticationTag - ) - }) - - it('should encrypt: Test Case 12', () => { - const output = AESGCM( - toArray( - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956' + - '809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'hex' - ), - toArray('feedfacedeadbeeffeedfacedeadbeefabaddad2', 'hex'), - toArray( - '9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b5254' + - '16aedbf5a0de6a57a637b39b', - 'hex' - ), - toArray('feffe9928665731c6d6a8f9467308308feffe9928665731c', 'hex') - ) - - expect( - toArray( - 'd27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e4581e79012af34ddd9e2f' + - '037589b292db3e67c036745fa22e7e9b7373b', - 'hex' - ) - ).toEqual(output.result) - expect(toArray('dcf566ff291c25bbb8568fc3d376a6d9', 'hex')).toEqual( - output.authenticationTag - ) - }) - it('should encrypt: Test Case 13', () => { const output = AESGCM( - [], [], toArray('000000000000000000000000', 'hex'), toArray( @@ -361,7 +203,6 @@ describe('AESGCM', () => { it('should encrypt: Test Case 14', () => { const output = AESGCM( toArray('00000000000000000000000000000000', 'hex'), - [], toArray('000000000000000000000000', 'hex'), toArray( '0000000000000000000000000000000000000000000000000000000000000000', @@ -384,7 +225,6 @@ describe('AESGCM', () => { '809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255', 'hex' ), - [], toArray('cafebabefacedbaddecaf888', 'hex'), toArray( 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', @@ -403,91 +243,6 @@ describe('AESGCM', () => { output.authenticationTag ) }) - - it('should encrypt: Test Case 16', () => { - const output = AESGCM( - toArray( - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956' + - '809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'hex' - ), - toArray('feedfacedeadbeeffeedfacedeadbeefabaddad2', 'hex'), - toArray('cafebabefacedbaddecaf888', 'hex'), - toArray( - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', - 'hex' - ) - ) - - expect( - toArray( - '522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b' + - '08b1056828838c5f61e6393ba7a0abcc9f662', - 'hex' - ) - ).toEqual(output.result) - expect(toArray('76fc6ece0f4e1768cddf8853bb2d551b', 'hex')).toEqual( - output.authenticationTag - ) - }) - - it('should encrypt: Test Case 17', () => { - const output = AESGCM( - toArray( - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956' + - '809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'hex' - ), - toArray('feedfacedeadbeeffeedfacedeadbeefabaddad2', 'hex'), - toArray('cafebabefacedbad', 'hex'), - toArray( - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', - 'hex' - ) - ) - - expect( - toArray( - 'c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33934a4f0954' + - 'cc2363bc73f7862ac430e64abe499f47c9b1f', - 'hex' - ) - ).toEqual(output.result) - expect(toArray('3a337dbf46a792c45e454913fe2ea8f2', 'hex')).toEqual( - output.authenticationTag - ) - }) - - it('should encrypt: Test Case 18', () => { - const output = AESGCM( - toArray( - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956' + - '809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'hex' - ), - toArray('feedfacedeadbeeffeedfacedeadbeefabaddad2', 'hex'), - toArray( - '9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416' + - 'aedbf5a0de6a57a637b39b', - 'hex' - ), - toArray( - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', - 'hex' - ) - ) - - expect( - toArray( - '5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da' + - '3ebf1c5d82cdea2418997200ef82e44ae7e3f', - 'hex' - ) - ).toEqual(output.result) - expect(toArray('a44a8266ee1c8eb0c8b5d4cf5ae9f19a', 'hex')).toEqual( - output.authenticationTag - ) - }) }) describe('exclusiveOR', () => { @@ -646,22 +401,21 @@ describe('getBytes', () => { describe('AESGCM IV validation', () => { const key = new Array(16).fill(0x01) - const aad: number[] = [] const plaintext = [1, 2, 3, 4] it('AESGCM throws when IV is empty', () => { expect(() => { - AESGCM(plaintext, aad, [], key) + AESGCM(plaintext, [], key) }).toThrow(new Error('Initialization vector must not be empty')) }) it('AESGCMDecrypt throws when IV is empty', () => { const iv = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - const { result: ciphertext, authenticationTag } = AESGCM(plaintext, aad, iv, key) + const { result: ciphertext, authenticationTag } = AESGCM(plaintext, iv, key) // Now call decrypt but with an empty IV – this should be rejected expect(() => { - AESGCMDecrypt(ciphertext, aad, [], authenticationTag, key) + AESGCMDecrypt(ciphertext, [], authenticationTag, key) }).toThrow(new Error('Initialization vector must not be empty')) }) @@ -669,16 +423,16 @@ describe('AESGCM IV validation', () => { const iv = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] expect(() => { - AESGCM(plaintext, aad, iv, []) + AESGCM(plaintext, iv, []) }).toThrow(new Error('Key must not be empty')) }) it('AESGCMDecrypt throws when key is empty', () => { const iv = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - const { result: ciphertext, authenticationTag } = AESGCM(plaintext, aad, iv, key) + const { result: ciphertext, authenticationTag } = AESGCM(plaintext, iv, key) expect(() => { - AESGCMDecrypt(ciphertext, aad, iv, authenticationTag, []) + AESGCMDecrypt(ciphertext, iv, authenticationTag, []) }).toThrow(new Error('Key must not be empty')) }) @@ -686,14 +440,14 @@ describe('AESGCM IV validation', () => { const iv = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] expect(() => { - AESGCMDecrypt([], aad, iv, [], key) + AESGCMDecrypt([], iv, [], key) }).toThrow(new Error('Cipher text must not be empty')) }) it('AESGCM still work with a valid IV', () => { const iv = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - const { result: ciphertext, authenticationTag } = AESGCM(plaintext, aad, iv, key) - const decrypted = AESGCMDecrypt(ciphertext, aad, iv, authenticationTag, key) + const { result: ciphertext, authenticationTag } = AESGCM(plaintext, iv, key) + const decrypted = AESGCMDecrypt(ciphertext, iv, authenticationTag, key) expect(decrypted).toEqual(plaintext) })