/* * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved. * Original author: Edmund Wagner * Creation date: 23.05.2007 * * Source: $HeadURL$ * Last changed: $LastChangedDate$ * * the unrar licence applies to all junrar source and binary distributions * you are not allowed to use this source to re-create the RAR compression algorithm * * Here some html entities which can be used for escaping javadoc tags: * "&": "&" or "&" * "<": "<" or "<" * ">": ">" or ">" * "@": "@" */ package de.innosystec.unrar.io; import gnu.crypto.cipher.IBlockCipher; import gnu.crypto.cipher.Rijndael; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; /** * DOCUMENT ME * * @author $LastChangedBy$ * @version $LastChangedRevision$ */ public class ReadOnlyAccessFile extends RandomAccessFile implements IReadOnlyAccess { private byte[] salt; private Queue<Byte> data = new LinkedList<Byte>(); private Rijndael rin; private byte[] AESKey = new byte[16]; private byte[] AESInit = new byte[16]; private String password; /** * @param file * the file * @throws FileNotFoundException */ public ReadOnlyAccessFile(File file, String password) throws FileNotFoundException { super(file, "r"); this.password = password; } public int readFully(byte[] buffer, int count) throws IOException { assert (count > 0) : count; // read salt if (this.salt != null) { // System.out.println("salt is not null"); int cs = data.size(); int sizeToRead = count - cs; if (sizeToRead > 0) { int alignedSize = sizeToRead + ((~sizeToRead + 1) & 0xf); for(int i=0;i<alignedSize/16;i++){ //long ax = System.currentTimeMillis(); byte[] tr = new byte[16]; this.readFully(tr, 0, 16); /** * decrypt & add to data list * */ byte[] out = new byte[16]; this.rin.decryptBlock(tr, 0, out, 0); for(int j=0;j<out.length;j++){ this.data.add((byte)(out[j]^AESInit[j%16])); //32:114, 33:101 } for(int j=0;j<AESInit.length;j++){ AESInit[j] = tr[j]; } //System.out.println(System.currentTimeMillis()-ax); } //System.out.println(out); } for (int i = 0; i < count; i++) { buffer[i] = data.poll(); } } else { this.readFully(buffer, 0, count); } return count; } public long getPosition() throws IOException { return this.getFilePointer(); } public void setPosition(long pos) throws IOException { this.seek(pos); } public void setSalt(byte[] salt) { this.salt = salt; if(salt!=null){ // caculate aes key and aes iv and then init rinj //String password = "1234"; this.rin = new Rijndael(); initAES(this.rin, salt, AESInit, AESKey); } // System.out.println(AESKey); // System.out.println(AESInit); } public void initAES(Rijndael rin, byte[] salt, byte[] AESInit, byte[] AESKey){ int rawLength = 2 * password.length(); byte[] rawpsw = new byte[rawLength + 8]; byte[] pwd = password.getBytes(); for (int i = 0; i < password.length(); i++) { rawpsw[i * 2] = pwd[i]; rawpsw[i * 2 + 1] = 0; } for (int i = 0; i < salt.length; i++) { rawpsw[i + rawLength] = salt[i]; } // rawLength += 8; // SHA-1 try { MessageDigest sha = MessageDigest.getInstance("sha-1"); final int HashRounds = 0x40000; final int xh = HashRounds / 16; //List<Byte> content = new ArrayList<Byte>(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); //byte[] input = null; byte[] digest = null; // byte[] pswNum = new byte[3]; //long ax = System.currentTimeMillis(); for (int i = 0; i < HashRounds; i++) { bout.write(rawpsw); //bb.put(rawpsw); //content.addAll(Arrays.asList(rawpsw)); // pswNum[0] = (byte) i; // pswNum[1] = (byte) (i >> 8); // pswNum[2] = (byte) (i >> 16); //bb.put(pswNum); bout.write(new byte[]{(byte) i, (byte) (i >> 8), (byte) (i >> 16)}); //content.addAll(Arrays.asList(pswNum)); if (i % xh == 0) { // //System.out.println("i="+i); // input = new byte[content.size()]; // for(int j=0;j<input.length;j++){ // input[j] = content.get(j); // } //input = bout.toByteArray(); //long ax = System.currentTimeMillis(); byte[] input = bout.toByteArray(); //long bx = System.currentTimeMillis(); sha.update(input); //long cx = System.currentTimeMillis(); //System.out.println(cx-bx); digest = sha.digest(); //AESInit[i / xh] = digest[digest.length-1]; AESInit[i / xh] = digest[19]; } } //System.out.println(System.currentTimeMillis()-ax); // input = new byte[content.size()]; // for(int j=0;j<input.length;j++){ // input[j] = content.get(j); // } //input = bout.toByteArray(); //sha.reset(); sha.update(bout.toByteArray()); digest = sha.digest(); //System.out.println(System.currentTimeMillis()-ax); for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) AESKey[i * 4 + j] = (byte) (((digest[i*4]*0x1000000)&0xff000000|((digest[i*4+1]*0x10000)&0xff0000)|((digest[i*4+2]*0x100)&0xff00)|digest[i*4+3]&0xff) >> (j * 8)); //AESKey[i * 4 + j] = (byte) (((digest[i*4]<<24)&0xff000000+(digest[i*4+1]<<16)&0xff0000+(digest[i*4+2]<<8)&0xff00+digest[i*4+3]&0xff) >> (j * 8)); //System.out.println(System.currentTimeMillis()-ax); } catch (Exception e) { e.printStackTrace(); } try { Map<Object, Object> map = new HashMap<Object, Object>(); map.put(IBlockCipher.KEY_MATERIAL, AESKey); map.put(IBlockCipher.CIPHER_BLOCK_SIZE, new Integer(16)); rin.init(map); } catch (Exception e) { e.printStackTrace(); } } public void resetData(){ this.data.clear(); } @Override public int paddedSize() { // TODO Auto-generated method stub return this.data.size(); } }