/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto; import java.util.Arrays; 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.exception.ZipExceptionConstants; import net.lingala.zip4j.model.AESExtraDataRecord; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.util.InternalZipConstants; import net.lingala.zip4j.util.Raw; import net.lingala.zip4j.util.Zip4jConstants; public class AESDecrypter implements IDecrypter { private LocalFileHeader localFileHeader; private AESEngine aesEngine; private MacBasedPRF mac; private final int PASSWORD_VERIFIER_LENGTH = 2; private int KEY_LENGTH; private int MAC_LENGTH; private int SALT_LENGTH; private byte[] aesKey; private byte[] macKey; private byte[] derivedPasswordVerifier; private byte[] storedMac; private int nonce = 1; private byte[] iv; private byte[] counterBlock; private int loopCount = 0; public AESDecrypter(LocalFileHeader localFileHeader, byte[] salt, byte[] passwordVerifier) throws ZipException { if (localFileHeader == null) { throw new ZipException("one of the input parameters is null in AESDecryptor Constructor"); } this.localFileHeader = localFileHeader; this.storedMac = null; iv = new byte[InternalZipConstants.AES_BLOCK_SIZE]; counterBlock = new byte[InternalZipConstants.AES_BLOCK_SIZE]; init(salt, passwordVerifier); } private void init(byte[] salt, byte[] passwordVerifier) throws ZipException { if (localFileHeader == null) { throw new ZipException("invalid file header in init method of AESDecryptor"); } AESExtraDataRecord aesExtraDataRecord = localFileHeader.getAesExtraDataRecord(); if (aesExtraDataRecord == null) { throw new ZipException("invalid aes extra data record - in init method of AESDecryptor"); } switch (aesExtraDataRecord.getAesStrength()) { case Zip4jConstants.AES_STRENGTH_128: KEY_LENGTH = 16; MAC_LENGTH = 16; SALT_LENGTH = 8; break; case Zip4jConstants.AES_STRENGTH_192: KEY_LENGTH = 24; MAC_LENGTH = 24; SALT_LENGTH = 12; break; case Zip4jConstants.AES_STRENGTH_256: KEY_LENGTH = 32; MAC_LENGTH = 32; SALT_LENGTH = 16; break; default: throw new ZipException("invalid aes key strength for file: " + localFileHeader.getFileName()); } if (localFileHeader.getPassword() == null || localFileHeader.getPassword().length <= 0) { throw new ZipException("empty or null password provided for AES Decryptor"); } byte[] derivedKey = deriveKey(salt, localFileHeader.getPassword()); if (derivedKey == null || derivedKey.length != (KEY_LENGTH + MAC_LENGTH + PASSWORD_VERIFIER_LENGTH)) { throw new ZipException("invalid derived key"); } aesKey = new byte[KEY_LENGTH]; macKey = new byte[MAC_LENGTH]; derivedPasswordVerifier = new byte[PASSWORD_VERIFIER_LENGTH]; System.arraycopy(derivedKey, 0, aesKey, 0, KEY_LENGTH); System.arraycopy(derivedKey, KEY_LENGTH, macKey, 0, MAC_LENGTH); System.arraycopy(derivedKey, KEY_LENGTH + MAC_LENGTH, derivedPasswordVerifier, 0, PASSWORD_VERIFIER_LENGTH); if (derivedPasswordVerifier == null) { throw new ZipException("invalid derived password verifier for AES"); } if (!Arrays.equals(passwordVerifier, derivedPasswordVerifier)) { throw new ZipException("Wrong Password for file: " + localFileHeader.getFileName(), ZipExceptionConstants.WRONG_PASSWORD); } aesEngine = new AESEngine(aesKey); mac = new MacBasedPRF("HmacSHA1"); mac.init(macKey); } public int decryptData(byte[] buff, int start, int len) throws ZipException { if (aesEngine == null) { throw new ZipException("AES not initialized properly"); } try { for (int j = start; j < (start + len); j += InternalZipConstants.AES_BLOCK_SIZE) { loopCount = (j + InternalZipConstants.AES_BLOCK_SIZE <= (start + len)) ? InternalZipConstants.AES_BLOCK_SIZE : ((start + len) - j); mac.update(buff, j, loopCount); Raw.prepareBuffAESIVBytes(iv, nonce, InternalZipConstants.AES_BLOCK_SIZE); aesEngine.processBlock(iv, counterBlock); for (int k = 0; k < loopCount; k++) { buff[j + k] = (byte)(buff[j + k] ^ counterBlock[k]); } nonce++; } return len; } catch (ZipException e) { throw e; } catch (Exception e) { throw new ZipException(e); } } public int decryptData(byte[] buff) throws ZipException { return decryptData(buff, 0, buff.length); } private byte[] deriveKey(byte[] salt, char[] password) throws ZipException { try { PBKDF2Parameters p = new PBKDF2Parameters("HmacSHA1", "ISO-8859-1", salt, 1000); PBKDF2Engine e = new PBKDF2Engine(p); byte[] derivedKey = e.deriveKey(password, KEY_LENGTH + MAC_LENGTH + PASSWORD_VERIFIER_LENGTH); return derivedKey; } catch (Exception e) { throw new ZipException(e); } } public int getPasswordVerifierLength() { return PASSWORD_VERIFIER_LENGTH; } public int getSaltLength() { return SALT_LENGTH; } public byte[] getCalculatedAuthenticationBytes() { return mac.doFinal(); } public void setStoredMac(byte[] storedMac) { this.storedMac = storedMac; } public byte[] getStoredMac() { return storedMac; } // public byte[] getStoredMac() throws ZipException { // if (raf == null) { // throw new ZipException("attempting to read MAC on closed file handle"); // } // // try { // byte[] storedMacBytes = new byte[InternalZipConstants.AES_AUTH_LENGTH]; // int bytesRead = raf.read(storedMacBytes); // if (bytesRead != InternalZipConstants.AES_AUTH_LENGTH) { // if (zipModel.isSplitArchive()) { //// unzipEngine.startNextSplitFile(); // if (bytesRead == -1) bytesRead = 0; // int newlyRead = raf.read(storedMacBytes, bytesRead, InternalZipConstants.AES_AUTH_LENGTH - bytesRead); // bytesRead += newlyRead; // if (bytesRead != InternalZipConstants.AES_AUTH_LENGTH) { // throw new ZipException("invalid number of bytes read for stored MAC after starting split file"); // } // } else { // throw new ZipException("invalid number of bytes read for stored MAC"); // } // } // return storedMacBytes; // } catch (IOException e) { // throw new ZipException(e); // } catch (Exception e) { // throw new ZipException(e); // } // // } }