package org.commcare.android.crypt;

import android.util.Log;

import org.commcare.android.javarosa.AndroidLogger;
import org.javarosa.core.services.Logger;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

/**
 * Methods for dealing with encrypted input/output.
 *
 * @author Phillip Mates (pmates@dimagi.com).
 */
public class EncryptionIO {
    private final static String TAG = EncryptionIO.class.getSimpleName();

    public static OutputStream createFileOutputStream(String filename,
                                                      SecretKeySpec symetricKey)
            throws FileNotFoundException {
        final File path = new File(filename);
        FileOutputStream fos = new FileOutputStream(path);
        if (symetricKey == null) {
            return fos;
        } else {
            try {
                Cipher cipher = Cipher.getInstance("AES");
                cipher.init(Cipher.ENCRYPT_MODE, symetricKey);
                return new CipherOutputStream(fos, cipher);

                //All of these exceptions imply a bad platform and should be irrecoverable (Don't ever
                //write out data if the key isn't good, or the crypto isn't available)
            } catch (InvalidKeyException e) {
                e.printStackTrace();
                Logger.log(AndroidLogger.TYPE_ERROR_CRYPTO, "Invalid key: " + e.getMessage());
                throw new RuntimeException(e.getMessage());
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
                Logger.log(AndroidLogger.TYPE_ERROR_CRYPTO, "Unavailable Crypto algorithm: " + e.getMessage());
                throw new RuntimeException(e.getMessage());
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
                Logger.log(AndroidLogger.TYPE_ERROR_CRYPTO, "Bad Padding: " + e.getMessage());
                throw new RuntimeException(e.getMessage());
            }
        }
    }

    public static InputStream getFileInputStream(String filepath,
                                                 SecretKeySpec symetricKey) {
        final File file = new File(filepath);
        InputStream is;
        try {
            is = new FileInputStream(file);
            if (symetricKey != null) {
                Cipher cipher = Cipher.getInstance("AES");
                cipher.init(Cipher.DECRYPT_MODE, symetricKey);
                is = new BufferedInputStream(new CipherInputStream(is, cipher));
            }

            //CTS - Removed a lot of weird checks  here. file size < max int? We're shoving this
            //form into a _Byte array_, I don't think there's a lot of concern than 2GB of data
            //are gonna sneak by.

            return is;

            //CTS - Removed the byte array length check here. Plenty of
            //files are smaller than their contents (padded encryption data, etc),
            //so you can't actually know that's correct. We should be relying on the
            //methods we use to read data to make sure it's all coming out.
        } catch (FileNotFoundException e) {
            Log.e(TAG, "Cannot find " + file.getName());
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException | NoSuchPaddingException
                | NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

@@ -34,34 +34,8 @@
 t;%0A%0A
-import android.util.Log;%0A%0A
 impo
@@ -153,32 +153,69 @@
 redInputStream;%0A
+import java.io.BufferedOutputStream;%0A
 import java.io.F
@@ -1412,24 +1412,49 @@
  return new 
+BufferedOutputStream(new 
 CipherOutput
@@ -1472,16 +1472,17 @@
  cipher)
+)
 ;%0A%0A     

Use buffered output stream in encryption io