package org.commcare.android.javarosa; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Date; import javax.crypto.Cipher; import javax.crypto.CipherOutputStream; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.SecretKeySpec; import org.commcare.android.database.EncryptedModel; import org.commcare.android.logic.GlobalConstants; import org.commcare.android.storage.framework.Persisted; import org.commcare.android.storage.framework.Persisting; import org.commcare.android.storage.framework.Table; import org.commcare.android.util.FileUtil; import org.commcare.dalvik.application.CommCareApplication; import org.javarosa.core.model.utils.DateUtils; import org.javarosa.core.services.Logger; import org.javarosa.core.util.externalizable.DeserializationException; import org.javarosa.core.util.externalizable.ExtUtil; import org.javarosa.core.util.externalizable.PrototypeFactory; /** * A small DB record for keeping track of serialized device reports which we are planning * on submitting. Keeps track of the location on disk, and the key we use to encrypt it. * * Fairly similar record to what we're storing for the forms. Should possibly use that * one and its process * * @author ctsims * */ @Table(DeviceReportRecord.STORAGE_KEY) public class DeviceReportRecord extends Persisted implements EncryptedModel{ public static final String STORAGE_KEY = "log_records"; @Persisting(1) private String fileName; @Persisting(2) private byte[] aesKey; /** * Serialization Only!!! */ public DeviceReportRecord() { } public DeviceReportRecord(String fileName, byte[] aesKey) { this.fileName = fileName; this.aesKey = aesKey; } public static DeviceReportRecord GenerateNewRecordStub() { DeviceReportRecord slr = new DeviceReportRecord(); slr.fileName = new File(CommCareApplication._().getCurrentApp().fsPath((GlobalConstants.FILE_CC_LOGS)) + FileUtil.SanitizeFileName(File.separator + DateUtils.formatDateTime(new Date(), DateUtils.FORMAT_ISO8601)) + ".xml").getAbsolutePath(); slr.aesKey = CommCareApplication._().createNewSymetricKey().getEncoded(); return slr; } /* * (non-Javadoc) * @see org.commcare.android.database.EncryptedModel#isEncrypted(java.lang.String) */ @Override public boolean isEncrypted(String data) { return false; } /* * (non-Javadoc) * @see org.commcare.android.database.EncryptedModel#isBlobEncrypted() */ @Override public boolean isBlobEncrypted() { return true; } public byte[] getKey() { return aesKey; } public String getFilePath() { return fileName; } public final OutputStream openOutputStream() throws FileNotFoundException, IOException { try { String path = getFilePath(); File f = new File (path); FileOutputStream os = new FileOutputStream(f); SecretKeySpec spec = new SecretKeySpec(getKey(), "AES"); Cipher encrypter = Cipher.getInstance("AES"); encrypter.init(Cipher.ENCRYPT_MODE, spec); return new CipherOutputStream(os, encrypter); } catch(InvalidKeyException ike) { ike.printStackTrace(); Logger.log(AndroidLogger.TYPE_ERROR_CRYPTO, "Invalid key: " + ike.getMessage()); throw new IOException("Bad key while trying to generate output stream for device report"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); Logger.log(AndroidLogger.TYPE_ERROR_CRYPTO, "Unavailable Crypto algorithm: " + e.getMessage()); throw new IOException("Bad key while trying to generate output stream for device report"); } catch (NoSuchPaddingException e) { e.printStackTrace(); Logger.log(AndroidLogger.TYPE_ERROR_CRYPTO, "Bad Padding: " + e.getMessage()); throw new IOException("Bad key while trying to generate output stream for device report"); } } }