WinZip AE-2 Archivverschlüsselung

WinZip AE-1 und AE-2 sind die innerhalb von .zip-Archiven verwendeten AES-Verschlüsselungsverfahren, die den ursprünglichen, schwachen PKZIP-2.0-Stream-Chiffre ersetzen. Jede Funktion befindet sich in sgcCrypto_Zip_AE2 und arbeitet jeweils an einem Archiveintrag; das Erstellen des umgebenden .zip-Containers selbst liegt außerhalb des Umfangs dieser Unit.

 

Aufbau

 

Das Schlüsselmaterial stammt aus PBKDF2-HMAC-SHA1 über das Passwort, 1000 Iterationen, und erzeugt in einem Aufruf einen Verschlüsselungsschlüssel, einen Authentifizierungsschlüssel und einen 2-Byte-Passwort-Verifier. Sowohl die Salt-Länge als auch die Schlüssellänge hängen von der für den Eintrag gewählten AES-Stärke ab.

 


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);

 

Einen Eintrag verschlüsseln und entschlüsseln

 


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;

 

Der 2-Byte-Verifier erkennt die meisten falschen Passwörter sofort, und der 10-Byte-HMAC-SHA1-Authentifizierungscode, den aAuthCode trägt, erkennt den Rest, zusammen mit jeder Manipulation am verschlüsselten Inhalt.

 

Was AE-2 nicht schützt

 

Der Authentifizierungscode deckt nur den Dateiinhalt eines Eintrags ab. Er deckt nicht den Dateinamen, die Zeitstempel oder das Archivlayout ab, sodass ein Angreifer, der den Inhalt nicht lesen kann, dennoch Einträge im Archiv umbenennen, umsortieren oder entfernen kann, ohne dass dies erkannt wird. Er schützt auch nicht vor einem schwachen Passwort: Die Schlüsselableitung verwendet nur 1000 PBKDF2-Runden, weit weniger als Argon2id oder scrypt verwenden würden, weil das Format älter ist als diese. Ein AE-2-Archiv ist nur so stark wie das dafür gewählte Passwort.

 

Das Extra-Feld

 

Ein .zip-Eintrag markiert sich selbst als AE-verschlüsselt, indem er seine Kompressionsmethode auf 99 setzt und ein kleines Extra-Feld (Header-ID 0x9901) hinzufügt, das die tatsächliche darunterliegende Kompressionsmethode, die AE-Version (1 oder 2) und die AES-Stärke festhält, was ein Leser benötigt, bevor er überhaupt ein Passwort versuchen kann.

 


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;

 

Zwei Hilfsfunktionen packen und entpacken das Salt-/Verifier-/Chiffretext-/Authentifizierungscode-Layout, das dem lokalen Dateiheader folgt, für Aufrufer, die einen vollständigen Eintrag zusammensetzen oder lesen, statt nur seine Kryptografie:

 


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;