PDF Library for Delphi dapat membuka PDF terenkripsi dari raw file encryption key, bukan password. DAOpenFileWithEncryptionKey menerima key sebagai teks heksadesimal, memeriksanya terhadap verifier yang sudah tersimpan dalam encryption dictionary, lalu mengembalikan read-only Direct Access handle; DAOpenFromStreamWithEncryptionKey melakukan hal yang sama untuk TStream milik caller. Keduanya hadir mulai v3.496.0
Skenarionya memang sempit, tetapi nyata. Dalam pekerjaan forensik Anda menerima key yang dipulihkan dari memory image tanpa password. Proses archival massal memiliki sepuluh ribu dokumen yang file key-nya disimpan di escrow database karena sistem DRM asal sudah bertahun-tahun berhenti menerbitkan password. Migrasi dari produk rights-management yang sudah dipensiunkan memiliki key material tetapi tidak memiliki hal lain. Dalam semua kasus itu, credential yang Anda pegang adalah output dari key derivation, bukan input, dan tidak ada parameter password di API yang dapat menerimanya
Mengapa file encryption key bukan password?
Password dan file encryption key berada di sisi yang berlawanan dari key derivation dalam PDF standard security handler (ISO 32000-1 §7.6.3). Handler menerima password, mencampurnya dengan /O, /P, file ID, dan hash khusus revision, lalu menghasilkan file key. Masukkan file key ke slot password dan Anda mendapatkan nonsense yang di-hash menjadi nonsense lain, sehingga fitur ini membutuhkan entry point tersendiri, bukan flag pada DAOpenFile
Tempat raw key dapat disuntikkan ditentukan oleh revision. Revision 2 sampai 4 masih menurunkan object key per-object yang berbeda dari file key, object number, generation number, dan untuk AESV2 AES salt, sehingga memiliki file key sama sekali tidak memungkinkan Anda melewati object-level derivation. Revision 5 sampai 7 menggunakan file key 32-byte secara langsung untuk AES-256, tanpa langkah per-object. Satu layer yang sama untuk keduanya adalah file key itu sendiri, dan itulah satu-satunya tempat PDF Library for Delphi menerima key dari luar. Jika password masih tersedia, tetap gunakan jalur biasa dan biarkan password retry lifecycle menangani percobaan pertama yang salah, karena raw-key entry sengaja melepaskan beberapa kemudahan yang dipertahankan jalur password
Input apa yang diterima DAOpenFileWithEncryptionKey?
Hanya ASCII hex tanpa prefix dan tanpa whitespace, dengan panjang genap, dan jumlah byte hasil decode harus tepat sesuai revision encryption. Untuk revision 2 sampai 4, panjang yang diharapkan berasal dari /Length dalam encryption dictionary: kelipatan 8 bit antara 5 dan 16 byte, dengan default 40 bit jika /Length tidak ada. Untuk revision 5 sampai 7 panjangnya tepat 32 byte, tanpa negosiasi. Prefix 0x, jumlah digit ganjil, lebih dari 64 karakter hex, bit tidak dikenal dalam Options, atau dokumen yang sama sekali tidak terenkripsi semuanya menghasilkan hasil yang sama: handle 0 dan LastErrorCode diset ke PDFLIB_ERROR_RAW_ENCRYPTION_KEY_INVALID, yaitu 425. Kekakuan ini memang disengaja. Parser yang longgar dan memangkas whitespace lalu melakukan zero-padding pada input pendek dapat mengubah paste clipboard yang terpotong menjadi credential, kemudian baru gagal di tempat yang jauh lebih sulit dibaca
Var
Lib: TPDFlib;
FileHandle, PageRef: Integer;
Begin
Lib:= TPDFlib.Create;
Try
// KeyHex berisi 32 karakter hex untuk AES-128 R4, dan 64 untuk AES-256 R6
FileHandle:= Lib.DAOpenFileWithEncryptionKey(CaseFile, KeyHex);
If FileHandle= 0 Then
Raise Exception.CreateFmt('raw key refused (error %d)', [Lib.LastErrorCode]);
Try
PageRef:= Lib.DAFindPage(FileHandle, 1);
WriteLn(Lib.DAExtractPageText(FileHandle, PageRef, 0));
Finally
Lib.DACloseFile(FileHandle);
End;
Finally
Lib.Free;
End;
End;
Failure code lain tetap dibedakan agar batch job dapat memisahkan kesalahan operator dari masalah evidence: 411 ketika file tidak ada, 401 ketika file tidak dapat dibuka untuk dibaca, dan 409 ketika struktur cross-reference rusak. Semua hal yang berkaitan dengan key disatukan menjadi 425, dengan sengaja, karena entry point raw-key yang melaporkan bagian mana dari key yang salah akan menjadi oracle
Apa yang sebenarnya dibuktikan oleh verifikasi?
PDF Library for Delphi membuktikan bahwa key yang diberikan memang milik dokumen ini, menggunakan verifier yang sudah dibawa oleh encryption dictionary, dan pemeriksaannya berbeda menurut revision. Revision 2 menghitung ulang RC4 encryption atas 32-byte standard padding string lalu membandingkan semua 32 byte dengan /U. Revision 3 dan 4 melakukan hash padding bersama file ID, menjalankan pass RC4 ditambah 19 putaran berbasis XOR, lalu membandingkan 16 byte pertama dari /U. Revision 5 sampai 7 mendekripsi string /Perms 16-byte dengan IV nol dan sekaligus memeriksa empat hal independen: permission word little-endian terhadap /P, empat byte FF pada posisi 5 sampai 8, flag encrypt-metadata sebagai T atau F, dan marker adb pada posisi 10 sampai 12
Jika verifier ada tetapi tidak cocok, open selalu ditolak. Ini perlu ditegaskan karena merupakan jaminan yang menjadi dasar seluruh fitur. Perhatikan juga apa yang bukan bagian dari verifikasi: verifikasi menyatakan bahwa key dapat mendekripsi file ini, bukan bahwa seseorang memberi Anda wewenang untuk menggunakannya. Permission word yang dipulihkan dari /Perms adalah bukti tentang key, bukan pemberian hak, dan jika Anda ingin mengetahui apa yang sebenarnya diklaim boleh dilakukan dokumen, itu adalah pekerjaan terpisah untuk pass audit encryption dan permissions. Masalah normalisasi sisi password, seperti penanganan SASLprep untuk password AES-256 non-ASCII, tidak muncul di sini karena tidak ada string yang pernah masuk ke hash
Kapan PDF_RAW_KEY_ALLOW_UNVERIFIED berlaku?
PDF_RAW_KEY_ALLOW_UNVERIFIED mencakup tepat satu situasi: dokumen tidak memiliki verifier yang dapat digunakan, karena /Perms hilang atau bukan 16 byte, atau /U terlalu pendek untuk dibandingkan. Opsi ini tidak dapat mengabaikan evidence yang gagal. Ubah satu digit hex di dalam /Perms lalu berikan key yang benar dengan opsi aktif, dan PDF Library for Delphi tetap mengembalikan 0 dan 425. Berikan key berupa 32 byte nol terhadap verifier yang utuh dengan opsi aktif, hasilnya tetap sama. Opsi ini melonggarkan ketiadaan bukti, bukan kontradiksi terhadap bukti. Karena recovery open dan verified open merupakan dua keadaan epistemik yang berbeda, keduanya juga dilaporkan terpisah, bukan dilipat ke dalam return value, dan DAGetEncryptionKeyValidation menerima open handle lalu menjawab dengan salah satu dari tiga constant:
PDF_RAW_KEY_VALIDATION_VERIFIED(1) berarti verifier ada dan cocokPDF_RAW_KEY_VALIDATION_UNVERIFIED(2) berarti key diterima hanya karena tidak ada verifier yang dapat dievaluasi dan caller secara eksplisit meminta policy tersebutPDF_RAW_KEY_VALIDATION_NONE(0) adalah yang dilaporkan handle yang dibuka dengan password biasa
FileHandle:= Lib.DAOpenFileWithEncryptionKey(CaseFile, KeyHex);
If (FileHandle= 0)And
(Lib.LastErrorCode= PDFLIB_ERROR_RAW_ENCRYPTION_KEY_INVALID) Then
// tidak ada verifier di file ini? coba lagi dengan recovery policy eksplisit
FileHandle:= Lib.DAOpenFileWithEncryptionKey(CaseFile, KeyHex,
PDF_RAW_KEY_ALLOW_UNVERIFIED);
If FileHandle<> 0 Then
Begin
Case Lib.DAGetEncryptionKeyValidation(FileHandle) Of
PDF_RAW_KEY_VALIDATION_VERIFIED:
Chain.Note('key verified against the encryption dictionary');
PDF_RAW_KEY_VALIDATION_UNVERIFIED:
Chain.Note('no verifier available: extraction is unattested');
End;
End;
Read-only sejak konstruksi, dan siapa pemilik stream?
Raw-key file entry selalu membuka source dengan fmOpenRead or fmShareDenyWrite dan menandai seluruh Direct Access chain sebagai read-only, sehingga DAAppendFile menolak penulisan in-place dan mengembalikan 2, bukan mencoba incremental update. Ini bukan policy yang dapat dinegosiasikan dengan handle; status tersebut ditetapkan di constructor sebelum file bahkan diparse. Untuk pekerjaan evidence, properti yang Anda inginkan adalah byte source tetap identik setelah handle ditutup, dan regression suite menegaskan hal itu pada fixture AES-128 revision 4 maupun AES-256 revision 6. Stream entry berperilaku sama pada write dan menambahkan satu aturan: DAOpenFromStreamWithEncryptionKey tidak pernah mengambil ownership, sehingga DACloseFile membiarkan TStream Anda tetap hidup dan Anda sendiri yang membebaskannya
Source:= TMemoryStream.Create;
Try
Source.LoadFromFile(ArchivePath);
Source.Position:= 0;
FileHandle:= Lib.DAOpenFromStreamWithEncryptionKey(Source, KeyHex);
If FileHandle<> 0 Then
Try
// 2 = handle read-only: export ke tempat lain, jangan pernah append ke evidence
Assert(Lib.DAAppendFile(FileHandle)= 2);
Harvest(Lib, FileHandle);
Finally
Lib.DACloseFile(FileHandle);
End;
Finally
Source.Free; // handle tidak pernah memiliki stream ini
End;
Kebersihan key dan apa yang diekspor DLL
Menutup chain menimpa file key, password cache, dan derived object key, sedangkan key yang sudah didekode dihapus dalam blok Finally pada entry point itu sendiri, baik open berhasil maupun tidak. Ada detail halus di baliknya yang menghabiskan waktu debugging nyata: setiap salinan yang harus bertahan dibuat secara eksplisit dengan SetLength dan Move, bukan melalui assignment. Assign satu AnsiString ke yang lain di Delphi membuat kedua nama berbagi satu buffer dengan copy-on-write, sehingga wipe pada sisi caller akan men-zero key yang masih digunakan crypt handler dan dokumen akan terdekripsi menjadi garbage tanpa alasan yang dapat dijelaskan stack trace. Hanya entry point berbasis file yang melintasi DLL boundary, dalam bentuk wide dan ANSI, bersama accessor status validation; varian TStream tetap khusus Delphi karena bergantung pada object lifetime dan reference semantics Delphi yang tidak memiliki representasi jujur dalam flat C ABI. Jika recovery tooling Anda adalah DLL client, rencanakan staging ke temporary file dan hapus file itu dengan kontrol yang sama seperti yang diterapkan pada key
Perlakukan hex key sebagai credential material dengan aturan penanganan yang sama seperti password dokumen, dan simpan status validation di log chain of custody agar pembaca berikutnya dapat membedakan extraction terverifikasi dari extraction yang tidak memiliki attestation. Jika Anda sedang mengevaluasi komponen PDF Delphi untuk forensik, archival massal, atau migrasi DRM, raw-key entry point, jaminan read-only, dan permukaan audit encryption semuanya merupakan bagian dari library yang sama, dan daftar fitur lengkap tersedia di halaman produk PDF Library for Delphi