/*
* 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.Random;
import net.lingala.zip4j.crypto.engine.ZipCryptoEngine;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.util.InternalZipConstants;
public class StandardEncrypter implements IEncrypter {
private ZipCryptoEngine zipCryptoEngine;
private byte[] headerBytes;
public StandardEncrypter(char[] password, int crc) throws ZipException {
if (password == null || password.length <= 0) {
throw new ZipException("input password is null or empty in standard encrpyter constructor");
}
this.zipCryptoEngine = new ZipCryptoEngine();
this.headerBytes = new byte[InternalZipConstants.STD_DEC_HDR_SIZE];
init(password, crc);
}
private void init(char[] password, int crc) throws ZipException {
if (password == null || password.length <= 0) {
throw new ZipException("input password is null or empty, cannot initialize standard encrypter");
}
zipCryptoEngine.initKeys(password);
headerBytes = generateRandomBytes(InternalZipConstants.STD_DEC_HDR_SIZE);
// Initialize again since the generated bytes were encrypted.
zipCryptoEngine.initKeys(password);
headerBytes[InternalZipConstants.STD_DEC_HDR_SIZE - 1] = (byte)((crc >>> 24));
headerBytes[InternalZipConstants.STD_DEC_HDR_SIZE - 2] = (byte)((crc >>> 16));
if (headerBytes.length < InternalZipConstants.STD_DEC_HDR_SIZE) {
throw new ZipException("invalid header bytes generated, cannot perform standard encryption");
}
encryptData(headerBytes);
}
public int encryptData(byte[] buff) throws ZipException {
if (buff == null) {
throw new NullPointerException();
}
return encryptData(buff, 0, buff.length);
}
public int encryptData(byte[] buff, int start, int len) throws ZipException {
if (len < 0) {
throw new ZipException("invalid length specified to decrpyt data");
}
try {
for (int i = start; i < start + len; i++) {
buff[i] = encryptByte(buff[i]);
}
return len;
} catch (Exception e) {
throw new ZipException(e);
}
}
protected byte encryptByte(byte val) {
byte temp_val = (byte) (val ^ zipCryptoEngine.decryptByte() & 0xff);
zipCryptoEngine.updateKeys(val);
return temp_val;
}
protected byte[] generateRandomBytes(int size) throws ZipException {
if (size <= 0) {
throw new ZipException("size is either 0 or less than 0, cannot generate header for standard encryptor");
}
byte[] buff = new byte[size];
Random rand = new Random();
for (int i = 0; i < buff.length; i ++) {
// Encrypted to get less predictability for poorly implemented
// rand functions.
buff[i] = encryptByte((byte) rand.nextInt(256));
}
// buff[0] = (byte)87;
// buff[1] = (byte)176;
// buff[2] = (byte)-49;
// buff[3] = (byte)-43;
// buff[4] = (byte)93;
// buff[5] = (byte)-204;
// buff[6] = (byte)-105;
// buff[7] = (byte)213;
// buff[8] = (byte)-80;
// buff[9] = (byte)-8;
// buff[10] = (byte)21;
// buff[11] = (byte)242;
// for( int j=0; j<2; j++ ) {
// Random rand = new Random();
// int i = rand.nextInt();
// buff[0+j*4] = (byte)(i>>24);
// buff[1+j*4] = (byte)(i>>16);
// buff[2+j*4] = (byte)(i>>8);
// buff[3+j*4] = (byte)i;
// }
return buff;
}
public byte[] getHeaderBytes() {
return headerBytes;
}
}