Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove hardcoded crypto #449

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions src/main/java/net/lingala/zip4j/crypto/AESDecrypter.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@
package net.lingala.zip4j.crypto;

import net.lingala.zip4j.crypto.PBKDF2.MacBasedPRF;
import net.lingala.zip4j.crypto.engine.AESEngine;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.AESExtraDataRecord;
import net.lingala.zip4j.model.enums.AesKeyStrength;

import java.util.Arrays;

import javax.crypto.Cipher;

import static net.lingala.zip4j.crypto.AesCipherUtil.prepareBuffAESIVBytes;
import static net.lingala.zip4j.exception.ZipException.Type.WRONG_PASSWORD;
import static net.lingala.zip4j.util.InternalZipConstants.AES_BLOCK_SIZE;
Expand All @@ -33,35 +34,35 @@
*/
public class AESDecrypter implements Decrypter {

private AESEngine aesEngine;
private Cipher aes;
private MacBasedPRF mac;

private int nonce = 1;
private byte[] iv;
private byte[] counterBlock;

public AESDecrypter(AESExtraDataRecord aesExtraDataRecord, char[] password, byte[] salt,
byte[] passwordVerifier, boolean useUtf8ForPassword) throws ZipException {
byte[] passwordVerifier) throws ZipException {
iv = new byte[AES_BLOCK_SIZE];
counterBlock = new byte[AES_BLOCK_SIZE];
init(salt, passwordVerifier, password, aesExtraDataRecord, useUtf8ForPassword);
init(salt, passwordVerifier, password, aesExtraDataRecord);
}

private void init(byte[] salt, byte[] passwordVerifier, char[] password,
AESExtraDataRecord aesExtraDataRecord, boolean useUtf8ForPassword) throws ZipException {
AESExtraDataRecord aesExtraDataRecord) throws ZipException {

if (password == null || password.length <= 0) {
throw new ZipException("empty or null password provided for AES decryption", WRONG_PASSWORD);
}

final AesKeyStrength aesKeyStrength = aesExtraDataRecord.getAesKeyStrength();
final byte[] derivedKey = AesCipherUtil.derivePasswordBasedKey(salt, password, aesKeyStrength, useUtf8ForPassword);
final byte[] derivedKey = AesCipherUtil.derivePasswordBasedKey(salt, password, aesKeyStrength);
final byte[] derivedPasswordVerifier = AesCipherUtil.derivePasswordVerifier(derivedKey, aesKeyStrength);
if (!Arrays.equals(passwordVerifier, derivedPasswordVerifier)) {
throw new ZipException("Wrong Password", ZipException.Type.WRONG_PASSWORD);
}

aesEngine = AesCipherUtil.getAESEngine(derivedKey, aesKeyStrength);
aes = AesCipherUtil.getAESEngine(derivedKey, aesKeyStrength, Cipher.ENCRYPT_MODE);
mac = AesCipherUtil.getMacBasedPRF(derivedKey, aesKeyStrength);
}

Expand All @@ -74,7 +75,7 @@ public int decryptData(byte[] buff, int start, int len) throws ZipException {

mac.update(buff, j, loopCount);
prepareBuffAESIVBytes(iv, nonce);
aesEngine.processBlock(iv, counterBlock);
counterBlock = aes.update(iv);

for (int k = 0; k < loopCount; k++) {
buff[j + k] = (byte) (buff[j + k] ^ counterBlock[k]);
Expand Down
36 changes: 10 additions & 26 deletions src/main/java/net/lingala/zip4j/crypto/AESEncrypter.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@
package net.lingala.zip4j.crypto;

import net.lingala.zip4j.crypto.PBKDF2.MacBasedPRF;
import net.lingala.zip4j.crypto.engine.AESEngine;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.enums.AesKeyStrength;

import java.security.SecureRandom;

import javax.crypto.Cipher;

import static net.lingala.zip4j.crypto.AesCipherUtil.derivePasswordBasedKey;
import static net.lingala.zip4j.crypto.AesCipherUtil.derivePasswordVerifier;
import static net.lingala.zip4j.crypto.AesCipherUtil.getAESEngine;
Expand All @@ -35,7 +36,7 @@
*/
public class AESEncrypter implements Encrypter {

private AESEngine aesEngine;
private Cipher aes;
private MacBasedPRF mac;
private final SecureRandom random = new SecureRandom();

Expand All @@ -45,11 +46,10 @@ public class AESEncrypter implements Encrypter {
private int loopCount = 0;

private final byte[] iv;
private final byte[] counterBlock;
private byte[] derivedPasswordVerifier;
private byte[] saltBytes;

public AESEncrypter(char[] password, AesKeyStrength aesKeyStrength, boolean useUtf8ForPassword) throws ZipException {
public AESEncrypter(char[] password, AesKeyStrength aesKeyStrength) throws ZipException {
if (password == null || password.length == 0) {
throw new ZipException("input password is empty or null");
}
Expand All @@ -59,16 +59,15 @@ public AESEncrypter(char[] password, AesKeyStrength aesKeyStrength, boolean useU
}

this.finished = false;
counterBlock = new byte[AES_BLOCK_SIZE];
iv = new byte[AES_BLOCK_SIZE];
init(password, aesKeyStrength, useUtf8ForPassword);
init(password, aesKeyStrength);
}

private void init(char[] password, AesKeyStrength aesKeyStrength, boolean useUtf8ForPassword) throws ZipException {
private void init(char[] password, AesKeyStrength aesKeyStrength) throws ZipException {
saltBytes = generateSalt(aesKeyStrength.getSaltLength());
byte[] derivedKey = derivePasswordBasedKey(saltBytes, password, aesKeyStrength, useUtf8ForPassword);
byte[] derivedKey = derivePasswordBasedKey(saltBytes, password, aesKeyStrength);
derivedPasswordVerifier = derivePasswordVerifier(derivedKey, aesKeyStrength);
aesEngine = getAESEngine(derivedKey, aesKeyStrength);
aes = getAESEngine(derivedKey, aesKeyStrength, Cipher.ENCRYPT_MODE);
mac = getMacBasedPRF(derivedKey, aesKeyStrength);
}

Expand Down Expand Up @@ -97,7 +96,7 @@ public int encryptData(byte[] buff, int start, int len) throws ZipException {
AES_BLOCK_SIZE : ((start + len) - j);

prepareBuffAESIVBytes(iv, nonce);
aesEngine.processBlock(iv, counterBlock);
byte[] counterBlock = aes.update(iv);

for (int k = 0; k < loopCount; k++) {
buff[j + k] = (byte) (buff[j + k] ^ counterBlock[k]);
Expand All @@ -111,27 +110,12 @@ public int encryptData(byte[] buff, int start, int len) throws ZipException {
}

private byte[] generateSalt(int size) throws ZipException {

if (size != 8 && size != 16) {
throw new ZipException("invalid salt size, cannot generate salt");
}

int rounds;

if (size == 8) {
rounds = 2;
} else {
rounds = 4;
}

byte[] salt = new byte[size];
for (int j = 0; j < rounds; j++) {
int i = random.nextInt();
salt[j * 4] = (byte) (i >> 24);
salt[1 + j * 4] = (byte) (i >> 16);
salt[2 + j * 4] = (byte) (i >> 8);
salt[3 + j * 4] = (byte) i;
}
random.nextBytes(salt);
return salt;
}

Expand Down
44 changes: 30 additions & 14 deletions src/main/java/net/lingala/zip4j/crypto/AesCipherUtil.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package net.lingala.zip4j.crypto;

import net.lingala.zip4j.crypto.PBKDF2.MacBasedPRF;
import net.lingala.zip4j.crypto.PBKDF2.PBKDF2Engine;
import net.lingala.zip4j.crypto.PBKDF2.PBKDF2Parameters;
import net.lingala.zip4j.crypto.engine.AESEngine;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.enums.AesKeyStrength;

import static net.lingala.zip4j.util.InternalZipConstants.AES_HASH_CHARSET;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import static net.lingala.zip4j.util.InternalZipConstants.AES_HASH_ITERATIONS;
import static net.lingala.zip4j.util.InternalZipConstants.AES_MAC_ALGORITHM;
import static net.lingala.zip4j.util.InternalZipConstants.AES_PASSWORD_VERIFIER_LENGTH;
Expand All @@ -25,15 +29,18 @@ public class AesCipherUtil {
* @throws ZipException Thrown when Derived Key is not valid
*/
public static byte[] derivePasswordBasedKey(final byte[] salt, final char[] password,
final AesKeyStrength aesKeyStrength,
final boolean useUtf8ForPassword) throws ZipException {
final PBKDF2Parameters parameters = new PBKDF2Parameters(AES_MAC_ALGORITHM, AES_HASH_CHARSET, salt, AES_HASH_ITERATIONS);
final PBKDF2Engine engine = new PBKDF2Engine(parameters);

final AesKeyStrength aesKeyStrength) throws ZipException {
final int keyLength = aesKeyStrength.getKeyLength();
final int macLength = aesKeyStrength.getMacLength();
final int derivedKeyLength = keyLength + macLength + AES_PASSWORD_VERIFIER_LENGTH;
final byte[] derivedKey = engine.deriveKey(password, derivedKeyLength, useUtf8ForPassword);
final byte[] derivedKey;
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2With" + AES_MAC_ALGORITHM);
KeySpec keyspec = new PBEKeySpec(password, salt, AES_HASH_ITERATIONS, derivedKeyLength * 8);
derivedKey = factory.generateSecret(keyspec).getEncoded();
} catch (Exception e) {
throw new ZipException("Failed to derive zip key.");
}
if (derivedKey != null && derivedKey.length == derivedKeyLength) {
return derivedKey;
} else {
Expand Down Expand Up @@ -77,14 +84,23 @@ public static MacBasedPRF getMacBasedPRF(final byte[] derivedKey, final AesKeySt
*
* @param derivedKey Derived Key
* @param aesKeyStrength AES Key Strength
* @param opmode the operation mode of this cipher (this is one of
* the following:
* {@code ENCRYPT_MODE}, {@code DECRYPT_MODE},
* {@code WRAP_MODE} or {@code UNWRAP_MODE})
* @return AES Engine configured with AES Key
* @throws ZipException Thrown on AESEngine initialization failures
*/
public static AESEngine getAESEngine(final byte[] derivedKey, final AesKeyStrength aesKeyStrength) throws ZipException {
public static Cipher getAESEngine(final byte[] derivedKey, final AesKeyStrength aesKeyStrength, int optMode) throws ZipException {
final int keyLength = aesKeyStrength.getKeyLength();
final byte[] aesKey = new byte[keyLength];
System.arraycopy(derivedKey, START_INDEX, aesKey, START_INDEX, keyLength);
return new AESEngine(aesKey);
try {
final SecretKey aesKey = new SecretKeySpec(derivedKey, START_INDEX, keyLength, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(optMode, aesKey);
return cipher;
} catch (Exception e) {
throw new ZipException("Failed to init cipher.", e);
}
}

public static void prepareBuffAESIVBytes(byte[] buff, int nonce) {
Expand Down
133 changes: 0 additions & 133 deletions src/main/java/net/lingala/zip4j/crypto/PBKDF2/PBKDF2Engine.java

This file was deleted.

Loading