HotXLS čita Agile-šifrirane Excel datoteke, vrstu zaštite lozinkom koju Excel 2010 i sve kasnije verzije primjenjuju prema zadanim postavkama, putem jednog poziva: TXLSXWorkbook.OpenEncrypted. Komponenta analizira XML deskriptor šifriranja, izvodi ključeve iz lozinke s SHA-512 spin-count lancem sažetaka, provjerava lozinku u odnosu na šifrirani verifikator, a zatim dešifrira paket u segmentima AES-CBC od 4096 bajtova. Nije uključena instalacija Excela, nikakav COM niti vanjski kriptografski DLL
Ovaj članak posebno pokriva čitalačku stranu Agile šifriranja. Dva srodna problema imaju svoje članke: interoperabilnost s naslijeđenim RC4 i XOR shemama unutar starih BIFF .xls datoteka pokrivena je u članku o ECB i RC4 interoperabilnosti, a stvaranje lozinkom zaštićenih radnih knjiga s ECMA-376 Standardnim šifriranjem pokriveno je u članku o AES-zaštićenom XLSX izlazu. Ovdje datoteka već postoji, netko drugi ju je šifrirao, a vaš je posao otvoriti je
Scenarij koji nameće ovaj problem poznat je svima koji upravljaju cjevovodom dokumenata. Usluga uvoza na poslužiteljskoj strani prihvaća učitavanje radnih knjiga; na stroju nema Excela niti će ga ikada biti; i jednog jutra kupac učita sasvim običan .xlsx koji čitač ZIP datoteka odbija jer to uopće nije ZIP. Kupac ga je spremio s lozinkom. Od tog trenutka vaš loader ili razumije specifikaciju [MS-OFFCRYPTO] ili vraća datoteku korisniku koji, sa svog stajališta, nije učinio ništa neobično
Što je Agile šifriranje u Excel datoteci?
Agile šifriranje je shema zaštite lozinkom definirana u [MS-OFFCRYPTO] §2.3.4.10 do §2.3.4.15, i to je ono što Excel 2010 i novije verzije zapisuju kad god je radna knjiga spremljena s lozinkom. Šifrirana datoteka više nije ZIP paket. To OLE Compound File Binary (CFB) spremnik koji sadrži dva toka: EncryptionInfo, koji opisuje kako je šifriranje izvedeno, i EncryptedPackage, što je stvarni .xlsx ZIP šifriran kao neprozirni objekt (blob). CFB potpis (D0 CF 11 E0 A1 B1 1A E1) ista je magija koju nose naslijeđene BIFF .xls datoteke, zbog čega se preimenovana ili šifrirana datoteka ne može klasificirati samo po ekstenziji
Ono što razlikuje Agile od njegovih prethodnika jest to što je EncryptionInfo samopisujući. Nakon 8-bajtnog prefiksa verzije, pri čemu su i glavna i sporedna verzija 4, tok je UTF-8 XML deskriptor. Element keyData deklarira šifru (AES), način ulančavanja (ChainingModeCBC), sažetak (SHA512), duljinu ključa u bitovima, veličinu bloka i Base64 sol (salt). Element lozinke keyEncryptor nosi vlastitu sol, spinCount i tri Base64 korisna tereta: encryptedVerifierHashInput, encryptedVerifierHashValue i encryptedKeyValue. Excel zapisuju AES-256 sa spin countom od 100 000, ali deskriptor smije deklarirati AES-128 ili AES-192, i HotXLS poštuje ono što kaže keyBits umjesto da pretpostavlja 256
Jedna ulazna točka za običan tekst, Standard i Agile radne knjige
TXLSXWorkbook.OpenEncrypted rukuje sa sva tri stanja s kojima se pozivatelj može susresti: običan ZIP, Standard-šifrirani i Agile-šifrirani, tako da rukovatelji učitavanjem ne moraju klasificirati datoteke prije nego što ih učitaju. Metoda najprije ispituje datoteku: ako nema CFB potpisa, prepušta učitavanje uobičajenoj stazi Open, a lozinka se jednostavno ignorira. Ako je datoteka CFB spremnik, najprije pokušava s ECMA-376 Standardnim šifriranjem, a kada je potpis verzije EncryptionInfo Agile 4.4, šalje je u Agile cjevovod. Povratna vrijednost je 1 u slučaju uspjeha, što je isti ugovor kao i za Open
var
Wb: TXLSXWorkbook;
begin
Wb := TXLSXWorkbook.Create;
try
// Works for plain .xlsx, Standard-encrypted and
// Agile-encrypted files alike
if Wb.OpenEncrypted('upload.xlsx', 'customer-password') = 1 then
Writeln(VarToWideStr(Wb.Sheets[1].Cells[1, 1].Value));
finally
Wb.Free;
end;
end;
Rezervna opcija (fallback) za nešifrirani unos važnija je nego što izgleda. Serijski uvoznik koji uvijek poziva OpenEncrypted ne treba grananje na mjestu poziva: datoteke koje nikada nisu bile zaštićene učitavaju se točno kao i prije, a datoteke koje stignu šifrirane dešifriraju se na licu mjesta i zatim šalju običnom ZIP loaderu kao tok u memoriji. Postoji samo jedna staza koda za testiranje, a ne tri
Kako lozinka postaje AES ključ?
Agile šifriranje nikada ne koristi lozinku izravno. HotXLS najprije izračunava iterirani sažetak: početni sažetak je SHA-512 preko soli lozinke spojene s UTF-16LE bajtovima lozinke, a zatim se sažetak ponovno sažima spinCount puta, pri čemu se u svakom krugu ispred prethodnog sažetka dodaje 32-bitni little-endian brojač iteracija. Uz Excelov zadani spin count od 100 000, to je stotinu tisuća serijskih poziva SHA-512 po pokušaju lozinke, i u tome je cijela poanta. Spin count je prigušivač grube sile (brute-force throttle): legitimnog pozivatelja košta nekoliko milisekundi jednokratno, a napadača rječnikom košta tih istih nekoliko milisekundi za svaki pojedini pokušaj pogađanja
// [MS-OFFCRYPTO] iterated password hash:
// H(0) = SHA-512(salt + UTF-16LE(password))
// H(n) = SHA-512(LE32(n - 1) + H(n - 1)), repeated spinCount times
function AgilePasswordHash(const Password: WideString;
const Salt: TBytes; SpinCount: Integer): TBytes;
var
buf: TBytes;
i: Integer;
begin
Result := XlsSHA512(Concat(Salt, Utf16LEBytes(Password)));
SetLength(buf, 4 + 64);
for i := 0 to SpinCount - 1 do
begin
PutLE32(buf, 0, i); // iteration counter, little-endian
Move(Result[0], buf[4], 64); // previous digest
Result := XlsSHA512(buf);
end;
end;
Spun hash je i dalje nije ključ. Tri različita ključa izvode se iz njega ponovnim sažimanjem s pridodanom fiksnom 8-bajtnom blok ključ konstantom, jednom po svrsi: FE A7 D2 76 3B 4B 9E 79 za dešifriranje unosa verifikatora, D7 AA 0F 6D 30 61 34 4E za sažetak verifikatora, i 14 6E 0B E7 AB AC D0 D6 za otvaranje samog ključa paketa. Svaki SHA-512 rezultat skraćuje se na deklariranu duljinu ključa, i, prema [MS-OFFCRYPTO], popunjava se bajtovima 0x36 u teoretskom slučaju kada je sažetak kraći od ključa. Isto pravilo popunjavanja s 0x36 primjenjuje se kada se sol lozinke proširi na veličinu bloka za upotrebu kao CBC inicijalizacijski vektor
Provjera lozinke i zamka skraćivanja saltSize
HotXLS provjerava lozinku prije nego što dotakne paket, koristeći verifikacijski par iz deskriptora. Dešifrira encryptedVerifierHashInput s prvim izvedenim ključem, sažima rezultat pomoću SHA-512, dešifrira encryptedVerifierHashValue s drugim izvedenim ključem i uspoređuje dva sažetka bajt po bajt. Nepodudaranje znači da je lozinka pogrešna, što se prijavljuje kao jasan ishod, a ne kao zbrkana radna knjiga. Ono što je kritično, to znači da se tijelo paketa nikada ne dešifrira s lošim ključem, pa ne postoji scenarij u kojem pogrešna lozinka proizvodi naizgled ispravne, a zapravo oštećene podatke
Ovdje postoji detalj specifikacije koji je lako pogrešno shvatiti. [MS-OFFCRYPTO] §2.3.4.13 definira verifikator kao saltSize bajtova nasumičnih podataka, pri čemu je saltSize duljina soli (salta) šifranta ključa, a ne veličina bloka šifre. Budući da je AES-CBC šifrirani tekst poravnat po blokovima, dešifrirani unos verifikatora vraća se popunjen do višekratnika od 16 bajtova i mora se skratiti natrag na saltSize prije sažimanja. Excel uvijek zapisuje saltSize jednak blockSize-u, oboje 16, pa implementacija koja preskače skraćivanje prolazi svaki test protiv stvarnog Excel izlaza, a zatim zakazuje na prvoj datoteci proizvođača koji je odabrao drugu duljinu soli. HotXLS skraćuje na duljinu soli jer je to ono što specifikacija zapravo kaže, a to što se te dvije vrijednosti u praksi slažu je koincidencija, a ne ugovor
Kako se dešifrira EncryptedPackage?
Tok EncryptedPackage započinje s 8-bajtnom little-endian veličinom čistog teksta, nakon čega slijedi šifrirani tekst u segmentima od 4096 bajtova, a HotXLS ga dešifrira segment po segment sa svježim IV-om po segmentu. Sam ključ paketa nije izveden iz lozinke: to je nasumični srednji ključ koji je pisac šifrirao u encryptedKeyValue, a HotXLS ga otvara s trećim izvedenim ključem, skraćujući ga na duljinu ključa koju deklarira keyData. IV svakog segmenta je SHA-512 preko soli keyData spojene s 32-bitnim little-endian indeksom segmenta, skraćen na veličinu bloka. Takva konstrukcija znači da se bilo koji segment od 4096 bajtova može dešifrirati neovisno, što u načelu čini format prikladnim za nasumični pristup, iako HotXLS dešifrira cijeli paket u memoriju i predaje rezultirajuće ZIP bajtove svom uobičajenom XLSX loaderu
Deklarirana veličina čistog teksta obavlja posljednji dio posla. AES-CBC izlaz je poravnat po blokovima, pa posljednji segment nosi do 15 bajtova ispune (padding) koji nisu dio dokumenta; dešifrirani međuspremnik se skraćuje na prefiks veličine, a rezultat je točno .xlsx ZIP koji je Excel šifrirao. HotXLS provjerava valjanost prefiksa u odnosu na stvarnu duljinu toka prije dešifriranja, tako da skraćeni učitani podaci ili izmijenjeno polje veličine zakazuju čisto umjesto da uzrokuju prekoračenje
Prijavljivanje pogrešaka i poštene granice
Granice vrijedi jasno reći. HotXLS čita Agile deskriptore koji deklariraju AES u CBC načinu rada sa SHA-512, što pokriva ono što Excel 2010 do Excel 365 stvarno zapisuju, u sve tri veličine ključa. Deskriptori koji deklariraju druge šifre ili algoritme sažetka odbijaju se umjesto da se pogađaju, a šifranti ključeva temeljeni na certifikatu se ne konzultiraju, već samo šifrant ključa lozinke. Na strani pisanja HotXLS trenutno proizvodi Standardno šifriranje umjesto Agilea, što je razlika koja je važna ako nizvodni alati ispituju shemu; detalji su u članku o pisanju AES-zaštićenog XLSX izlaza
function LoadUploadedWorkbook(const FileName: WideString;
const Password: WideString; Wb: TXLSXWorkbook): Boolean;
begin
Result := False;
try
Result := Wb.OpenEncrypted(FileName, Password) = 1;
except
on E: EXlsxEncryptionNotImplemented do
// Raised for both a wrong password and an unsupported
// scheme; E.Message states which, so log it verbatim and
// only offer a password retry for the wrong-password case
RejectUpload(FileName, E.Message);
end;
end;
Učitavanja zaštićena lozinkom prestaju biti poseban slučaj kada loader tretira šifriranje kao dio formata datoteke, a ne kao iznimku od njega. Ulazna točka OpenEncrypted, izvođenje SHA-512 sa spin-countom i segmentirani cjevovod AES-CBC koji su ovdje opisani dolaze kao dio komponente HotXLS Delphi Excel Component, zajedno s ostatkom njezinog izvornog mehanizma za čitanje i pisanje XLS i XLSX datoteka za Delphi i C++Builder