Criptografia de Arquivos WinZip AE-2

WinZip AE-1 e AE-2 são os esquemas de criptografia AES utilizados dentro de arquivos .zip, substituindo a cifra de fluxo original e fraca do PKZIP 2.0. Toda função está em sgcCrypto_Zip_AE2 e opera sobre uma entrada do arquivo por vez; construir o container .zip ao redor está fora do escopo desta unit.

 

Layout

 

O material de chave vem do PBKDF2-HMAC-SHA1 sobre a senha, 1000 iterações, produzindo uma chave de criptografia, uma chave de autenticação e um verificador de senha de 2 bytes em uma única chamada. Tanto o tamanho do salt quanto o tamanho da chave dependem da força de AES escolhida para a 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);

 

Criptografando e descriptografando uma 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;

 

O verificador de 2 bytes detecta a maioria das senhas erradas imediatamente, e o código de autenticação HMAC-SHA1 de 10 bytes que aAuthCode carrega detecta o restante, junto com qualquer adulteração do conteúdo criptografado.

 

O que o AE-2 não protege

 

O código de autenticação cobre apenas o conteúdo do arquivo de uma entrada. Ele não cobre o nome do arquivo, os timestamps, ou o layout do archive, de modo que um atacante que não consegue ler o conteúdo ainda assim pode renomear, reordenar ou remover entradas do archive sem ser detectado. Ele também não protege contra uma senha fraca: a derivação de chave usa apenas 1000 rounds de PBKDF2, muito menos que o Argon2id ou o scrypt usariam, porque o formato é anterior a eles. Um archive AE-2 é tão forte quanto a senha escolhida para ele.

 

O campo extra

 

Uma entrada .zip se marca como criptografada com AE definindo seu método de compressão como 99 e adicionando um pequeno campo extra (ID de cabeçalho 0x9901) que registra o método de compressão real por baixo, a versão do AE (1 ou 2) e a força do AES, que é o que um leitor precisa antes mesmo de tentar uma senha.

 


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;

 

Duas funções de conveniência empacotam e desempacotam o layout de salt / verificador / texto cifrado / código de autenticação que segue o cabeçalho de arquivo local, para chamadores que montam ou leem uma entrada completa em vez de apenas sua criptografia:

 


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;