WinZip AE-2 아카이브 암호화

WinZip AE-1과 AE-2는 원래의 취약한 PKZIP 2.0 스트림 암호를 대체하여 .zip 아카이브 내부에서 사용되는 AES 암호화 방식입니다. 모든 함수는 sgcCrypto_Zip_AE2에 들어 있으며 한 번에 하나의 아카이브 항목을 대상으로 동작합니다. 주변의 .zip 컨테이너 자체를 만드는 것은 이 유닛의 범위를 벗어납니다.

 

레이아웃

 

키 소재는 비밀번호에 대한 PBKDF2-HMAC-SHA1(1000회 반복)에서 나오며, 한 번의 호출로 암호화 키, 인증 키, 2바이트 비밀번호 검증자를 생성합니다. 솔트 길이와 키 길이는 모두 해당 항목에 선택된 AES 강도에 따라 달라집니다.

 


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

 

항목 암호화 및 복호화

 


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;

 

2바이트 검증자는 대부분의 잘못된 비밀번호를 즉시 걸러내며, aAuthCode가 담고 있는 10바이트 HMAC-SHA1 인증 코드가 나머지를 걸러내면서 암호화된 내용에 대한 변조도 함께 잡아냅니다.

 

AE-2가 보호하지 않는 것

 

인증 코드는 오직 한 항목의 파일 내용만 다룹니다. 파일 이름이나 타임스탬프, 아카이브 레이아웃은 다루지 않으므로, 내용을 읽을 수 없는 공격자라도 아카이브 안의 항목을 탐지되지 않고 이름을 바꾸거나 순서를 바꾸거나 삭제할 수 있습니다. 또한 약한 비밀번호로부터도 보호하지 않습니다: 키 유도는 이 형식이 Argon2id나 scrypt보다 먼저 만들어졌기 때문에 훨씬 적은 1000번의 PBKDF2 반복만 사용합니다. AE-2 아카이브는 오직 그것에 선택된 비밀번호만큼만 강합니다.

 

추가 필드

 

.zip 항목은 압축 방식을 99로 설정하고 작은 추가 필드(헤더 ID 0x9901)를 더해 자신이 AE로 암호화되었음을 표시합니다. 이 필드는 그 아래의 실제 압축 방식, AE 버전(1 또는 2), AES 강도를 기록하며, 이는 리더가 비밀번호를 시도하기 전에 필요한 정보입니다.

 


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;

 

두 개의 편의 함수가 로컬 파일 헤더 뒤에 오는 솔트 / 검증자 / 암호문 / 인증 코드 레이아웃을 묶고 풀어냅니다. 이는 암호화 부분만이 아니라 전체 항목을 조립하거나 읽어야 하는 호출자를 위한 것입니다:

 


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;