Napi?ete radnu knjigu, ?ifrirate je lozinkom, predate datoteku kolegi, i kolega je otvori u Excelu. Excel tra?i lozinku. Kolega je utipka, i Excel je prihvati. Za sada ?ifriranje izgleda to?no. Zatim Excel postavi dijalo?ki okvir koji ka?e da je datoteka o?te?ena i da se ne mo?e otvoriti, ili se otvori uz list besmislenih ?elija. Lozinka je bila to?na. Datoteka je ipak pokvarena. Ovo je najvi?e zbunjuju?i na?in zakazivanja u ?ifriranju Office-a, jer su dio koji vam govori da je lozinka to?na i dio koji dr?i va?e podatke za?ti?eni dvjema razli?itim operacijama, a ispravnost jedne ne jam?i ispravnost druge
Oba ovdje opisana buga imala su to?no ovaj oblik. U svakom slu?aju, provjeritelj je pro?ao, a tijelo nije, ?to vas ?alje u potragu za bugom u lozinki ili izvo?enju klju?a koji ne postoji. Stvarni propust bio je nizvodno, u na?inu na koji su bajtovi paketa transformirani. Dva propusta su neovisna, jedan u AES putanji i jedan u RC4 putanji, ali dijele problem dijagnoze, pa je vrijedno vidjeti za?to je napola to?an rezultat najte?a vrsta za ?itanje
Za?to prolazna lozinka ne dokazuje ni?ta o tijelu
Format koji koristi moderni ?ifrirani XLSX je ECMA-376 Standard Encryption, i on pohranjuje dvije ?ifrirane stvari jednu uz drugu. Jedna je EncryptionVerifier: mali blok koji dr?i nasumi?nu vrijednost i njezin hash, ?ifriran klju?em izvedenim iz lozinke. Druga je EncryptedPackage: cijeli zip spremnik radne knjige, ?ifriran istim klju?em. Provjeritelj postoji kako bi ?ita? mogao potvrditi lozinku prije nego ?to potro?i trud na megabajte tijela. De?ifrirajte provjeritelj, izra?unajte hash nasumi?ne vrijednosti, usporedite ga s pohranjenim hashom, i ako se podudaraju, lozinka je to?na
Zamka je u tome ?to su provjeritelj i paket ?ifrirani odvojenim pozivima preko odvojenih spremnika. Klju? koji je ispravno izveden de?ifrirat ?e provjeritelj ispravno bez obzira na to ?to se nakon toga dogodi s paketom. Stoga, ako je va?e izvo?enje klju?a to?no, ali je transformacija paketa pogre?na, Excel potvr?uje lozinku iz provjeritelja, a zatim zakazuje na tijelu. Simptom se ?ita kao "to?na lozinka, pokvarena datoteka", ?to usmjerava istragu na putanju lozinke, koja je jedini dio koji zapravo nije bio pokvaren. Isti razmak vlada i naslije?enim slu?ajem RC4: hash provjeritelja se provjerava prvi, a tijelo koje ispadne iz sinkronizacije i dalje ostavlja tu provjeru netaknutom
Prva buba: AES u ECB-u, a ne u CBC-u
[MS-OFFCRYPTO] ?2.3.4.15 specificira da Standard Encryption ?ifrira paket pomo?u AES-a u na?inu rada Electronic Codebook. Svaki blok od 16 bajtova podstavljenog paketa ?ifrira se neovisno istim klju?em. Nema ulan?avanja izme?u blokova i nema inicijalizacijskog vektora. To je neobi?an izbor prema modernim standardima, gdje se ECB obi?no izbjegava, ali interoperabilnost nije mjesto za naga?anje specifikacije. Excel de?ifrira paket kao ECB, pa ga proizvo?a? mora ?ifrirati kao ECB ili se to dvoje ne?e slo?iti
Bug je bio u tome ?to je paket ?ifriran pomo?u AES-a u na?inu rada CBC koriste?i inicijalizacijski vektor s nultim vrijednostima. Evo za?to to gotovo radi, i za?to je "gotovo" najgore mjesto na koje mo?ete sletjeti. U CBC-u, prvi blok ?istog teksta prolazi kroz XOR s IV-om prije ?ifriranja. Kada se IV sastoji od samih nula, taj XOR ne mijenja ni?ta, pa prvi blok CBC-a s nultim IV-om proizvodi to?no isti ?ifrirani tekst kao i ECB. Od drugog bloka nadalje CBC uvodi prethodni ?ifrirani blok u sljede?i, pa se svaki blok nakon prvog razlikuje od ECB-a
Sada to preklopite na strukturu. Izgled paketa postavlja 8-bajtni prefiks duljine na sam po?etak, pa dijelovi datoteke koje Excel najranije provjerava sjede u prvom bloku ili dva. Prvi blok koji se podudara zna?i da najranija provjera prolazi dok se svaki kasniji blok de?ifrira u ?um. Ispravak nije suptilan kada se imenuje na?in rada: ?ifrirajte svaki blok od 16 bajtova pomo?u ECB-a i zaustavite ulan?avanje. U pogonu, XlsEncryptStdPackage prolazi kroz podstavljeni spremnik u koracima od 16 bajtova i poziva AESEncryptECB128Block na svakom od njih, ?to je isti primitiv koji se ve? koristi za blokove provjeritelja. Izvor nosi komentar na petlji koji jasno navodi pravilo: CBC s nultim IV-om odgovara ECB-u samo za prvi blok, pa bi se ostatak paketa de?ifrirao u sme?e i Excel bi ga odbio
var
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create(nil);
try
Book.Open('report.xlsx');
// SaveAsEncrypted serializes the workbook, then runs the
// ECMA-376 Standard Encryption pipeline: AES-128 ECB over the
// package per [MS-OFFCRYPTO] 2.3.4.15. Returns 1 on success.
if Book.SaveAsEncrypted('report_secure.xlsx', 'S3cret!') <> 1 then
raise Exception.Create('Encryption failed');
finally
Book.Free;
end;
end;
Druga buba: RC4 ponovno ključanje izlazi iz koraka
Putanja naslije?enog .xls koristi shemu RC4 CryptoAPI, i njezino je pravilo druga?ije. [MS-OFFCRYPTO] ?2.3.6 specificira da se ?ifra ponovno klju?a na svakoj granici bloka od 1024 bajta. Tok je podijeljen na blokove od 1024 bajta, novi RC4 klju? se izvodi za blokove broj 0, 1, 2 i tako dalje, a unutar svakog bloka tok klju?a se tro?i kontinuirano od bajta do bajta. Dvije invarijante moraju vrijediti zajedno: ponovno klju?anje na svakoj granici i tro?enje toka klju?a bez praznina unutar bloka. RC4 je strujna ?ifra, pa je njezin tok klju?a jedan ure?eni niz; n-ti bajt koji povu?ete odre?en je time koliko ste bajtova povukli prije njega. De?ifriranje je isti XOR na istom nizu, ?to zna?i da proizvo?a? i potro?a? moraju povu?i to?no iste bajtove na to?no istim pozicijama
To je cijela pote?ko?a. Strujna ?ifra nema ponovnu sinkronizaciju. Ako izgubite jedan bajt toka klju?a, svaki bajt nakon njega prolazi kroz XOR s pogre?nim bajtom toka klju?a, i pogre?ka se nikada sama ne ispravlja; kaskadno se prenosi do kraja bloka i, jednom kada je pokrenuta pozicija pogre?na, na svaki blok nakon njega. Bug je ovdje u?inio upravo to. Broja? blokova je po?eo od sentinel vrijednosti minus jedan, a rutina preskakanja pretpostavila je da broja? ve? odgovara trenutnom bloku. Po?ev?i od tog sentinela, ponovno je klju?ala i pokrenula cijeli blok od 1024 bajta toka klju?a koji nikada nije trebao biti potro?en, i u tom procesu dovela preostali broj u negativan raspon. Od te to?ke de?ifrator je bio puni blok izvan faze. Provjeritelj, provjeren prije svega ovoga, i dalje je prolazio, pa je lozinka izgledala to?no dok je svaka podatkovna ?elija izlazila kao sme?e
Ispravljena logika ?ivi u TXLSDecrypterRC4. I Skip i Decrypt dijele jednu petlju: ponovno klju?anje samo kada se pokrenuta pozicija prebaci u novi blok, gdje je indeks bloka pozicija podijeljena s REKEY_BLOCK_SIZE (1024), zatim se tro?i do ostatka trenutnog bloka i ni?ta vi?e. MakeKey se poziva s indeksom bloka, nikada sa zastarjelim ili sentinel indeksom, a pozivatelj pozicija napreduje za to?an broj obra?enih bajtova tako da Skip i Decrypt ostaju fazno poravnani s proizvo?a?em. Lekcija je u najmanjoj jedinici: jedan izgubljeni bajt nije mala pogre?ka u strujnoj ?ifri, to je potpuni gubitak svega nizvodno
var
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create(nil);
try
// CanReadEncrypted checks the Compound File (OLE2) signature so
// you can branch before attempting a normal Open. OpenEncrypted
// routes plain files to Open and handles the encrypted container.
if Book.CanReadEncrypted('legacy.xls') then
Book.OpenEncrypted('legacy.xls', 'S3cret!')
else
Book.Open('legacy.xls');
// read cells here
finally
Book.Free;
end;
end;
Interoperabilnost sa zamrznutom specifikacijom je podudaranje u bajt
Oba buga svode se na isto korijensko na?elo, i to vrijedi navesti samo za sebe jer mijenja na?in na koji procjenjujete izbore dizajna. Kada je potro?a? va?eg izlaza fiksni vanjski program koji ne mo?ete promijeniti, na?in ?ifriranja i ritam ponovnog klju?anja nisu detalji implementacije koje mo?ete optimizirati ili pojednostaviti. Oni su dio mre?nog ugovora. Excel ?e de?ifrirati s ECB-om i ponovno klju?ati na granicama od 1024 bajta svi?ali se vam ti izbori ili ne, a va? jedini posao je proizvesti bajtove koji se de?ifrira u izvornik pod to?no tim postupkom. Na?in koji je moderniji, IV koji izgleda bezopasan, broja? koji po?inje tamo gdje se ?ini prirodnim; bilo ?to od toga je nedostatak onog trenutka kada odstupi od onoga ?to ?ita? o?ekuje. Interoperabilnost sa zamrznutom specifikacijom nije pribli?na. Ona je bajtovno to?na ili je pokvarena
To je tako?er razlog za?to je provjeritelj sam po sebi lo? test dima. On vam govori da izvo?enje klju?a radi, ?to je nu?no, ali daleko od dovoljnog. Test koji samo otvara ?ifriranu datoteku i potvr?uje lozinku izvijestit ?e o uspjehu dok je tijelo ne?itljivo. Stvarni test de?ifrira paket i uspore?uje oporavljene bajtove s izvornim ulazom, ili provodi radnu knjigu kroz ?ifriranje i de?ifriranje i ponovno ?ita ?elije. Provjeritelj dokazuje lozinku; samo tijelo dokazuje ?ifriranje
Podržani način čitanja i pisanja zaštićenih radnih knjiga
Javna povr?ina je mala. Za pisanje lozinkom za?ti?ene moderne radne knjige, popunite ili otvorite TXLSXWorkbook i pozovite SaveAsEncrypted s nazivom datoteke i lozinkom; to serijalizira radnu knjigu i pokre?e cjevovod Standard Encryption koji je prvi popravak ispravio, vra?aju?i 1 za uspjeh. Za ?itanje, pozovite CanReadEncrypted kako biste testirali je li datoteka ?ifrirani spremnik Compound File, a zatim granajte: OpenEncrypted rukuje ?ifriranom putanjom i vra?a se na Open za obi?ne datoteke, a Open s lozinkom je dostupan izravno. Rukovanje na?inom rada i petlja ponovnog klju?anja opisani gore nalaze se ispod tih poziva; vi dajete lozinku i naziv datoteke, a pogon uskla?uje specifikaciju u va?e ime
var
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create(nil);
try
Book.Open('quarterly.xlsx');
Book.SaveAsEncrypted('quarterly_locked.xlsx', 'P@ssphrase');
// Reopen on the consumer side
Book.OpenEncrypted('quarterly_locked.xlsx', 'P@ssphrase');
finally
Book.Free;
end;
end;
Oblik za?ti?enog izlaza, tok EncryptionInfo, blokovi provjeritelja i izgled paketa pokriveni su u na?em vodi?u kroz AES za?ti?eni XLSX izlaz. Za zasebno pitanje zaklju?avanja na razini lista i kako za?tita stupa u interakciju s postavljanjem stranice i ispisom, pogledajte ?lanak o za?titi, postavljanju stranice i ispisu. Obje se grade na putanji ?ifriranja koja je ovdje opisana, a koja se isporu?uje kao dio softvera HotXLS spreadsheet component za Delphi i C++Builder, zajedno s API-jima za ?itanje, pisanje i prikazivanje koji su pokriveni drugdje na ovom blogu