What's the Zip Strong Encryption Specification/SecureZip key derivation function?

251 views Asked by At

In the (PK)ZIP specification at https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT, specifically in the Strong Encryption Specification (SES) section, there is a line on deriving a key from a password:

MasterSessionKey = DeriveKey(SHA1(Password)) 

What's DeriveKey?

(In WinZip's AES documentation at https://www.winzip.com/en/support/aes-encryption/, they use PBKDF2 with 1000 iterations. I don't see any similar explanation in APPNOTE)

2

There are 2 answers

0
bixarrio On

The next paragraph 'defines' it

7.2.5.3 The function names and parameter requirements will depend on the choice of the cryptographic toolkit selected. Almost any toolkit supporting the reference implementations for each algorithm can be used. The RSA BSAFE(r), OpenSSL, and Microsoft CryptoAPI libraries are all known to work well.

I guess it's up to you to decide which of the encryption algorithms you want to use and go from there

5
Oleg Cherednik On

PKWARE implemented a strong encryption in version 5, but did not provide the algorithm of encoding/decoding (Method For Strongly Encrypted .ZIP Files - Patent US 2020/0250329 A1). In this algorithm AES encryption was implemented as part of it. You can define this by strong encryption (bit 6) = yes in General Purpose Flag.

After that WinZip could not use this algo, so it invented another one. You can define this by strong encryption (bit 6) = no in General Purpose Flag and AesExtraFieldRecord with signature 0x990.

As you can see there're two ways to encrypt a zip file. All open source software use the second one. The first one is available only by PKWARE SecureZIP

You can find example of this alogirthm in (7zip) Strong.cpp:35. In java it should look like this:

public static byte[] getMasterKey(String password) {
    byte[] data = password.getBytes(StandardCharsets.UTF_8);
    byte[] sha1 = DigestUtils.sha1(data);
    return DeriveKey(sha1);
}

private static byte[] DeriveKey(byte[] digest) {
    byte[] buf = new byte[kDigestSize * 2];  // kDigestSize = 20
    DeriveKey2(digest, (byte)0x36, buf, 0);
    DeriveKey2(digest, (byte)0x5C, buf, kDigestSize);
    return Arrays.copyOfRange(buf, 0, 32);
}

private static void DeriveKey2(byte[] digest, byte c, byte[] dest, int offs) {
    byte[] buf = new byte[64];
    Arrays.fill(buf, c);

    for (int i = 0; i < kDigestSize; i++)
        buf[i] ^= digest[i];

    byte[] sha1 = DigestUtils.sha1(buf);
    System.arraycopy(sha1, 0, dest, offs, sha1.length);
}

Demo:

String password = "JohnDoe";
byte[] masterKey = getMasterKey(password);