PDF Library for Delphi gali atverti užšifruotą PDF naudojant ne slaptažodį, o neapdorotą failo šifravimo raktą. DAOpenFileWithEncryptionKey priima raktą kaip šešioliktainį tekstą, patikrina jį pagal šifravimo žodyne jau saugomą tikrintuvą ir grąžina tik skaitymo Direct Access rankeną; DAOpenFromStreamWithEncryptionKey tą patį atlieka su iškvietėjo valdomu TStream. Abu metodai atsirado v3.496.0
Scenarijus siauras, bet tikras. Forensikos užduotis pateikia iš atminties atvaizdo atkurtą raktą, tačiau slaptažodžio nėra. Masinio archyvavimo procese dešimt tūkstančių dokumentų failų raktai saugomi depozito duomenų bazėje, nes pradinė DRM sistema slaptažodžių nebeišduoda jau daugelį metų. Pereinant nuo nebenaudojamo teisių valdymo produkto lieka rakto medžiaga ir nieko daugiau. Visais šiais atvejais turimas kredencialas yra raktų išvedimo rezultatas, o ne įvestis, ir nė vienas API slaptažodžio parametras jo nepriims
Kodėl failo šifravimo raktas nėra slaptažodis?
Slaptažodis ir failo šifravimo raktas PDF standarto saugumo tvarkyklėje (ISO 32000-1 §7.6.3) yra skirtingose raktų išvedimo pusėse. Tvarkyklė paima slaptažodį, sumaišo jį su /O, /P, failo ID ir konkrečiai redakcijai skirtu maišu bei gauna failo raktą. Į slaptažodžio vietą padėjus failo raktą, gaunama beprasmybė, maišoma į kitą beprasmybę, todėl reikalingas atskiras entry point, o ne DAOpenFile vėliava
Vieta, į kurią galima įterpti neapdorotą raktą, priklauso nuo redakcijos. 2–4 redakcijos vis dar iš failo rakto, objekto numerio, generacijos numerio ir AESV2 atveju AES druskos išveda atskirą kiekvieno objekto raktą, todėl failo rakto turėjimas neleidžia išvengti objektų lygio išvedimo. 5–7 redakcijos 32 baitų failo raktą tiesiogiai naudoja AES-256, be objekto lygio etapo. Bendra abiem redakcijoms lieka pats failo raktas, todėl tik į jį PDF Library for Delphi priima išoriškai pateiktą raktą. Jei slaptažodį vis dar turite, rinkitės įprastą kelią ir leiskite slaptažodžio pakartotinių bandymų ciklui tvarkyti pirmą neteisingą bandymą, nes neapdoroto rakto entry point sąmoningai atsisako kelių patogumų, kuriuos išlaiko slaptažodžio kelias
Kokią įvestį priima DAOpenFileWithEncryptionKey?
Tik neperrašytą, be tarpų, lyginio ilgio ASCII šešioliktainį tekstą, o iškoduotų baitų skaičius turi tiksliai atitikti šifravimo redakciją. 2–4 redakcijose tikėtinas ilgis imamas iš /Length šifravimo žodyne: bitų kartotinis, patenkantis tarp 5 ir 16 baitų, o nesant /Length numatoma 40 bitų. 5–7 redakcijose ilgis yra lygiai 32 baitai, be derybų. Prefiksas 0x, nelyginis skaitmenų skaičius, daugiau nei 64 šešioliktainiai simboliai, nežinomas bitas Options arba visai nešifruotas dokumentas duoda tą patį rezultatą: rankena 0 ir LastErrorCode, nustatytas į PDFLIB_ERROR_RAW_ENCRYPTION_KEY_INVALID, t. y. 425. Griežtumas čia svarbus. Atlaidi analizė, kuri nukerpa tarpus ir papildo trumpą įvestį nuliais, be vargo pavers nukirptą iškarpinės kopiją kredencialu, o tada žlugs daug mažiau aiškioje vietoje
Var
Lib: TPDFlib;
FileHandle, PageRef: Integer;
Begin
Lib:= TPDFlib.Create;
Try
// KeyHex yra 32 šešioliktainiai simboliai AES-128 R4 ir 64 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;
Kiti klaidų kodai išlaikomi atskiri, kad paketinis darbas galėtų atskirti operatoriaus klaidą nuo įrodymų problemos: 411, kai failo nėra, 401, kai jo negalima atverti skaitymui, 409, kai sugadinta kryžminių nuorodų struktūra. Viskas, kas susiję su raktu, tyčia sutraukiama į 425, nes neapdoroto rakto entry point, pranešantis, kuri rakto dalis buvo neteisinga, taptų oracle
Ką iš tikrųjų įrodo tikrinimas?
PDF Library for Delphi įrodo, kad pateiktas raktas priklauso šiam dokumentui, naudodama tikrintuvą, kurį jau turi šifravimo žodynas, o patikra skiriasi pagal redakciją. 2 redakcija perskaičiuoja RC4 šifravimą 32 baitų standartinio užpildo eilutei ir visus 32 baitus palygina su /U. 3 ir 4 redakcijos maišo užpildą su failo ID, vykdo RC4 etapą ir 19 XOR pagrįstų raundų bei palygina pirmus 16 /U baitų. 5–7 redakcijos iššifruoja 16 baitų /Perms eilutę su nuliniu IV ir vienu metu tikrina keturis nepriklausomus dalykus: little-endian leidimų žodį pagal /P, keturis FF baitus 5–8 pozicijose, metaduomenų šifravimo vėliavą kaip T arba F ir adb žymę 10–12 pozicijose
Kai tikrintuvas yra, bet nesutampa, atvėrimas visada atmetamas. Tai verta pasakyti aiškiai, nes nuo šios garantijos priklauso visa funkcija. Taip pat svarbu, ko tikrinimas neįrodo: jis sako, kad raktu galima iššifruoti šį failą, bet ne tai, kad kas nors suteikė jums teisę jį naudoti. Iš /Perms atkurtas leidimų žodis yra rakto įrodymas, o ne leidimas, ir jei norite žinoti, ką dokumentas iš tikrųjų teigia leidžiantis, tai atskira užduotis, kurią atlieka šifravimo ir leidimų audito etapas. Slaptažodžio pusės normalizavimo problemos, pavyzdžiui, SASLprep tvarkymas ne ASCII AES-256 slaptažodžiams, čia paprasčiausiai neatsiranda, nes jokia eilutė nepasiekia maišo
Kada taikoma PDF_RAW_KEY_ALLOW_UNVERIFIED?
PDF_RAW_KEY_ALLOW_UNVERIFIED apima tik vieną situaciją: dokumente nėra tinkamo tikrintuvo, nes /Perms trūksta arba jis nėra 16 baitų, arba /U per trumpas palyginimui. Ji negali panaikinti nesutampančių įrodymų. Sugadinkite vieną šešioliktainį /Perms skaitmenį ir su nustatyta parinktimi pateikite teisingą raktą – PDF Library for Delphi vis tiek grąžins 0 ir 425. Pateikite 32 nulinių baitų raktą prieš nepažeistą tikrintuvą su nustatyta parinktimi – atsakymas bus toks pats. Parinktis sušvelnina įrodymo nebuvimą, bet niekada neignoruoja prieštaravimo. Kadangi atkūrimo atvėrimas ir patikrintas atvėrimas yra skirtingos pažinimo būsenos, jos taip pat pranešamos atskirai, o DAGetEncryptionKeyValidation iš atvertos rankenos grąžina vieną iš trijų konstantų:
PDF_RAW_KEY_VALIDATION_VERIFIED(1) reiškia, kad tikrintuvas buvo ir sutapoPDF_RAW_KEY_VALIDATION_UNVERIFIED(2) reiškia, kad raktas priimtas tik todėl, jog tikrintuvo nebuvo galima įvertinti, o iškvietėjas aiškiai paprašė šios politikosPDF_RAW_KEY_VALIDATION_NONE(0) gaunama iš įprastai slaptažodžiu atvertos rankenos
FileHandle:= Lib.DAOpenFileWithEncryptionKey(CaseFile, KeyHex);
If (FileHandle= 0)And
(Lib.LastErrorCode= PDFLIB_ERROR_RAW_ENCRYPTION_KEY_INVALID) Then
// šiame faile nėra tikrintuvo? kartokite pagal aiškią atkūrimo politiką
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;
Tik skaitymui pagal konstrukciją ir kam priklauso srautas
Neapdoroto rakto failo entry point visada atveria šaltinį su fmOpenRead or fmShareDenyWrite ir visą Direct Access grandinę pažymi tik skaitymui, todėl DAAppendFile atsisako rašyti vietoje ir grąžina 2, užuot bandęs atlikti prieauginį atnaujinimą. Tai ne politika, nuo kurios rankeną galima įkalbėti atsitraukti; ji nustatoma konstruktoriuje dar prieš analizuojant failą. Dirbant su įrodymais svarbu, kad šaltinio baitai po rankenos uždarymo būtų identiški, ir regresijos rinkinys tai tiksliai tikrina tiek AES-128 4 redakcijos, tiek AES-256 6 redakcijos faile. Srauto entry point rašymo atžvilgiu elgiasi taip pat ir prideda dar vieną taisyklę: DAOpenFromStreamWithEncryptionKey niekada neperima nuosavybės, todėl DACloseFile palieka jūsų TStream gyvą, o atlaisvinti jį turite patys
Source:= TMemoryStream.Create;
Try
Source.LoadFromFile(ArchivePath);
Source.Position:= 0;
FileHandle:= Lib.DAOpenFromStreamWithEncryptionKey(Source, KeyHex);
If FileHandle<> 0 Then
Try
// 2 = tik skaitymo rankena: eksportuokite kitur, niekada nepridėkite prie įrodymų
Assert(Lib.DAAppendFile(FileHandle)= 2);
Harvest(Lib, FileHandle);
Finally
Lib.DACloseFile(FileHandle);
End;
Finally
Source.Free; // rankena niekada nebuvo šio srauto savininkė
End;
Rakto higiena ir ką eksportuoja DLL
Uždarius grandinę perrašomas failo raktas, slaptažodžių podėlis ir išvesti objektų raktai, o iškoduotas raktas nušluojamas pačiame entry point Finally bloke, nesvarbu, ar atvėrimas pavyko. Už to slypi subtilumas, pareikalavęs tikro derinimo laiko: bet kuri išlikti turinti kopija aiškiai klonuojama su SetLength ir Move, o ne priskiriama. Delphi vieną AnsiString priskyrus kitam abu vardai dalijasi vienu buferiu dėl copy-on-write, todėl nušluosčius iškvietėjo pusę būtų nunulintas raktas, kurį šifravimo tvarkyklė dar naudoja, ir dokumentas būtų iššifruotas į šiukšles be jokio paaiškinamo stack trace. Per DLL ribą eina tik failo entry point, ANSI ir wide formos bei tikrinimo būsenos prieigos metodas; TStream variantas lieka tik Delphi pusėje, nes priklauso nuo Delphi objektų gyvavimo trukmės ir nuorodų semantikos, neturinčios sąžiningo atvaizdavimo plokščioje C ABI. Jei jūsų atkūrimo įrankis yra DLL klientas, planuokite tarpinį failą ir jo ištrynimą pagal tas pačias kontrolės taisykles, kurias taikote raktui
Laikykite šešioliktainį raktą kredencialo medžiaga ir tvarkykite jį taip, kaip tvarkytumėte dokumento slaptažodį, o tikrinimo būseną įrašykite į grandinės, kuri fiksuoja įrodymų kilmę, žurnalą, kad vėliau būtų galima atskirti patikrintą išgavimą nuo nepatvirtinto. Jei vertinate Delphi PDF komponentą forensikai, masiniam archyvavimui ar DRM migracijai, neapdoroto rakto entry point, tik skaitymo garantija ir šifravimo audito paviršius priklauso tai pačiai bibliotekai, o visą funkcijų sąrašą rasite PDF Library for Delphi produkto puslapyje