Cifratura degli archivi WinZip AE-2

WinZip AE-1 e AE-2 sono gli schemi di cifratura AES utilizzati all'interno degli archivi .zip, in sostituzione del debole stream cipher originale PKZIP 2.0. Ogni funzione si trova in sgcCrypto_Zip_AE2 e opera su una singola voce d'archivio alla volta; la costruzione del contenitore .zip vero e proprio esula dall'ambito di questa unit.

Struttura

Il materiale chiave proviene da PBKDF2-HMAC-SHA1 applicato alla password, 1000 iterazioni, producendo in un'unica chiamata una chiave di cifratura, una chiave di autenticazione e un verificatore di password di 2 byte. Sia la lunghezza del salt che la lunghezza della chiave dipendono dalla robustezza AES scelta per la voce.


function sgcZipAE2_SaltLength(aStrength: TsgcZipAESStrength): Integer;
function sgcZipAE2_KeyLength(aStrength: TsgcZipAESStrength): Integer;
procedure sgcZipAE2_DeriveKeys(const aPassword, aSalt: TBytes; aStrength: TsgcZipAESStrength;
  out aEncKey, aAuthKey, aVerifier: TBytes);

Cifratura e decifratura di una voce


function sgcZipAE2_Encrypt(const aPassword, aSalt, aPlain: TBytes; aStrength: TsgcZipAESStrength;
  out aVerifier, aAuthCode: TBytes): TBytes;
function sgcZipAE2_Decrypt(const aPassword, aSalt, aCipher, aVerifier, aAuthCode: TBytes;
  aStrength: TsgcZipAESStrength; out aPlain: TBytes): Boolean;

var
  oSalt, oCipher, oVerifier, oAuth, oPlain: TBytes;
begin
  oSalt := sgcRandomBytes(sgcZipAE2_SaltLength(zasAES256));
  oCipher := sgcZipAE2_Encrypt(oPassword, oSalt, oPlain, zasAES256, oVerifier, oAuth);
  if sgcZipAE2_Decrypt(oPassword, oSalt, oCipher, oVerifier, oAuth, zasAES256, oPlain) then
    { correct password, untampered data };
end;

Il verificatore di 2 byte individua immediatamente la maggior parte delle password errate, e il codice di autenticazione HMAC-SHA1 di 10 byte trasportato da aAuthCode individua il resto, insieme a qualsiasi manomissione del contenuto cifrato.

Cosa AE-2 non protegge

Il codice di autenticazione copre solo il contenuto del file di una singola voce. Non copre il nome del file, i timestamp o la struttura dell'archivio, quindi un attaccante che non può leggere il contenuto può comunque rinominare, riordinare o eliminare voci nell'archivio senza essere rilevato. Inoltre non protegge da una password debole: la derivazione della chiave utilizza solo 1000 round PBKDF2, molti meno di quelli che userebbero Argon2id o scrypt, perché il formato è precedente a essi. Un archivio AE-2 è robusto solo quanto la password scelta per esso.

Il campo extra

Una voce .zip si contrassegna come cifrata AE impostando il proprio metodo di compressione a 99 e aggiungendo un piccolo campo extra (header ID 0x9901) che registra il metodo di compressione reale sottostante, la versione AE (1 o 2) e la robustezza AES, informazioni di cui un lettore ha bisogno prima ancora di poter tentare una password.


function sgcZipAE2_BuildExtraField(aStrength: TsgcZipAESStrength; aCompressionMethod: Word;
  aVersion: TsgcZipAEVersion = zavAE2): TBytes;
function sgcZipAE2_ParseExtraField(const aData: TBytes; out aStrength: TsgcZipAESStrength;
  out aCompressionMethod: Word; out aVersion: TsgcZipAEVersion): Boolean;

Due funzioni di convenienza impacchettano e spacchettano la struttura salt / verificatore / ciphertext / codice di autenticazione che segue l'header del file locale, per i chiamanti che assemblano o leggono un'intera voce anziché solo la sua parte crittografica:


function sgcZipAE2_PackEntry(const aSalt, aVerifier, aCipher, aAuthCode: TBytes): TBytes;
function sgcZipAE2_UnpackEntry(const aEntry: TBytes; aStrength: TsgcZipAESStrength;
  out aSalt, aVerifier, aCipher, aAuthCode: TBytes): Boolean;