Skip to content

Commit

Permalink
#103 Avoid array when ECD
Browse files Browse the repository at this point in the history
  • Loading branch information
oleg-cherednik committed Nov 21, 2024
1 parent d1cf7eb commit de0b213
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.io.IOException;
import javax.crypto.Cipher;

/**
Expand All @@ -25,31 +26,31 @@ public final class AesCentralDirectoryDecoder implements Decoder {
public static AesCentralDirectoryDecoder create128(DecryptionHeader decryptionHeader,
char[] password,
long compressedSize,
ByteOrder byteOrder) {
ByteOrder byteOrder) throws IOException {
return create(decryptionHeader, password, AesStrength.S128, compressedSize, byteOrder);
}

@SuppressWarnings("NewMethodNamingConvention")
public static AesCentralDirectoryDecoder create192(DecryptionHeader decryptionHeader,
char[] password,
long compressedSize,
ByteOrder byteOrder) {
ByteOrder byteOrder) throws IOException {
return create(decryptionHeader, password, AesStrength.S192, compressedSize, byteOrder);
}

@SuppressWarnings("NewMethodNamingConvention")
public static AesCentralDirectoryDecoder create256(DecryptionHeader decryptionHeader,
char[] password,
long compressedSize,
ByteOrder byteOrder) {
ByteOrder byteOrder) throws IOException {
return create(decryptionHeader, password, AesStrength.S256, compressedSize, byteOrder);
}

private static AesCentralDirectoryDecoder create(DecryptionHeader decryptionHeader,
char[] password,
AesStrength strength,
long compressedSize,
ByteOrder byteOrder) {
ByteOrder byteOrder) throws IOException {
Cipher cipher = AesStrongEngine.createCipher(decryptionHeader, password, strength, byteOrder);
AesStrongEngine engine = new AesStrongEngine(cipher);
return new AesCentralDirectoryDecoder(engine, compressedSize);
Expand Down
28 changes: 13 additions & 15 deletions src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,34 +50,32 @@ public final class AesDecoder implements Decoder {
private final long compressedSize;

@SuppressWarnings("NewMethodNamingConvention")
public static AesDecoder create128(ZipEntry zipEntry, DataInput in) {
public static AesDecoder create128(ZipEntry zipEntry, DataInput in) throws IOException {
return create(zipEntry, AesStrength.S128, in);
}

@SuppressWarnings("NewMethodNamingConvention")
public static AesDecoder create192(ZipEntry zipEntry, DataInput in) {
public static AesDecoder create192(ZipEntry zipEntry, DataInput in) throws IOException {
return create(zipEntry, AesStrength.S192, in);
}

@SuppressWarnings("NewMethodNamingConvention")
public static AesDecoder create256(ZipEntry zipEntry, DataInput in) {
public static AesDecoder create256(ZipEntry zipEntry, DataInput in) throws IOException {
return create(zipEntry, AesStrength.S256, in);
}

private static AesDecoder create(ZipEntry zipEntry, AesStrength strength, DataInput in) {
return Quietly.doQuietly(() -> {
byte[] salt = in.readBytes(strength.getSaltSize());
byte[] key = AesEngine.createKey(zipEntry.getPassword(), salt, strength);
private static AesDecoder create(ZipEntry zipEntry, AesStrength strength, DataInput in) throws IOException {
byte[] salt = in.readBytes(strength.getSaltSize());
byte[] key = AesEngine.createKey(zipEntry.getPassword(), salt, strength);

Cipher cipher = AesEngine.createCipher(strength.createSecretKeyForCipher(key));
byte[] passwordChecksum = strength.createPasswordChecksum(key);
checkPasswordChecksum(passwordChecksum, zipEntry, in);
Cipher cipher = AesEngine.createCipher(strength.createSecretKeyForCipher(key));
byte[] passwordChecksum = strength.createPasswordChecksum(key);
checkPasswordChecksum(passwordChecksum, zipEntry, in);

Mac mac = AesEngine.createMac(strength.createSecretKeyForMac(key));
AesEngine engine = new AesEngine(cipher, mac);
long compressedSize = AesEngine.getDataCompressedSize(zipEntry.getCompressedSize(), strength);
return new AesDecoder(engine, compressedSize);
});
Mac mac = AesEngine.createMac(strength.createSecretKeyForMac(key));
AesEngine engine = new AesEngine(cipher, mac);
long compressedSize = AesEngine.getDataCompressedSize(zipEntry.getCompressedSize(), strength);
return new AesDecoder(engine, compressedSize);
}

// ---------- Decrypt ----------
Expand Down
38 changes: 19 additions & 19 deletions src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,9 @@
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.PBEKeySpec;
Expand Down Expand Up @@ -121,25 +117,29 @@ public byte[] getMac() {
return mac.doFinal();
}

public static byte[] createKey(char[] password, byte[] salt, AesStrength strength)
throws NoSuchAlgorithmException, InvalidKeySpecException {
int keyLength = strength.getSize() * 2 + 16;
KeySpec keySpec = new PBEKeySpec(password, salt, ITERATION_COUNT, keyLength);
return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(keySpec).getEncoded();
public static byte[] createKey(char[] password, byte[] salt, AesStrength strength) {
return Quietly.doQuietly(() -> {
int keyLength = strength.getSize() * 2 + 16;
KeySpec keySpec = new PBEKeySpec(password, salt, ITERATION_COUNT, keyLength);
return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(keySpec).getEncoded();
});
}

public static Cipher createCipher(SecretKeySpec secretKeySpec)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
// use custom AES implementation, so no worry for DECRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
return cipher;
public static Cipher createCipher(SecretKeySpec secretKeySpec) {
return Quietly.doQuietly(() -> {
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
// use custom AES implementation, so no worry for DECRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
return cipher;
});
}

public static Mac createMac(SecretKeySpec secretKeySpec) throws NoSuchAlgorithmException, InvalidKeyException {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(secretKeySpec);
return mac;
public static Mac createMac(SecretKeySpec secretKeySpec) {
return Quietly.doQuietly(() -> {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(secretKeySpec);
return mac;
});
}

public static AesStrength getStrength(EncryptionMethod encryptionMethod) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,28 +50,26 @@ public final class AesStrongDecoder implements Decoder {
private long decryptedBytes;

@SuppressWarnings("NewMethodNamingConvention")
public static AesStrongDecoder create128(ZipEntry zipEntry, DataInput in) {
public static AesStrongDecoder create128(ZipEntry zipEntry, DataInput in) throws IOException {
return create(zipEntry, AesStrength.S128, in);
}

@SuppressWarnings("NewMethodNamingConvention")
public static AesStrongDecoder create192(ZipEntry zipEntry, DataInput in) {
public static AesStrongDecoder create192(ZipEntry zipEntry, DataInput in) throws IOException {
return create(zipEntry, AesStrength.S192, in);
}

@SuppressWarnings("NewMethodNamingConvention")
public static AesStrongDecoder create256(ZipEntry zipEntry, DataInput in) {
public static AesStrongDecoder create256(ZipEntry zipEntry, DataInput in) throws IOException {
return create(zipEntry, AesStrength.S256, in);
}

public static AesStrongDecoder create(ZipEntry zipEntry, AesStrength strength, DataInput in) {
return Quietly.doQuietly(() -> {
in.mark(DECRYPTION_HEADER);
Cipher cipher = createCipher(zipEntry, strength, in);
int decryptionHeaderSize = (int) in.getMarkSize(DECRYPTION_HEADER);
long compressedSize = zipEntry.getCompressedSize() - decryptionHeaderSize;
return new AesStrongDecoder(cipher, compressedSize);
});
public static AesStrongDecoder create(ZipEntry zipEntry, AesStrength strength, DataInput in) throws IOException {
in.mark(DECRYPTION_HEADER);
Cipher cipher = createCipher(zipEntry, strength, in);
int decryptionHeaderSize = (int) in.getMarkSize(DECRYPTION_HEADER);
long compressedSize = zipEntry.getCompressedSize() - decryptionHeaderSize;
return new AesStrongDecoder(cipher, compressedSize);
}

private static Cipher createCipher(ZipEntry zipEntry, AesStrength strength, DataInput in) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.io.IOException;
import java.util.Optional;
import javax.crypto.Cipher;

Expand Down Expand Up @@ -76,7 +77,7 @@ public enum EncryptionAlgorithm {
public Decoder createEcdDecoder(DecryptionHeader decryptionHeader,
char[] password,
long compressedSize,
ByteOrder byteOrder) {
ByteOrder byteOrder) throws IOException {
return Optional.ofNullable(ecdDecoderFactory)
.orElseThrow(() -> new EncryptionNotSupportedException(this))
.create(decryptionHeader, password, compressedSize, byteOrder);
Expand All @@ -98,7 +99,8 @@ public static EncryptionAlgorithm parseCode(int code) {

private interface DecoderFactory {

Decoder create(DecryptionHeader decryptionHeader, char[] password, long compressedSize, ByteOrder byteOrder);
Decoder create(DecryptionHeader decryptionHeader, char[] password, long compressedSize, ByteOrder byteOrder)
throws IOException;

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class EncryptedDataInput extends BaseDataInput {
protected final Decoder decoder;
protected long available;

public static EncryptedDataInput create(ZipEntry zipEntry, DataInput in) {
public static EncryptedDataInput create(ZipEntry zipEntry, DataInput in) throws IOException {
Decoder decoder = zipEntry.createDecoder(in);
int batchSize = Math.max(0, decoder.getBlockSize());
long encryptedSize = decoder == Decoder.NULL ? zipEntry.getCompressedSize() : decoder.getCompressedSize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;

import java.io.IOException;
import java.util.Optional;
import java.util.function.Function;

Expand Down Expand Up @@ -73,7 +74,7 @@ public Encoder createEncoder(ZipEntry zipEntry) {
.apply(zipEntry);
}

public Decoder createDecoder(ZipEntry zipEntry, DataInput in) {
public Decoder createDecoder(ZipEntry zipEntry, DataInput in) throws IOException {
return Optional.ofNullable(decoderFactory)
.orElseThrow(() -> new EncryptionNotSupportedException(this))
.create(zipEntry, in);
Expand Down Expand Up @@ -103,7 +104,7 @@ public static EncryptionMethod get(PkwareExtraField extraField, GeneralPurposeFl

private interface DecoderFactory {

Decoder create(ZipEntry zipEntry, DataInput in);
Decoder create(ZipEntry zipEntry, DataInput in) throws IOException;

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import lombok.Getter;
import lombok.Setter;

import java.io.IOException;

/**
* @author Oleg Cherednik
* @since 26.07.2019
Expand Down Expand Up @@ -61,7 +63,7 @@ final class RegularFileZipEntry extends ZipEntry {
}

@Override
public Decoder createDecoder(DataInput in) {
public Decoder createDecoder(DataInput in) throws IOException {
return encryptionMethod.createDecoder(this, in);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public ZipFile.Entry createImmutableEntry() {
externalFileAttributes);
}

public Decoder createDecoder(DataInput in) {
public Decoder createDecoder(DataInput in) throws IOException {
return Decoder.NULL;
}

Expand Down

0 comments on commit de0b213

Please sign in to comment.