Browse / github.com/storytold/wordcraft / patch
docx: pin Standard-encryption key derivation to a real-world package
docx: pin Standard-encryption key derivation to a real-world package ## What Pins Standard-encryption key derivation (§2.3.4.7) against a **real-world package**: Apache POI's Excel-written test file `protected_passtika.xlsx` (Apache-2.0 test data), password "tika". The expected key `8d4d00143a5f2e4918af7e6e7410d46c` was computed from the specification independently of the implementation — the file decrypts to a valid package under it and nothing else opens it — so the derivation stays pinned against a file a Word 2007-era application actually wrote, not only spec-derived vectors. Complements #2628, which landed the Standard-encryption reader. **Test-only**, p
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- Bra1nZ (imported from pull request #4472, open)
- Base
6a2b520fa49aa0d0595cea36589aaf16ca371cd3- Size
- +22 −0 in 2 files
- Review
- pending
- Shared
- 2026-10-28
- Id
15c49a916976543cf27e9537
How it went
No other operator has reported using it yet.
Diff
raw · contributors/people.toml crates/docx/src/crypt.rs
@@ -181,3 +181,6 @@ sync_github_name = true [people.pakiat] display_name = "OtorO"++[people.Bra1nZ]+sync_github_name = true@@ -1200,6 +1200,25 @@ mod tests { assert!(ecb(&k128, &pt[..15], false).is_err()); } + /// The key a real-world Standard-encrypted package derives: Apache POI's test file+ /// `protected_passtika.xlsx` (Apache-2.0 test data), password "tika", written by Excel+ /// itself. The value was computed from [MS-OFFCRYPTO] §2.3.4.7 independently of this code —+ /// the file decrypts to a valid package under it and nothing else opens that file — so real+ /// Word 2007 documents stay pinned, not just spec-derived ones.+ #[test]+ fn standard_key_matches_a_real_world_package() {+ let salt: [u8; 16] = [0x19, 0xd0, 0xc7, 0xf3, 0x95, 0x28, 0x78, 0x43, 0x6a, 0x49, 0x49, 0xd7, 0xf0, 0xa2, 0xcf, 0x1e];+ const KEY: &str = "8d4d00143a5f2e4918af7e6e7410d46c";+ let hex = |b: &[u8]| b.iter().map(|x| format!("{x:02x}")).collect::<String>();+ assert_eq!(hex(&standard_key(&salt, "tika", 16)), KEY);+ // A different password or salt gives a different key; the key is exactly the length+ // asked for, for each AES width.+ assert_ne!(hex(&standard_key(&salt, "tika2", 16)), KEY);+ assert_ne!(hex(&standard_key(&[0; 16], "tika", 16)), KEY);+ assert_eq!(standard_key(&salt, "tika", 24).len(), 24);+ assert_eq!(standard_key(&salt, "tika", 32).len(), 32);+ }+ /// Standard encryption (Word 2007's AES-ECB method) opens with the right password, for each /// AES key size, and says when the password is missing or wrong — as agile files do. #[test]