diff --git a/vendor/README.craft-reader.md b/vendor/README.craft-reader.md index a3d174e67..2ce83e9ae 100644 --- a/vendor/README.craft-reader.md +++ b/vendor/README.craft-reader.md @@ -377,3 +377,10 @@ comments label them `Craft patch (flatten)`. | hayro-interpret `color.rs` | `color::InkSpace`, `ColorSpace::ink_space` and `ColorSpace::ink_values`: how a space's colors are kept as inks (gray, CMYK, its colorants, `All`, `None`) and colors converted in batches, process color as `Color::ink_paint` converts it. `ColorSpace::components`; `ColorSpace::is_device_cmyk` and `ColorSpace::spot_alternate` public | | hayro-interpret `types.rs`, `x_object.rs` | `RasterImage::with_inks`: an image's samples in its own space's inks (`InkImage`), `/Decode` and `/Matte` applied, transfer functions left out, with its alpha at the alpha's own size | | hayro-interpret `interpret/mod.rs` | The `sh` operator drops the shading's `/Background`, which only a shading pattern paints (ISO 32000-2 §8.7.4.5.1) | + +Legacy encryption key length, ported from [PdfCraft #1882](https://github.com/storytold/pdfcraft/pull/1882) +(commit 344ba9f8, marked `PdfCraft patch` in `hayro-syntax/src/crypto/mod.rs`): an encryption +dictionary of revision 2–4 whose `/Length` is above 128 bits is refused with `InvalidEncryption`. +Those revisions take the file key from a 16-byte MD5 hash, so `/V 2 /R 3 /Length 256` sliced past +the hash and panicked on open. Test: +`crypto::pdfcraft_password_tests::legacy_key_length_above_128_bits_is_refused_not_a_panic`. diff --git a/vendor/hayro-syntax/SOURCE.json b/vendor/hayro-syntax/SOURCE.json index 75f35b6d4..8de1a73fb 100644 --- a/vendor/hayro-syntax/SOURCE.json +++ b/vendor/hayro-syntax/SOURCE.json @@ -98,7 +98,7 @@ "path": "src/crypto/mod.rs", "source_sha256": "6fca01cee7e67f3aaf435101b7ea98fa4fe23f807eadeaec63ee1c2ece759189", "source_git_blob": "15371514c10dc993b335edfec19a3f989b497954", - "sha256": "6fca01cee7e67f3aaf435101b7ea98fa4fe23f807eadeaec63ee1c2ece759189" + "sha256": "9e0fc0edf48217b01acfcf7bcd3ebe0673f225f0c3857bf887efd0b0f31a74b2" }, { "path": "src/crypto/rc4.rs", diff --git a/vendor/hayro-syntax/src/crypto/mod.rs b/vendor/hayro-syntax/src/crypto/mod.rs index 15371514c..8e8bfb13b 100644 --- a/vendor/hayro-syntax/src/crypto/mod.rs +++ b/vendor/hayro-syntax/src/crypto/mod.rs @@ -145,7 +145,9 @@ pub(crate) fn get( }; let byte_length = length / 8; - if byte_length == 0 { + // PdfCraft patch: revisions 2-4 derive an RC4 or AES-128 key from a 16-byte MD5 hash, so + // a longer /Length is malformed (and would slice past the end of the hash). + if byte_length == 0 || (revision <= 4 && byte_length > 16) { return Err(InvalidEncryption); } @@ -902,4 +904,16 @@ mod pdfcraft_password_tests { assert_eq!(unlock(&user, "cafe\u{301}"), ok, "R6"); assert_eq!(unlock(&user, "cafe"), wrong, "R6"); } + + #[test] + #[cfg(feature = "std")] + fn legacy_key_length_above_128_bits_is_refused_not_a_panic() { + use crate::reader::{Reader, ReaderContext, ReaderExt}; + let hex32 = "00".repeat(32); + for (length, expected) in [(128, DecryptionError::PasswordProtected), (256, InvalidEncryption)] { + let dict = format!("<< /Filter /Standard /V 2 /R 3 /Length {length} /P -4 /O <{hex32}> /U <{hex32}> >>"); + let dict = Reader::new(dict.as_bytes()).read_with_context::>(&ReaderContext::dummy()); + assert_eq!(get(&dict.unwrap(), b"", b"").err(), Some(expected), "/Length {length}"); + } + } }