WinZip AE-1 et AE-2 sont les schémas de chiffrement AES utilisés à l'intérieur des archives .zip, remplaçant le chiffrement de flux PKZIP 2.0 d'origine, faible. Chaque fonction se trouve dans sgcCrypto_Zip_AE2 et opère sur une entrée d'archive à la fois ; la construction du conteneur .zip lui-même est hors du périmètre de cette unité.
Structure
Le matériel de clé provient de PBKDF2-HMAC-SHA1 appliqué au mot de passe, 1000 itérations, produisant une clé de chiffrement, une clé d'authentification et un vérificateur de mot de passe de 2 octets en un seul appel. La longueur du sel et la longueur de la clé dépendent toutes deux de la force AES choisie pour l'entrée.
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);
Chiffrer et déchiffrer une entrée
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;
Le vérificateur de 2 octets détecte immédiatement la plupart des mots de passe erronés, et le code d'authentification HMAC-SHA1 de 10 octets que porte aAuthCode détecte le reste, ainsi que toute altération du contenu chiffré.
Ce qu'AE-2 ne protège pas
Le code d'authentification ne couvre que le contenu du fichier d'une seule entrée. Il ne couvre ni le nom du fichier, ni les horodatages, ni la structure de l'archive, de sorte qu'un attaquant incapable de lire le contenu peut néanmoins renommer, réordonner ou supprimer des entrées dans l'archive sans être détecté. Cela ne protège pas non plus contre un mot de passe faible : la dérivation de clé n'utilise que 1000 tours de PBKDF2, bien moins qu'Argon2id ou scrypt n'en utiliseraient, car le format est antérieur à ceux-ci. Une archive AE-2 n'est robuste qu'à hauteur du mot de passe choisi pour elle.
Le champ supplémentaire
Une entrée .zip se marque comme chiffrée AE en réglant sa méthode de compression sur 99 et en ajoutant un petit champ supplémentaire (ID d'en-tête 0x9901) qui enregistre la méthode de compression réelle sous-jacente, la version AE (1 ou 2) et la force AES, ce dont un lecteur a besoin avant même de pouvoir tenter un mot de passe.
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;
Deux fonctions utilitaires empaquettent et dépaquettent la structure sel / vérificateur / texte chiffré / code d'authentification qui suit l'en-tête de fichier local, pour les appelants qui assemblent ou lisent une entrée complète plutôt que sa seule cryptographie :
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;