PDF Library for Delphi lahko šifrirani PDF odpre z neobdelanim ključem datoteke namesto z geslom. DAOpenFileWithEncryptionKey sprejme ključ kot šestnajstiško besedilo, ga preveri proti preverjevalniku, ki je že shranjen v šifrirnem slovarju, in vrne ročaj Direct Access samo za branje; DAOpenFromStreamWithEncryptionKey naredi enako za TStream, ki je v lasti klicatelja. Obe funkciji sta bili dodani v v3.496.0
Primer je ozek, vendar resničen. Forenzična preiskava vam izroči ključ, obnovljen iz pomnilniške slike, brez gesla. Množično arhiviranje ima deset tisoč dokumentov, katerih ključi datotek so v zbirki za hrambo, ker je izvorni sistem DRM pred leti prenehal izdajati gesla. Pri selitvi z opuščenega izdelka za upravljanje pravic imate ključno gradivo, ničesar drugega pa ne. V vseh teh primerih je poverilnica, ki jo držite, rezultat izpeljave ključa, ne vhod, zato je noben parameter za geslo v API-ju ne more sprejeti
Zakaj ključ šifriranja datoteke ni geslo?
Geslo in ključ šifriranja datoteke sta na nasprotnih straneh izpeljave ključa v standardnem varnostnem obravnavalniku PDF (ISO 32000-1 §7.6.3). Obravnavalnik vzame geslo, ga zmeša z /O, /P, ID-jem datoteke in zgoščevalno funkcijo, odvisno od revizije, ter izdela ključ datoteke. Če ključ datoteke vnesete v mesto za geslo, dobite nesmisel, zgoščen v drugačen nesmisel, zato ta funkcija potrebuje lastno vstopno točko, ne zastavice na DAOpenFile
Mesto, kamor je mogoče vstaviti surovi ključ, določa revizija. Revizije 2 do 4 še vedno izpeljejo ločen ključ na objekt iz ključa datoteke, številke objekta, generacijske številke in pri AESV2 še solnega dodatka AES, zato s ključem datoteke preskoka izpeljave na ravni objekta sploh ni. Revizije 5 do 7 32-bajtni ključ datoteke neposredno uporabijo za AES-256, brez koraka za posamezni objekt. Skupna plast obema je sam ključ datoteke, zato je to edino mesto, kjer PDF Library for Delphi sprejme zunanji ključ. Če geslo še imate, raje ostanite na običajni poti in prepustite življenjskemu ciklu ponavljanja gesla obravnavo prvega napačnega poskusa, saj surovoključna vstopna točka namenoma opusti več udobij, ki jih geselska pot ohrani
Kakšen vhod sprejme DAOpenFileWithEncryptionKey?
Samo šestnajstiške znake ASCII brez predpone in presledkov, v sodem številu, število dekodiranih bajtov pa se mora natančno ujemati z revizijo šifriranja. Pri revizijah 2 do 4 pričakovana dolžina pride iz /Length v šifrirnem slovarju: večkratnik 8 bitov med 5 in 16 bajti, pri manjkajočem /Length pa je privzeta 40-bitna vrednost. Pri revizijah 5 do 7 je dolžina natanko 32 bajtov, brez pogajanja. Predpona 0x, liho število števk, več kot 64 šestnajstiških znakov, neznan bit v Options ali dokument, ki sploh ni šifriran, imajo enak rezultat: ročaj 0 in LastErrorCode, nastavljen na PDFLIB_ERROR_RAW_ENCRYPTION_KEY_INVALID, kar je 425. Strogo preverjanje je namenoma takšno. Ohlapen razčlenjevalnik, ki obreže presledke in kratki vhod dopolni z ničlami, bo skrajšan prilepek iz odložišča z veseljem spremenil v poverilnico, nato pa odpovedal nekje, kjer je razlog precej manj jasen
Var
Lib: TPDFlib;
FileHandle, PageRef: Integer;
Begin
Lib:= TPDFlib.Create;
Try
// KeyHex vsebuje 32 šestnajstiških znakov za AES-128 R4 in 64 za 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;
Preostale kode napak ostanejo ločene, da lahko paketno opravilo razlikuje napako operaterja od težav z dokazi: 411, ko datoteka ne obstaja, 401, ko je ni mogoče odpreti za branje, in 409, ko je struktura navzkrižnih sklicev pokvarjena. Vse, kar je povezano s ključem, se namenoma združi v 425, saj bi bila surovoključna vstopna točka, ki pove, kateri del ključa je bil napačen, oracle
Kaj preverjanje dejansko dokaže?
PDF Library for Delphi dokaže, da podani ključ pripada temu dokumentu, pri čemer uporabi preverjevalnik, ki ga že vsebuje šifrirni slovar, preverjanje pa se razlikuje po revizijah. Revizija 2 znova izračuna šifriranje 32-bajtnega standardnega polnila z RC4 in vseh 32 bajtov primerja z /U. Reviziji 3 in 4 polnilo združita z ID-jem datoteke, izvedeta prehod RC4 ter 19 krogov, izpeljanih z XOR, in primerjata prvih 16 bajtov /U. Revizije 5 do 7 z ničelnim IV dešifrirajo 16-bajtni niz /Perms in hkrati preverijo štiri neodvisne stvari: dovoljenjski besedi little-endian proti /P, štiri bajte FF na položajih 5 do 8, zastavico metapodatkov šifriranja kot T ali F ter oznako adb na položajih 10 do 12
Ko preverjevalnik obstaja, vendar se ne ujema, je odpiranje brezpogojno zavrnjeno. To je vredno povedati naravnost, ker je prav to zagotovilo, na katerem temelji celotna funkcija. Upoštevajte tudi, česa preverjanje ne dokazuje: pove, da ključ dešifrira to datoteko, ne pa, da vam jo je kdo pooblastil uporabljati. Dovoljenjska beseda, pridobljena iz /Perms, je dokaz o ključu, ne dovoljenje, in če želite vedeti, kaj dokument dejansko trdi, da dovoljuje, je to ločeno opravilo za revizijo šifriranja in dovoljenj. Težave z normalizacijo na strani gesla, kot je obravnava ne-ASCII gesel AES-256 s SASLprep, tukaj preprosto ne nastopijo, saj noben niz ne pride do zgoščevanja
Kdaj se uporabi PDF_RAW_KEY_ALLOW_UNVERIFIED?
PDF_RAW_KEY_ALLOW_UNVERIFIED pokriva natanko eno situacijo: dokument nima uporabnega preverjevalnika, ker manjka /Perms, nima 16 bajtov ali pa je /U prekratek za primerjavo. Ne more preglasiti neuspešnega dokaza. Pokvarite eno šestnajstiško števko v /Perms in s nastavljeno možnostjo podajte pravilen ključ, pa PDF Library for Delphi še vedno vrne 0 in 425. Pri neokrnjenem preverjevalniku podajte ključ 32 ničelnih bajtov z nastavljeno možnostjo, pa je odgovor enak. Možnost omili odsotnost dokaza, nikoli pa protislovja z njim. Ker sta obnovitveno odpiranje in preverjeno odpiranje različni spoznavni stanji, se tudi poročata ločeno, namesto da bi se združili v vrnjeni vrednosti, DAGetEncryptionKeyValidation pa sprejme odprti ročaj in odgovori z eno od treh konstant:
PDF_RAW_KEY_VALIDATION_VERIFIED(1) pomeni, da je bil preverjevalnik prisoten in se je ujemalPDF_RAW_KEY_VALIDATION_UNVERIFIED(2) pomeni, da je bil ključ sprejet samo zato, ker ni bilo mogoče oceniti nobenega preverjevalnika, klicatelj pa je to politiko izrecno zahtevalPDF_RAW_KEY_VALIDATION_NONE(0) je vrednost, ki jo sporoči običajen ročaj, odprt z geslom
FileHandle:= Lib.DAOpenFileWithEncryptionKey(CaseFile, KeyHex);
If (FileHandle= 0)And
(Lib.LastErrorCode= PDFLIB_ERROR_RAW_ENCRYPTION_KEY_INVALID) Then
// v tej datoteki ni preverjevalnika? ponovi z izrecno obnovitveno politiko
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;
Samo za branje po konstrukciji in kdo je lastnik toka
Datotečna vstopna točka s surovim ključem vir vedno odpre z fmOpenRead or fmShareDenyWrite in celotno verigo Direct Access označi kot samo za branje, zato DAAppendFile zavrne pisanje na mestu in vrne 2, namesto da bi poskusila izvesti inkrementalno posodobitev. To ni politika, o kateri bi lahko prepričali ročaj; nastavljena je v konstruktorju, še preden je datoteka razčlenjena. Pri delu z dokazi želite lastnost, da so izvorni bajti po zaprtju ročaja bajtno enaki, regresijska zbirka pa to natančno preverja na testnem primeru AES-128 revizije 4 in AES-256 revizije 6. Vstopna točka za tok se pri pisanju vede enako in dodaja še eno pravilo: DAOpenFromStreamWithEncryptionKey nikoli ne prevzame lastništva, zato DACloseFile pusti vaš TStream živ in ga sprostite sami
Source:= TMemoryStream.Create;
Try
Source.LoadFromFile(ArchivePath);
Source.Position:= 0;
FileHandle:= Lib.DAOpenFromStreamWithEncryptionKey(Source, KeyHex);
If FileHandle<> 0 Then
Try
// 2 = ročaj samo za branje: izvozi drugam in nikoli ne dodaj dokazom
Assert(Lib.DAAppendFile(FileHandle)= 2);
Harvest(Lib, FileHandle);
Finally
Lib.DACloseFile(FileHandle);
End;
Finally
Source.Free; // ročaj nikoli ni bil lastnik tega toka
End;
Higiena ključev in kaj izvaža DLL
Zapiranje verige prepiše ključ datoteke, predpomnilnik gesla in izpeljane ključe objektov, dekodirani ključ pa se izbriše v bloku Finally same vstopne točke, ne glede na to, ali je odpiranje uspelo. Za tem se skriva podrobnost, ki je zahtevala precej resničnega razhroščevanja: vsaka kopija, ki mora preživeti, se izrecno klonira s SetLength in Move, ne pa priredi. Če v Delphiju eno AnsiString priredite drugemu, obe imeni pri kopiranju ob pisanju delita isti medpomnilnik, zato bi brisanje na strani klicatelja izničilo ključ, ki ga kriptografski obravnavalnik še uporablja, dokument pa bi se dešifriral v smeti brez razloga, ki bi ga razkril sledilnik sklada. Mejo DLL prečkajo samo datotečne vstopne točke v široki in ANSI obliki skupaj z dostopom do statusa preverjanja; različica TStream ostane samo za Delphi, ker je odvisna od življenjske dobe objektov Delphi in semantike sklicev, ki nimata poštene predstavitve v ravnem ABI-ju C. Če je vaše obnovitveno orodje odjemalec DLL, načrtujte pripravo začasne datoteke in njen izbris pod enakimi kontrolami, kot jih uporabljate za ključ
S šestnajstiškim ključem ravnajte kot z gradivom za poverilnice in uporabite pravila, ki bi jih dali geslu dokumenta, status preverjanja pa shranite v dnevnik verige skrbništva, da bo poznejši bralec lahko ločil preverjen izvoz od nepotrjenega. Če ocenjujete komponento PDF za Delphi za forenziko, množično arhiviranje ali selitev DRM, so vstopne točke s surovim ključem, zagotovilo samo za branje in površina revizije šifriranja deli iste knjižnice, celoten seznam funkcij pa je na strani izdelka PDF Library for Delphi