HotXLS lukee Agile-suojattuja Excel-tiedostoja — eli salasanasuojausta, jota Excel 2010 ja kaikki uudemmat versiot käyttävät oletuksena — yhdellä kutsulla: TXLSXWorkbook.OpenEncrypted. Komponentti jäsentää XML-salauskuvauksen, johtaa avaimet salasanasta SHA-512-tiivistealgoritmilla, varmistaa salasanan salattua varmistetta vasten ja purkaa sitten paketin 4096 tavun mittaisissa AES-CBC-segmenteissä. Excel-asennusta, COM-riippuvuuksia tai ulkoisia kryptografisia DLL-tiedostoja ei tarvita
Tämä artikkeli käsittelee nimenomaan Agile-salauksen lukupuolta. Kaksi muuta läheistä ongelmaa on käsitelty omissa artikkeleissaan: yhteensopivuutta vanhojen BIFF .xls-tiedostojen sisältämien perinteisten RC4- ja XOR-mallien kanssa käsitellään ECB- ja RC4-yhteensopivuutta käsittelevässä artikkelissa, ja salasanasuojattujen työkirjojen luomista ECMA-376 Standard -salauksella käsitellään AES-suojattua XLSX-tulostusta käsittelevässä artikkelissa. Tässä skenaariossa tiedosto on jo olemassa, joku muu on salannut sen ja tehtäväsi on avata se
Skenaario, joka tekee tästä välttämätöntä, on tuttu jokaiselle asiakirjaputkea ylläpitävälle. Palvelinpuolen tuontipalvelu ottaa vastaan työkirjalatauksia; koneella ei ole Exceliä eikä sitä koskaan siihen tule; ja eräänä aamuna asiakas lataa täysin tavallisen .xlsx-tiedoston, jonka ZIP-lukija hylkää, koska se ei ole lainkaan ZIP-tiedosto. Asiakas on tallentanut sen salasanalla. Siitä hetkestä lähtien lataajasi joko ymmärtää [MS-OFFCRYPTO]-määrittelyt tai se palauttaa tiedoston takaisin käyttäjälle, joka omasta näkökulmastaan ei tehnyt mitään epätavallista
Mitä on Agile-salaus Excel-tiedostossa?
Agile-salaus on salasanasuojausmalli, joka on määritelty [MS-OFFCRYPTO]-dokumentissa pykälissä §2.3.4.10–§2.3.4.15. Sitä Excel 2010 ja uudemmat kirjoittavat aina, kun työkirja tallennetaan salasanalla. Suojattu tiedosto ei ole enää ZIP-paketti. Se on OLE Compound File Binary (CFB) -säiliö, joka sisältää kaksi virtaa: EncryptionInfo, joka kuvaa miten salaus on tehty, ja EncryptedPackage, joka on varsinainen .xlsx-ZIP-tiedosto salattuna peitetyksi blobiksi. CFB-allekirjoitus (D0 CF 11 E0 A1 B1 1A E1) is the same magic that legacy BIFF .xls files carry, which is why a renamed or encrypted file cannot be classified by extension alone
Yksi liityntäpiste selväkielisille, Standard- ja Agile-työkirjoille
TXLSXWorkbook.OpenEncrypted käsittelee kaikki kolme tilaa, joita kutsuja voi kohdata: selväkielisen ZIP-tiedoston, Standard-suojatun ja Agile-suojatun tiedoston. Näin ollen latauksen käsittelijöiden ei tarvitse luokitella tiedostoja ennen niiden lataamista. Metodi tutkii ensin tiedoston: jos CFB-allekirjoitusta ei löydy, se siirtyy tavalliselle Open-polulle ja salasana yksinkertaisesti ohitetaan. Jos tiedosto on CFB-säiliö, se kokeilee ensin ECMA-376 Standard -salausta ja jos EncryptionInfo-version allekirjoitus on Agile 4.4, se siirtyy Agile-prosessiin. Palautusarvo on 1 onnistuessa, mikä on sama sopimus kuin Open-metodilla
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;
Varapolku salaamattomalle syötteelle on tärkeämpi kuin miltä se näyttää. Erätuoja, joka kutsuu aina OpenEncrypted-metodia, ei tarvitse haarautumista kutsuvaltiossa: tiedostot, joita ei koskaan suojattu, latautuvat täsmälleen kuten ennenkin, ja suojatut tiedostot puretaan paikoillaan ja syötetään tavalliselle ZIP-lukijalle muistivirrana. Testattavana on vain yksi koodipolku kolmen sijaan
Miten salasanasta tulee AES-avain?
Agile-salaus ei koskaan käytä salasanaa suoraan. HotXLS laskee ensin toistetun tiivisteen (iterated hash): ensimmäinen tiiviste on SHA-512 salasanasuolasta yhdistettynä salasanan UTF-16LE-tavuihin, ja sen jälkeen tiiviste lasketaan uudelleen spinCount kertaa. Jokaisella kierroksella edellisen tiivisteen eteen lisätään 32-bittinen little-endian-muotoinen kierroslaskuri. Excelin oletusarvoisella 100 000 spin-kierroksen määrällä tämä tarkoittaa satatuhatta peräkkäistä SHA-512-kutsua salasanaehdotusta kohden, mikä on koko homman ydin. Kierrosmäärä toimii raa'an voiman hyökkäyksen hidastimena: se maksaa oikeutetulle kutsujalle vain muutaman millisekunnin kerran, mutta aiheuttaa saman viiveen sanakirjahyökkääjälle jokaisen arvauksen kohdalla
Lopullinen tiiviste ei ole vielä avain. Siitä johdetaan kolme erillistä avainta tiivistämällä se vielä kerran siten, että siihen liitetään kiinteä 8-tavuinen lohkoavain (block key). Kullekin tarkoitukselle on oma vakionsa: FE A7 D2 76 3B 4B 9E 79 varmistesyötteen purkamiseen, D7 AA 0F 6D 30 61 34 4E varmistetiivisteelle ja 14 6E 0B E7 AB AC D0 D6 varsinaisen pakettiavaimen avaamiseen. Jokainen SHA-512-tulos katkaistaan ilmoitettuun avaimen pituuteen ja [MS-OFFCRYPTO]-ohjeiden mukaisesti täytetään 0x36-tavuilla teoreettisessa tapauksessa, jossa tiiviste on avainta lyhyempi. Samaa 0x36-täytesääntöä sovelletaan, kun salasanasuojauslaajennus ulotetaan lohkokoon mittaiseksi käytettäväksi CBC-alustusvektorina (IV)
Salasanan varmistus ja saltSize-katkaisun ansa
HotXLS varmistaa salasanan ennen kuin se koskee pakettiin käyttämällä kuvauksesta saatua varmisteparia. Se purkaa kohteen encryptedVerifierHashInput ensimmäisellä johdetulla avaimella, tiivistää tuloksen SHA-512-algoritmilla, purkaa kohteen encryptedVerifierHashValue toisella johdetulla avaimella ja vertaa kahta tiivistettä tavu tavulta. Ristiriita tarkoittaa, että salasana on väärä. Tästä raportoidaan erillisenä tuloksena eikä sotkeutuneena työkirjana, ja mikä tärkeintä, paketin runkoa ei koskaan pureta väärällä avaimella. Näin ollen väärä salasana ei voi tuottaa uskottavalta näyttävää korruptoitunutta dataa
Tässä on eräs määrittely-yksityiskohta, joka on helppo ymmärtää väärin. [MS-OFFCRYPTO] §2.3.4.13 määrittelee varmistimen saltSize-tavun pituiseksi satunnaisdataksi, jossa saltSize on avainsalaajan suolan pituus, ei salauksen lohkokoko. Koska AES-CBC-salausteksti on lohkokohdistettua, purettu varmistesyöte palautuu täytettynä 16 tavun moninkertaiseksi, ja se on katkaistava takaisin saltSize-pituuteen ennen tiivistämistä. Excel kirjoittaa saltSize-arvoksi aina saman kuin blockSize (molemmat 16), joten toteutus, joka ohittaa tämän katkaisun, läpäisee kaikki testit todellisella Excel-tulosteella, mutta epäonnistuu heti ensimmäisessä tiedostossa, jonka luoja valitsi toisen suolan pituuden. HotXLS katkaisee tiedon suolan pituuteen, koska se on se, mitä määrittelyssä todella sanotaan. Näiden kahden arvon samankaltaisuus käytännössä on sattumaa, ei sopimus
Miten EncryptedPackage puretaan?
EncryptedPackage-virta alkaa 8-tavuisella little-endian-muotoisella selväkielisen tekstin koolla, jota seuraa salattu teksti 4096 tavun segmenteissä. HotXLS purkaa sen segmentti segmentiltä käyttäen uutta IV-arvoa kullekin segmentille. Itse pakettiavain ei ole salasanasta johdettu: se on satunnainen väliavain, jonka kirjoittaja salasi kohteeseen encryptedKeyValue, ja HotXLS avaa sen kolmannella johdetulla avaimella katkaisten sen keyData-elementin ilmoittamaan avaimen pituuteen. Kunkin segmentin IV on SHA-512-tiiviste keyData-suolasta yhdistettynä 32-bittiseen little-endian-muotoiseen segmentti-indeksiin katkaistuna lohkokokoon. Tämä rakenne tarkoittaa, että mikä tahansa 4096 tavun segmentti voidaan purkaa itsenäisesti. Tämä tekee muodosta periaatteessa suorasaantiystävällisen (random access), vaikka HotXLS purkaa koko paketin muistiin ja välittää tuloksena saadut ZIP-tavut tavalliselle XLSX-lukijalleen
Ilmoitettu selväkielinen koko tekee viimeisen työvaiheen
Ilmoitettu selväkielinen koko tekee viimeisen työvaiheen. AES-CBC-tuloste on lohkokohdistettua, joten viimeinen segmentti sisältää enintään 15 tavua täytettä, joka ei kuulu dokumenttiin. Purettu puskuri katkaistan kokoprefiksin mukaan, ja tulos on täsmälleen se .xlsx-ZIP, jonka Excel salasi. HotXLS tarkistaa prefiksin todellista virran pituutta vasten ennen purkamista, joten katkennut lataus tai peukaloitu kokokenttä epäonnistuu siististi ylivuodon sijaan
Virheraportointi ja rehelliset rajoitukset
Epäonnistumistilat pidetään tarkoituksella erillään. Väärä salasana nostaa poikkeuksen selkeällä virhesanomalla varmistimen ristiriidan perusteella, jotta käyttöliittymä voi pyytää käyttäjää yrittämään uudelleen. CFB-säiliö, jonka kuvaus ilmoittaa muita kuin tuettuja algoritmeja (eli mitä tahansa muuta kuin AES-salausta CBC-ketjutuksella ja SHA-512-tiivistystä Agile-kuvauksessa), tai säiliö, joka ei ole Standard eikä Agile, nostaa toisenlaisen poikkeuksen ilmoittaen menetelmän olevan tukematon. Näitä kahta ei saa koskaan sekoittaa keskenään: salasanan yrittäminen uudelleen tukemattomaan menetelmään tuhlaa käyttäjän aikaa, ja väärän salasanan raportointi muotovirheeksi ohjaa tukihenkilöstösi väärään suuntaan
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;
Rajat on syytä todeta selkeästi. HotXLS lukee Agile-kuvauksia, jotka ilmoittavat AES-salauksen CBC-tilassa SHA-512-tunnisteella. Tämä kattaa sen, mitä Excel 2010:stä Excel 365:een todellisuudessa kirjoittavat kaikissa kolmessa avainkoossa. Muiden salaus- tai tiivistealgoritmien kuvaukset hylätään arvailun sijaan, eikä varmennepohjaisia avainsalaajia käytetä, vaan ainoastaan salasana-avainsalaajaa. Kirjoituspuolella HotXLS tuottaa tällä hetkellä Standard-salausta Agilen sijaan, mikä on tärkeä ero, jos myöhemmät työkalut tutkivat menetelmää. Yksityiskohdat ovat saatavilla AES-suojattua XLSX-tulostusta käsittelevässä artikkelissa
Salasanasuojatut lataukset lakkaavat olemasta erikoistapaus heti, kun lataaja käsittelee salausta osana tiedostomuotoa eikä sen poikkeuksena. Tässä kuvattu OpenEncrypted-liityntäpiste, SHA-512-kierrosjohdannainen ja segmentoitu AES-CBC-putki toimitetaan osana HotXLS Delphi Excel Component -kirjastoa, yhdessä sen luku- ja kirjoitusmoottorin kanssa Delphille ja C++Builderille