mirror of
https://github.com/hanzoai/jose.git
synced 2026-09-24 17:00:01 +00:00
fix: reject failed decompression with JWEInvalid error
This commit is contained in:
@@ -252,7 +252,10 @@ export async function flattenedDecrypt(
|
||||
) {
|
||||
throw new TypeError('maxDecompressedLength must be 0, a positive safe integer, or Infinity')
|
||||
}
|
||||
result.plaintext = await decompress(plaintext, maxDecompressedLength)
|
||||
result.plaintext = await decompress(plaintext, maxDecompressedLength).catch((cause) => {
|
||||
if (cause instanceof JWEInvalid) throw cause
|
||||
throw new JWEInvalid('Failed to decompress plaintext', { cause })
|
||||
})
|
||||
}
|
||||
|
||||
if (jwe.protected !== undefined) {
|
||||
|
||||
@@ -266,7 +266,9 @@ export class FlattenedEncrypt {
|
||||
|
||||
let plaintext = this.#plaintext
|
||||
if (joseHeader.zip === 'DEF') {
|
||||
plaintext = await compress(plaintext)
|
||||
plaintext = await compress(plaintext).catch((cause) => {
|
||||
throw new JWEInvalid('Failed to compress plaintext', { cause })
|
||||
})
|
||||
}
|
||||
|
||||
const { ciphertext, tag, iv } = await encrypt(enc, plaintext, cek, this.#iv, additionalData)
|
||||
|
||||
+4
-4
@@ -14,8 +14,8 @@ export async function compress(input: Uint8Array): Promise<Uint8Array> {
|
||||
|
||||
const cs = new CompressionStream('deflate-raw')
|
||||
const writer = cs.writable.getWriter()
|
||||
writer.write(input as Uint8Array<ArrayBuffer>)
|
||||
writer.close()
|
||||
writer.write(input as Uint8Array<ArrayBuffer>).catch(() => {})
|
||||
writer.close().catch(() => {})
|
||||
|
||||
const chunks: Uint8Array[] = []
|
||||
const reader = cs.readable.getReader()
|
||||
@@ -33,8 +33,8 @@ export async function decompress(input: Uint8Array, maxLength: number): Promise<
|
||||
|
||||
const ds = new DecompressionStream('deflate-raw')
|
||||
const writer = ds.writable.getWriter()
|
||||
writer.write(input as Uint8Array<ArrayBuffer>)
|
||||
writer.close()
|
||||
writer.write(input as Uint8Array<ArrayBuffer>).catch(() => {})
|
||||
writer.close().catch(() => {})
|
||||
|
||||
const chunks: Uint8Array[] = []
|
||||
let length = 0
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import test from 'ava'
|
||||
import * as crypto from 'node:crypto'
|
||||
|
||||
import {
|
||||
FlattenedEncrypt,
|
||||
@@ -130,6 +131,35 @@ if (hasCompressionStreams) {
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
test('invalid deflate data is properly rejected', async (t) => {
|
||||
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 })
|
||||
const cek = crypto.randomBytes(32)
|
||||
const iv = crypto.randomBytes(12)
|
||||
const protectedHeader = { alg: 'RSA-OAEP-256', enc: 'A256GCM', zip: 'DEF' }
|
||||
const protectedB64 = Buffer.from(JSON.stringify(protectedHeader)).toString('base64url')
|
||||
|
||||
const cipher = crypto.createCipheriv('aes-256-gcm', cek, iv, { authTagLength: 16 })
|
||||
cipher.setAAD(Buffer.from(protectedB64))
|
||||
const ciphertext = Buffer.concat([cipher.update(new Uint8Array([0x00])), cipher.final()])
|
||||
const tag = cipher.getAuthTag()
|
||||
const encryptedKey = crypto.publicEncrypt({ key: publicKey, oaepHash: 'sha256' }, cek)
|
||||
|
||||
const flattened = {
|
||||
protected: protectedB64,
|
||||
encrypted_key: Buffer.from(encryptedKey).toString('base64url'),
|
||||
iv: Buffer.from(iv).toString('base64url'),
|
||||
ciphertext: Buffer.from(ciphertext).toString('base64url'),
|
||||
tag: Buffer.from(tag).toString('base64url'),
|
||||
}
|
||||
|
||||
await t.throwsAsync(flattenedDecrypt(flattened, privateKey), {
|
||||
code: 'ERR_JWE_INVALID',
|
||||
message: 'Failed to decompress plaintext',
|
||||
})
|
||||
|
||||
t.pass()
|
||||
})
|
||||
}
|
||||
|
||||
test('unsupported zip value on encrypt throws JOSENotSupported', async (t) => {
|
||||
|
||||
Reference in New Issue
Block a user