Techninis straipsnis

HotXLS XLS XOR užkodavimas: rakto išvedimas ir XorRor

HotXLS rašo Excel 5.0/95 (BIFF5) XOR užkoduotas darbaknyges, kurias Excel 16 atidaro tik tada, kai trys detalės sutampa su [MS-OFFCRYPTO] iki taško: FILEPASS raktas turi būti CreateXorKey_Method1(password), 16 baitų XOR masyvas turi būti sudarytas su XorRor (sukimas į dešinę vienu bitu), o kiekvienas baitas turi naudoti XorArrayIndex = (srauto poslinkis + įrašo ilgis) mod 16. HotXLS indeksą sutvarkė v2.384.47, o raktą ir sukimą – v2.384.54. Iki to visi jo pagaminti su slaptažodžiu apsaugoti BIFF5 failai HotXLS atverdavo be nusiskundimų, o Excel – ne

Tas paskutinis sakinys – visa istorija miniatiūroje. Skaitytuvas ir rašytojas, dalijantys tą pačią klaidingą idėją, vienas su kitu sutaria puikiai, tad round-trip testai žaliuoja, kol vienintelis svarbus vartotojas sako ne. Excel 16 du kartus pasakė ne, su dviem skirtingais pranešimais, ir kiekvienas pranešimas rodė į kitą schemos sluoksnį. Šis straipsnis per tuos sluoksnius eina ta tvarka, kuria juos tikrina Excel, su baitų lygio detalėmis, kurios pravers ir kviečiant HotXLS, ir rašant savą BIFF skaitytuvą

Ką iš tikrųjų saugo BIFF XOR užkodavimas?

BIFF XOR užkodavimas faile saugo tik du 16 bitų žodžius, o viskas kita perskaičiuojama iš slaptažodžio. FILEPASS įrašas ($002F) stovi iškart po darbaknygės globals BOF, o BIFF5 faile jo kūnas yra tiksliai 4 baitai: XOR raktas, paskui slaptažodžio verifikatorius. Jokios druskos (salt), jokio algoritmo identifikatoriaus ir jokio užšifruoto verifikatoriaus bloko, kokius nešioja RC4 ir AES schemos

Iš tų dviejų žodžių skaitytuvas atkuria tris dalykus:

  • verifikatorių – 16 bitų maišą iš slaptažodžio baitų, apdorotų XOR su $CE4B. Jo palyginimas su saugomu žodžiu yra slaptažodžio patikra, ir vienintelė
  • XOR raktą – 16 bitų reikšmę iš [MS-OFFCRYPTO] §2.3.7.2 CreateXorKey_Method1, varomą dviejų konstantinių lentelių (InitialCode, 15 žodžių, ir XorMatrix, 105 žodžiai)
  • XOR masyvą – 16 baitų iš slaptažodžio baitų, papildytų fiksuota 16 baitų paketaine, kiekvienas apdorotas XOR su žemu rakto baitu (lyginėse pozicijose) arba aukštu rakto baitu (nelyginėse), tada sukamas į dešinę vienu bitu

Įrašų antraštės lieka grynatekstės, kaip ir saujelė visų įrašų, kuriuos schema atleidžia, tarp jų BOF, FILEPASS ir INTERFACEHDR. Kiekvienas kitas įrašo kūnas transformuojamas baitas po baito: sukimas į kairę 5 bitais, tada XOR su vienu iš 16 baitų masyvo elementų. Iššifravimas, kurį [MS-OFFCRYPTO] §2.3.7.3 išraiškingai vadina DecryptData_Method1, yra veidrodinis atspindys: pirmiau XOR, tada sukimas į dešinę 5

HotXLS programoje prie viso to tiesiogiai neliečiate. Nustatote slaptažodį, pasirenkate formatą, ir SaveAs išduoda FILEPASS bei transformuoja srautą:

uses
  SysUtils, lxHandle;

procedure SaveLegacyProtectedBook(const FileName: string);
var
  Wb: IXLSWorkbook;
begin
  Wb := TXLSWorkbook.Create;
  Wb.Sheets.Add.Name := 'Ledger';
  Wb.Sheets[1].Range['A1', 'A1'].Value := 'Account';
  Wb.Sheets[1].Range['B1', 'B1'].Value := 1250.75;

  // BIFF5 palaiko tik XOR užkodavimą; xletAuto pasirinktų tą patį
  Wb.EncryptionType := xletXor;
  // Laikykite ASCII ir ne ilgesnį kaip 15 simbolių (žr. žemiau)
  Wb.EncryptionPassword := 'secret';

  if Wb.SaveAs(FileName, xlExcel5) <> 1 then
    raise Exception.Create('BIFF5 save failed');
end;

Kodėl Excel sako, kad slaptažodis netinka, kai verifikatorius sutampa?

Excel atmeta slaptažodį, nes nepasitiki saugomu raktu: Excel raktą išveda iš įvesto slaptažodžio su CreateXorKey_Method1 ir lygina su FILEPASS rakto žodžiu, tad failas, kurio raktas kitoks, žlugdo slaptažodžio patikrą net tada, kai verifikatorius teisingas. Specifikacija raktą apibūdina kaip slaptažodžio išvestį, o ne laisvą parametrą, ir Excel 16 to laikosi

HotXLS rašytojas iki v2.384.54 rakto žodį užpildydavo dviem atsitiktiniais baitais. Popieriuje tai atrodo nekenksminga, nes verifikatorius yra dokumentuota slaptažodžio patikra, o masyvas sudaromas iš to rakto, kurį failas deklaruoja. Pats HotXLS tuos failus skaitė be rūpesčių, nes jo skaitytuvas raktą iš FILEPASS ėmė kaip duotybę. Excel 16, gavęs tą patį failą ir teisingą slaptažodį, atsakydavo, kad slaptažodis netinka. Nuo v2.384.54 raktas išvedamas, tad FILEPASS slaptažodžiui secret visada laiko raktą $014D ir verifikatorių $DAA7, o šios reikšmės suskaičiuotos su nepriklausoma specifikacijos implementacija

HotXLS BIFF5 XOR FILEPASS įrašo, kurį Excel 16 tikrina atidarydamas, diagrama: grynatekstis keturių baitų kūnas laiko XOR raktą ir slaptažodžio verifikatorių, Excel raktą išveda iš įvesto slaptažodžio su CreateXorKey_Method1 ir atsitiktinį raktą atmeta slaptažodžio klaida net tada, kai verifikatorius sutampa; HotXLS saugo išvestą raktą 014D slaptažodžiui secret
FILEPASS kūnas – tik du žodžiai, bet Excel raktą išveda iš jūsų slaptažodžio iš naujo ir lygina; atsitiktinai užpildytas raktas žlugdo patikrą net su teisingu verifikatoriumi, todėl HotXLS jį išveda nuo v2.384.54

Pati išvestis trumpa, kai abi lentelės vietoje. Eikite slaptažodį atgal, žiūrėkite į kiekvieno baito 6 bitą septynis kartus, jį stumdydami į kairę, ir kaskart, kai bitas nustatytas, įmaišykite XOR vieną XorMatrix elementą. Žemiau – principinis eskizas, atkuriantis specifikacijos algoritmą ir sutampantis su HotXLS implementacija; tai nėra HotXLS API:

// [MS-OFFCRYPTO] 2.3.7.2 CreateXorKey_Method1 principinis eskizas
// ir CreateXorArray_Method1 (iliustracija, ne HotXLS API)
type
  TXorArray = array [0..15] of Byte;

function DemoCreateXorKey(const Password: AnsiString): Word;
const
  InitialCode: array [0..14] of Word = ($E1F0, $1D0F, $CC9C, $84C0, $110C,
    $0E10, $F1CE, $313E, $1872, $E139, $D40F, $84F9, $280C, $A96A, $4EC3);
  XorMatrix: array [0..104] of Word = (
    $AEFC, $4DD9, $9BB2, $2745, $4E8A, $9D14, $2A09,
    $7B61, $F6C2, $FDA5, $EB6B, $C6F7, $9DCF, $2BBF,
    $4563, $8AC6, $05AD, $0B5A, $16B4, $2D68, $5AD0,
    $0375, $06EA, $0DD4, $1BA8, $3750, $6EA0, $DD40,
    $D849, $A0B3, $5147, $A28E, $553D, $AA7A, $44D5,
    $6F45, $DE8A, $AD35, $4A4B, $9496, $390D, $721A,
    $EB23, $C667, $9CEF, $29FF, $53FE, $A7FC, $5FD9,
    $47D3, $8FA6, $0F6D, $1EDA, $3DB4, $7B68, $F6D0,
    $B861, $60E3, $C1C6, $93AD, $377B, $6EF6, $DDEC,
    $45A0, $8B40, $06A1, $0D42, $1A84, $3508, $6A10,
    $AA51, $4483, $8906, $022D, $045A, $08B4, $1168,
    $76B4, $ED68, $CAF1, $85C3, $1BA7, $374E, $6E9C,
    $3730, $6E60, $DCC0, $A9A1, $4363, $86C6, $1DAD,
    $3331, $6662, $CCC4, $89A9, $0373, $06E6, $0DCC,
    $1021, $2042, $4084, $8108, $1231, $2462, $48C4);
var
  Len, I, Bit, Element: Integer;
  Ch: Byte;
begin
  Result := 0;
  Len := Length(Password);
  if Len > 15 then
    Len := 15;                       // raktas mato tik 15 baitų
  if Len = 0 then
    Exit;
  Result := InitialCode[Len - 1];
  Element := $68;                    // paskutinis XorMatrix elementas
  for I := Len downto 1 do
  begin
    Ch := Ord(Password[I]);
    for Bit := 1 to 7 do
    begin
      if (Ch and $40) <> 0 then
        Result := Result xor XorMatrix[Element];
      Ch := Byte(Ch shl 1);
      Dec(Element);
    end;
  end;
end;

function XorRor(B, KeyByte: Byte): Byte;
begin
  B := B xor KeyByte;
  Result := Byte((B shr 1) or (B shl 7));   // sukimas į dešinę vienu bitu
end;

procedure DemoCreateXorArray(const Password: AnsiString; out Arr: TXorArray);
const
  PadArray: TXorArray = ($BB, $FF, $FF, $BA, $FF, $FF, $B9, $80,
    $00, $BE, $0F, $00, $BF, $0F, $00, $00);
var
  Key: Word;
  Len, I: Integer;
begin
  Key := DemoCreateXorKey(Password);
  Len := Length(Password);
  if Len > 16 then
    Len := 16;
  for I := 0 to Len - 1 do
    Arr[I] := Ord(Password[I + 1]);
  for I := Len to 15 do
    Arr[I] := PadArray[I - Len];
  for I := 0 to 15 do
    if Odd(I) then
      Arr[I] := XorRor(Arr[I], Byte(Key shr 8))
    else
      Arr[I] := XorRor(Arr[I], Byte(Key and $FF));
end;

Slaptažodžiui secret tai duoda masyvą 1F 32 17 B9 14 BA 7B 7F 59 DD 59 7F 7A C0 A6 DF – patogus etalonas, jei bandote savą skaitytuvą

HotXLS XOR masyvo, skirto BIFF5 užkodavimui, sudarymo diagrama: šešiolika baitų, pasėtų iš slaptažodžio ir fiksuotos paketainės, yra apdorojami XOR su žemu rakto baitu lyginėse pozicijose ir aukštu nelyginėse, tada sukami į dešinę vienu bitu su XorRor, duodami etaloną 1F 32 17 B9 slaptažodžiui secret
Masyvo baitai atkeliauja iš slaptažodžio, paketainės ir dviejų rakto baitų, gale – vienas sukimas; sukimas į kairę 2 veikė tik tada, kai baito transformacija vykdavo priešinga tvarka, o Excel laikosi specifikacijos tvarkos

Kodėl teisingas raktas vis tiek duoda sugadintą failą?

Teisingas raktas vis tiek duoda sugadintą failą, kai XOR masyvas sukamas netinkama kryptimi: [MS-OFFCRYPTO] masyvo žingsnį apibrėžia kaip XorRor, sukimą į dešinę vienu bitu, o į kairę dviem bitais sukamas masyvas kiekvieną įrašo kūną iššifruoja į triukšmą. Rakto sutvarkymas Excel 16 peržengė pro slaptažodžio raginimą ir nukreipė tiesiai į kitokį įspėjimą – pranešimą, kad faile yra problema ir jo negalima atidaryti

Senas HotXLS kodas kiekvieną masyvo baitą sukdydavo į kairę 2 bitais – forma, kuri sklinda po kelias BIFF implementacijas. Kadangi HotXLS tą patį sukimą naudojo abiejose pusėse, jo paties skaitytuvas niekada nepastebėdavo. Excel 16 nebepasako Excel 5.0/95 failų, o rašydamas BIFF8 XOR nepasiūlo, tad savo Excel pavyzdžio, su kuo lyginti, nebuvo. Įrodymai teko keliauti iš kitos pusės: parašykite vieną grynatekstį BIFF5 srautą, užkoduokite jį aštuoniais būdais ir leiskite Excel 16 atidaryti kiekvieną variantą. Aštuoni variantai kombinavo tris nepriklausomus pasirinkimus:

PasirinkimasVariantas AVariantas B
Masyvo sukimasXorRor (sukimas į dešinę 1)Sukimas į kairę 2
Masyvo indeksas(offset + record length) mod 16offset mod 16
Baito transformacijos tvarkaSukimas į kairę 5, tada XORXOR, tada sukimas į kairę 5

Excel 16 atidarė tiksliai du iš aštuonių: XorRor su sukimu-tada-XOR ir įrašo ilgio indeksu bei vieną variantą, kuris tik atrodo kitoks. Sukimas-į-kairę-2 su XOR-tada-sukimu ir tuo pačiu indeksu yra ta pati funkcija persirengusi. Sukimas plinta per XOR, tad rol5(p xor rol2(b)) lygu rol5(p) xor rol7(b), o 8 bitų reikšmei sukimas į kairę 7 yra sukimas į dešinę 1. Trumpai, rol5 ∘ rol2 = ror1, todėl sukimo-į-kairę-2 masyvas izoliuotai atrodo tikėtinas: jis teisingas tik kartu su priešinga transformacijos tvarka. Suporuotas su specifikacijos tvarka, jis sugadina kiekvieną transformuotą baitą

Tas pats eksperimentas išsprendė ir antrą klausimą. Variantai, išmetę įrašo ilgį iš indekso, žlugdavo visi, kas patvirtino indekso taisyklę, kurią HotXLS priėmė leidimu anksčiau, remdamasis vien specifikacijos tekstu

Kaip skaičiuojamas XorArrayIndex kiekvienam baitui?

XorArrayIndex baitui yra jo poslinkis darbaknygės sraute, plius viso įrašo duomenų, kuriems jis priklauso, ilgis, mod 16. Indeksas todėl kiekvienam įrašui persišoka į nuo įrašo priklausomą reikšmę ir jame didėja po vienetą baitui. Specifikacijos pseudokodas įvestis vadina FileOffset ir Data.Length, kas lengva perskaityti kaip vien įrašo pradžios poslinkį, ir būtent toks perskaitymas HotXLS platino iki v2.384.47

Trys detalės sprendžia, ar jūsų indeksai sulygsta su Excel:

  • 4 baitų įrašo antraštė niekada netransformuojama, bet vis tiek užima srauto pozicijas, tad pirmasis įrašo kūno baitas stovi antraštės poslinkis + 4
  • Ilgio narys yra pilnas įrašo duomenų ilgis, o ne tikrųjų transformuotų baitų skaičius
  • BOUNDSHEET iš dalies grynas: pirmieji jo 4 baitai, lbPlyPos, lapo BOF srauto poslinkis, lieka skaitomi, kad parseris surastų lapus. Tie 4 baitai transformacijos praleidžiami, bet vis tiek skaitosi ir į poslinkį, ir į įrašo ilgį
HotXLS XorArrayIndex taisyklės BIFF5 XOR užkodavimui diagrama: kiekvienas kūno baitas naudoja srauto poslinkį plius pilną įrašo duomenų ilgį moduliui 16, grynatekstė keturių baitų antraštė ir BOUNDSHEET lbPlyPos priešdėlis vis tiek skaitosi į poslinkį, o įrašo ilgio nario ignoravimas buvo defektas, kurį HotXLS sutvarkė v2.384.47
Masyvo indeksas persišoka po kartą įrašui, o ne po kartą srautui: antraštė ir bet koks grynatekstis priešdėlis užima pozicijas, įrašo duomenų ilgis maitina modulį, ir abi senojo HotXLS pusės sutardavo dėl neteisingos formulės

Sudėta kartu, transformacija įrašui – kelios eilutės. Ir vėl: tai taisyklės eskizas, ne ką nors, ką reikia kviesti:

function Rol8(B: Byte; N: Integer): Byte;
begin
  Result := Byte((B shl N) or (B shr (8 - N)));
end;

// Užkoduoja vieno įrašo kūną vietoje. BodyPos yra Body[0] srauto poslinkis,
// t. y. įrašo antraštės poslinkis + 4. PlainPrefix yra 4 BOUNDSHEET,
// visas ilgis BOF / FILEPASS, 0 daugumai įrašų
procedure DemoObfuscateRecord(var Body: array of Byte; RecordLength: Word;
  PlainPrefix: Integer; BodyPos: LongWord; const Arr: TXorArray);
var
  I: Integer;
begin
  for I := PlainPrefix to RecordLength - 1 do
    Body[I] := Rol8(Body[I], 5) xor
      Arr[(BodyPos + LongWord(I) + RecordLength) mod 16];
end;

// Skaitymas – veidrodinis atvaizdas: B := Body[I] xor Arr[...];
// tada sukimas į dešinę 5, t. y. Rol8(B, 3)

Iki v2.384.47 HotXLS skaitytuvas indeksą skaičiuodavo vien iš srauto pozicijos, o rašytojas naudodavo grynąjį baito poslinkį. Abu ignoruodavo įrašo ilgį, tad ir vėl abi pusės sutardavo tarpusavyje ir su niekuo kitu. Nepriklausomai parašytas dekoderis v2.384.47 išvestį skaitė teisingai, o senesnę – kaip šiukšles, o vėliau aštuonių variantų Excel 16 testas taisyklę patvirtino prieš tikrąjį tikslą

Kas nutinka senesnių HotXLS versijų rašytiems XOR failams?

HotXLS ir toliau skaito savo iki v2.384.54 buvusius XOR failus tikrindamas FILEPASS raktą: kai saugomas raktas lygus rakto išvestei iš slaptažodžio, skaitytuvas sudaro specifikacijos XorRor masyvą, o kai skiriasi – laiko failą senesnio HotXLS failu ir atkuria sukimo-į-kairę-2 masyvą. Excel rašyti failai visada neša išvestą raktą, tad visada eina specifikacijos keliu

Ta patikra – heuristika su tikslia nesėkmės tikimybe. Senas failas, kurio atsitiktinis raktas netyčia sutaptų su išvestu raktu, būtų skaitomas su neteisingu masyvu, o tokios galimybės tikimybė – 1 iš 65 536. Atsarginis kelias dengia tik masyvo sukimą; indekso taisyklė nepersijungia, tad gelbstimi tik tie failai, kurie parašyti tarp v2.384.47 ir v2.384.53. Jei vis dar turite BIFF5 XOR failų iš to laikotarpio, atidarykite juos su esama HotXLS versija ir išsaugokite dar kartą, kad gautumėte failą, kurį priima Excel

Du slaptažodžio niuansai taikomi kiekvienam failui, senam ar naujam:

  • Ilgis. CreateXorKey_Method1 skaito tik pirmuosius 15 slaptažodžio baitų – tai specifikacijos riba. HotXLS tą lubas taiko raktui, o verifikatorių ir masyvą laiko ant įprastųjų pilno ilgio ir 16 baitų taisyklių, nuosekliai abiejose pusėse. Pats Excel šiam formatui atmeta ilgesnius kaip 15 simbolių slaptažodžius, tad laikykite 15 tikra riba
  • Simbolių rinkinys. HotXLS slaptažodį į baitus konvertuoja per sistemos ANSI kodavimo puslapį. Specifikacija aprašo kiekvieno UTF-16 simbolio žemo baito paėmimą, kas sutampa ASCII atveju. Neturėdami Excel pavyzdžių, apsaugotų ne ASCII slaptažodžiais, likusiai daliai tiesos šaltinio neturime, tad XOR failams laikykitės ASCII slaptažodžių

Skaitymo pusėje TXLSWorkbook.OnPassword leidžia paklausti slaptažodžio, kai Open sutinka FILEPASS įrašą. Įvykis yra TXLSPasswordEvent su var PassWord: WideString ir var Retry: Boolean; nustatykite Retry į True, kad bandytumėte dar kartą, iki trijų bandymų:

procedure TImportForm.WorkbookPassword(Sender: TObject;
  var PassWord: WideString; var Retry: Boolean);
var
  S: string;
begin
  S := '';
  Retry := InputQuery('Protected workbook', 'Password:', S);
  PassWord := S;
end;

procedure TImportForm.ImportLegacyFile(const FileName: string);
var
  Wb: IXLSWorkbook;
  Rc: Integer;
begin
  Wb := TXLSWorkbook.Create;
  Wb.OnPassword := WorkbookPassword;
  Rc := Wb.Open(FileName);
  // -1003: slaptažodis reikalingas, bet nepateiktas; -1005: netinkamas slaptažodis
  if Rc <> 1 then
    raise Exception.CreateFmt('Cannot open %s (code %d)', [FileName, Rc]);
  ShowMessage(VarToStr(Wb.Sheets[1].Range['A1', 'A1'].Value));
end;

Jei slaptažodį jau žinote, Open(FileName, APassWord) įvykio visai nepaliečia

Ar XOR užkodavimas ką nors apsaugo?

BIFF XOR užkodavimas nėra šifravimas ir nuo užsidegusio skaitytojo nepriegauna nieko. Slaptažodžio patikra yra 16 bitų verifikatorius, raktas – 16 bitų, o 16 baitų masyvas kartojasi per visą srautą, tad nuspėjamas BIFF įrašų turinys atidengia masyvo baitus be jokio slaptažodžio. HotXLS rašo XOR tik todėl, kad Excel 5.0/95 failai neturi kitos išeities, o gaminti tokius failus šiandien verta dėl seno vartotojo, kuris nesugeba skaityti nieko naujesnio

Klasikinis variklis schemą pasirenka per TXLSWorkbook.EncryptionType, o jos derinys su išsaugojimo formatu tikrinamas griežtai:

  • xletAuto (numatytasis) rašo RC4 CryptoAPI xlExcel97 ir XOR xlExcel5 – atitinkamai tai, ką pats Excel rašė kiekvienam formatui
  • xletXor galioja tik BIFF5; su xlExcel97 išsaugojimas meta išimtį vietoj tylio atsitraukimo
  • xletRC4 ir xletRC4CryptoAPI tikri tik BIFF8, o jų prašymas BIFF5 išsaugojime meta išimtį taip pat

RC4 irgi sensta, o jo suderinamumo detalės aprašytos straipsnyje kodėl Excel atmeta užšifruotą darbaknygę su teisingu slaptažodžiu. Jei gavėjas moka skaityti XLSX, naudokite XLSX variklį: TXLSXWorkbook.SaveAsEncryptedAgile rašo Agile Encryption (SHA-512 slaptažodžio maišymą su 100 000 iteracijų spin count ir AES-256-CBC) – formatą, kurį pagal numatymą rašo Excel 2010 ir naujesnės, o SaveAsEncrypted rašo senesnį AES-128 Standard Encryption. Abiejų kompromisus rasite straipsnyje XLSX failų šifravimas su AES Delphi programoje, o skaitymo pusę dengia Agile šifruotų Excel failų skaitymas su HotXLS

Trumpa atmintinė: BIFF XOR užkodavimas, kurį priima Excel 16

  • FILEPASS ($002F) seka po globals BOF; BIFF5 jo kūnas – 4 baitai: raktas, tada verifikatorius
  • Raktas = CreateXorKey_Method1(password) pagal [MS-OFFCRYPTO] §2.3.7.2, niekada atsitiktinis; slaptažodžiui secret jis $014D
  • Masyvas = slaptažodžio baitai + paketainė, XOR su žemu rakto baitu lyginėse pozicijose ir aukštu nelyginėse, tada XorRor (sukimas į dešinę 1)
  • Baito užkodavimas: sukimas į kairę 5, tada XOR; iškodavimas: XOR, tada sukimas į dešinę 5 (§2.3.7.3)
  • XorArrayIndex = (baito srauto poslinkis + įrašo duomenų ilgis) mod 16; antraštės ir grynatekstiai priešdėliai skaitosi į poslinkį
  • BOUNDSHEET palieka pirmuosius 4 baitus grynatekstius; BOF, FILEPASS ir INTERFACEHDR lieka visiškai grynatekščiai
  • Slaptažodžiai: ASCII, ne ilgesni kaip 15 simbolių
  • HotXLS: indeksas sutvarkytas v2.384.47, raktas ir XorRor – v2.384.54, senesni HotXLS XOR failai aptinkami pagal rakto nesutapimą
  • Tikrai apsaugai naudokite bent BIFF8 RC4 CryptoAPI, o geriau – XLSX Agile Encryption

HotXLS apdoroja BIFF5 ir BIFF8 slaptažodžių apsaugą, XLSX Standard ir Agile Encryption bei skaitymo pusės slaptažodžio atgalinį kvietimą iš vienos Delphi ir C++Builder bibliotekos, o aukščiau minėtos suderinamumo detalės sutvarkytos už jus. Leidimus, platformas ir bandomąją versiją rasite puslapyje HotXLS Delphi skaičiuoklės komponentas