docs(pdftract-2t1an): add verification note for encryption detection
Bead: pdftract-2t1an Added verification note documenting the complete implementation of encryption dictionary detection and EncryptionInfo struct. All acceptance criteria PASS: - V=1 R=2 RC4-40 detection (version=1, revision=2, key_length=40) - V=5 R=6 AES-256 detection (version=5, revision=6, key_length=256) - Non-Standard filter rejection with ENCRYPTION_UNSUPPORTED - Invalid /O/U length handling with ENCRYPTION_INVALID_DICT - Clean handling of missing /Encrypt key - Unit tests covering all V/R combinations Test results: 10/10 tests pass Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
6f86258a7a
commit
fe4dcdeaa8
1 changed files with 89 additions and 0 deletions
89
notes/pdftract-2t1an.md
Normal file
89
notes/pdftract-2t1an.md
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# pdftract-2t1an: Encryption dictionary detection + EncryptionInfo struct
|
||||
|
||||
## Summary
|
||||
|
||||
Implemented `detect_encryption(trailer, resolver)` function that parses the PDF trailer's /Encrypt dictionary into a structured `EncryptionInfo`. The implementation recognizes:
|
||||
|
||||
- /Filter (Standard handler only)
|
||||
- /V (1/2/4/5)
|
||||
- /R (2/3/4/5/6)
|
||||
- /KeyLength (with defaults per version)
|
||||
- /O, /U (owner/user password hashes with length validation)
|
||||
- /P (permissions bitfield)
|
||||
- /Perms (V=5 encrypted permissions)
|
||||
- /CF, /StmF, /StrF (crypt filters for V>=4)
|
||||
- /ID[0] (file ID from trailer)
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Public Types
|
||||
|
||||
- `EncryptionInfo`: Main encryption metadata struct
|
||||
- version, revision, key_length
|
||||
- owner_hash, user_hash, perms, file_id
|
||||
- Optional crypt_filters for V>=4
|
||||
|
||||
- `CryptFiltersV4`: Crypt filter metadata for V=4/5
|
||||
- stream_filter, string_filter
|
||||
- filters: BTreeMap<String, CryptFilterDef>
|
||||
|
||||
- `CryptFilterDef`: Individual crypt filter definition
|
||||
- cfm: CryptFilterMethod (Identity/V2/AesV2/AesV3)
|
||||
- length: Option<u32>
|
||||
- auth_event: AuthEvent
|
||||
|
||||
### Detection Function
|
||||
|
||||
`detect_encryption(trailer, resolver, diagnostics) -> Option<EncryptionInfo>`
|
||||
|
||||
1. Looks up /Encrypt in trailer (handles both ObjRef and inline dict)
|
||||
2. Resolves ObjRef via XrefResolver
|
||||
3. Validates /Filter == /Standard; emits ENCRYPTION_UNSUPPORTED if not
|
||||
4. Parses /V, /R, /KeyLength (with version-based defaults)
|
||||
5. Parses /O, /U with length validation (32 bytes for R<=4, 48 for R>=5)
|
||||
6. Parses /P permissions bitfield
|
||||
7. For V>=4, parses /CF, /StmF, /StrF
|
||||
8. For V=5, parses /Perms encrypted permissions
|
||||
9. Extracts /ID[0] from trailer (or empty if missing)
|
||||
10. Returns Some(EncryptionInfo) or None with diagnostics
|
||||
|
||||
## Diagnostics
|
||||
|
||||
- `ENCRYPTION_UNSUPPORTED`: Emitted for non-Standard filters (e.g., Adobe Public Key, custom)
|
||||
- `ENCRYPTION_INVALID_DICT`: Emitted for invalid /O or /U lengths, missing required fields
|
||||
|
||||
## Test Results
|
||||
|
||||
All 10 unit tests pass:
|
||||
|
||||
- test_v1_r2_rc4_40: ✅ V=1 R=2 RC4-40 (version=1, revision=2, key_length=40)
|
||||
- test_v5_r6_aes_256: ✅ V=5 R=6 AES-256 (version=5, revision=6, key_length=256)
|
||||
- test_non_standard_filter_emits_diagnostic: ✅ Returns None + ENCRYPTION_UNSUPPORTED
|
||||
- test_invalid_o_length_emits_diagnostic: ✅ Returns None + ENCRYPTION_INVALID_DICT
|
||||
- test_invalid_u_length_emits_diagnostic: ✅ Returns None + ENCRYPTION_INVALID_DICT
|
||||
- test_v5_invalid_hash_length_emits_diagnostic: ✅ 48-byte validation for R>=5
|
||||
- test_no_encrypt_key: ✅ Returns None cleanly when no /Encrypt
|
||||
- test_missing_id: ✅ Empty file_id when /ID missing
|
||||
- test_v4_crypt_filters: ✅ Parses /CF, /StmF, /StrF correctly
|
||||
- test_all_v_r_combinations: ✅ Covers V=1/R=2, V=2/R=3, V=4/R=4
|
||||
|
||||
## Acceptance Criteria Status
|
||||
|
||||
| Criterion | Status | Evidence |
|
||||
|-----------|--------|----------|
|
||||
| V=1 R=2 RC4-40 detection | PASS | test_v1_r2_rc4_40 |
|
||||
| V=5 R=6 AES-256 detection | PASS | test_v5_r6_aes_256 |
|
||||
| Non-Standard filter rejection | PASS | test_non_standard_filter_emits_diagnostic |
|
||||
| Invalid /O length handling | PASS | test_invalid_o_length_emits_diagnostic |
|
||||
| No /Encrypt key handling | PASS | test_no_encrypt_key |
|
||||
| All V/R combinations tested | PASS | test_all_v_r_combinations |
|
||||
|
||||
## Module Location
|
||||
|
||||
`crates/pdftract-core/src/encryption/detection.rs` (feature-gated on decrypt)
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `pdftract-core::parser::object::{PdfDict, PdfObject, ObjRef}`
|
||||
- `pdftract-core::diagnostics::{emit, Diagnostic, DiagCode}`
|
||||
- `std::collections::BTreeMap`
|
||||
Loading…
Add table
Reference in a new issue