Cifrado de Archivos WinZip AE-2

WinZip AE-1 y AE-2 son los esquemas de cifrado AES usados dentro de los archivos .zip, en sustitución del cifrado de flujo PKZIP 2.0 original, que es débil. Cada función está en sgcCrypto_Zip_AE2 y trabaja sobre una entrada del archivo cada vez; construir el propio contenedor .zip que la rodea queda fuera del alcance de esta unidad.

 

Estructura

 

El material de clave procede de PBKDF2-HMAC-SHA1 sobre la contraseña, 1000 iteraciones, produciendo una clave de cifrado, una clave de autenticación y un verificador de contraseña de 2 bytes en una sola llamada. Tanto la longitud del salt como la longitud de la clave dependen de la fortaleza de AES elegida para la entrada.

 


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

 

Cifrar y descifrar una entrada

 


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;

 

El verificador de 2 bytes detecta la mayoría de las contraseñas incorrectas de inmediato, y el código de autenticación HMAC-SHA1 de 10 bytes que lleva aAuthCode detecta el resto, junto con cualquier manipulación del contenido cifrado.

 

Qué no protege AE-2

 

El código de autenticación cubre únicamente el contenido del archivo de una entrada. No cubre el nombre del archivo, las marcas de tiempo ni la disposición del archivo, de modo que un atacante que no puede leer el contenido aún puede renombrar, reordenar o eliminar entradas del archivo sin que se detecte. Tampoco protege frente a una contraseña débil: la derivación de claves usa solo 1000 rondas de PBKDF2, muchas menos que las que usarían Argon2id o scrypt, porque el formato es anterior a ellos. Un archivo AE-2 es tan fuerte como la contraseña elegida para él.

 

El campo adicional

 

Una entrada .zip se marca a sí misma como cifrada con AE estableciendo su método de compresión a 99 y añadiendo un pequeño campo adicional (ID de cabecera 0x9901) que registra el método de compresión real subyacente, la versión AE (1 o 2) y la fortaleza de AES, que es lo que necesita un lector antes de poder siquiera intentar una contraseña.

 


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;

 

Dos funciones auxiliares empaquetan y desempaquetan la disposición salt / verificador / texto cifrado / código de autenticación que sigue a la cabecera de archivo local, para quienes ensamblan o leen una entrada completa en lugar de solo su criptografía:

 


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;