]> hydra-www.ietfng.org Git - android-vcpass-oisafe/commitdiff
OI Safe: cleaned up crypto, better handling of errors, added comments
authorrmceoin <rmceoin@72b678ce-9140-0410-bee8-679b907dd61a>
Wed, 8 Apr 2009 00:29:25 +0000 (00:29 +0000)
committerrmceoin <rmceoin@72b678ce-9140-0410-bee8-679b907dd61a>
Wed, 8 Apr 2009 00:29:25 +0000 (00:29 +0000)
git-svn-id: http://openintents.googlecode.com/svn/trunk/Safe@2019 72b678ce-9140-0410-bee8-679b907dd61a

src/org/openintents/safe/CryptoContentProvider.java
src/org/openintents/safe/CryptoHelper.java
src/org/openintents/safe/IntentHandler.java

index 42117294cabef6a8675ab86fb503f48568015a2b..3e09c8241ac8ea7c2683798e3c102d2215179ad7 100644 (file)
@@ -1,3 +1,19 @@
+/* $Id$\r
+ * \r
+ * Copyright (C) 2009 OpenIntents.org\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
 package org.openintents.safe;\r
 \r
 import java.io.File;\r
@@ -82,8 +98,9 @@ public class CryptoContentProvider extends ContentProvider {
 \r
                ParcelFileDescriptor pfd = null;\r
                try {\r
+                       // get the /files/ directory for our application, which\r
+                       // for us is /data/data/org.openintents.safe/files/\r
                        String filesDir=getContext().getFilesDir().toString();\r
-                       if (debug) Log.d(TAG,"openFile: filesDir="+filesDir);\r
 \r
                        String path=filesDir;\r
                        String cryptSession;\r
@@ -111,6 +128,13 @@ public class CryptoContentProvider extends ContentProvider {
 \r
                        if (debug) Log.d(TAG,"openFile: path="+path);\r
                pfd=ParcelFileDescriptor.open(new File(path), modeBits);\r
+               // This is pretty sneaky.  After opening the file,\r
+               // we'll immediately delete the file.\r
+               // Files are not truly deleted if there is an open file\r
+               // handle.   The file will not be deleted until the\r
+               // file handle is closed.   And then it magically\r
+               // disappears.   This makes for a one time use\r
+               // content provider.\r
                if (!getContext().deleteFile(sessionFile)) {\r
                        Log.e(TAG,"openFile: unable to delete: "+sessionFile);\r
                }\r
index c9868169ca7d8c18f6757b85182d9d67153c00d7..750525f1dc23386332288760c0a2925666a34ce6 100644 (file)
@@ -553,16 +553,18 @@ public class CryptoHelper {
     \r
 \r
     /**\r
-     * encrypt a string using a random session key\r
+     * encrypt a file using a random session key\r
      * \r
      * @author Peli\r
      * \r
-     * @param plaintext\r
-     * @return encrypted String\r
+     * @param contentResolver is used to be able to read the stream\r
+     * @param fileUri is the stream or file to read from\r
+     * @return Uri to the created plaintext file\r
      * @throws Exception\r
      */\r
-    public Uri encryptFileWithSessionKey(ContentResolver contentResolver, Uri fileUri) throws CryptoHelperException {\r
-       Log.i(TAG, "Encrypt with session key");\r
+    public Uri encryptFileWithSessionKey(ContentResolver contentResolver, Uri fileUri)\r
+       throws CryptoHelperException {\r
+       if (debug) Log.d(TAG, "Encrypt with session key");\r
                status=false; // assume failure\r
                if(password == null) {\r
                    String msg = "Must call setPassword before runing encrypt.";\r
@@ -582,15 +584,14 @@ public class CryptoHelper {
                        }\r
                        \r
                        FileOutputStream os = new FileOutputStream(outputPath);\r
-       \r
                        \r
                        byte[] cipherSessionKey = {};\r
-                       byte[] ciphertext = {};\r
+//                     byte[] ciphertext = {};\r
                        \r
                        // First create a session key\r
                        SecretKey sessionKey = null;\r
                        byte[] sessionKeyEncoded = null;\r
-                       String sessionKeyString = null;\r
+//                     String sessionKeyString = null;\r
                        try {\r
                                KeyGenerator keygen;\r
                                keygen = KeyGenerator.getInstance("AES");\r
@@ -598,15 +599,17 @@ public class CryptoHelper {
                                //keygen.init(128); // needs 64 bytes\r
                                sessionKey = keygen.generateKey();\r
                                sessionKeyEncoded = sessionKey.getEncoded();\r
-                               sessionKeyString = new String(sessionKeyEncoded);\r
+//                             sessionKeyString = new String(sessionKeyEncoded);\r
                        } catch (NoSuchAlgorithmException e) {\r
                                Log.e(TAG,"generateMasterKey(): "+e.toString());\r
+                               return null;\r
                        }\r
                            \r
                        // Encrypt the session key using the master key\r
                    try {\r
                                pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);\r
                            cipherSessionKey = pbeCipher.doFinal(sessionKeyEncoded);\r
+                           status=true;\r
                        } catch (IllegalBlockSizeException e) {\r
                            Log.e(TAG,"encryptWithSessionKey(): "+e.toString());\r
                        } catch (BadPaddingException e) {\r
@@ -616,6 +619,10 @@ public class CryptoHelper {
                        } catch (InvalidAlgorithmParameterException e) {\r
                            Log.e(TAG,"encryptWithSessionKey(): "+e.toString());\r
                        }\r
+                       if (status==false) {\r
+                               return null;\r
+                       }\r
+                       status=false;\r
        \r
                        String stringCipherVersion = "A";\r
                        byte[] bytesCipherVersion = stringCipherVersion.getBytes();\r
@@ -623,8 +630,8 @@ public class CryptoHelper {
        \r
                        os.write(cipherSessionKey, 0, cipherSessionKey.length);\r
        \r
-                       Log.d(TAG, "bytesCipherVersion.length: " + bytesCipherVersion.length);\r
-                       Log.d(TAG, "cipherSessionKey.length: " + cipherSessionKey.length);\r
+                       if (debug) Log.d(TAG, "bytesCipherVersion.length: " + bytesCipherVersion.length);\r
+                       if (debug) Log.d(TAG, "cipherSessionKey.length: " + cipherSessionKey.length);\r
                        \r
                        Trivium tri = new Trivium();\r
                        try {\r
@@ -640,10 +647,6 @@ public class CryptoHelper {
                                int offset = 0;\r
                                int numRead = 0;\r
                                while ((numRead = is.read(bytesIn, 0, bytesLen)) >= 0) {\r
-                                       if ((numRead & 3) != 0) {\r
-                                               Log.d(TAG, "Bytes read is inappropriate number: " + numRead + " : " + (numRead | 3));\r
-                                       }\r
-                                       \r
                                    tri.process(bytesIn, 0,\r
                                                bytesOut, 0, numRead);\r
                                    \r
@@ -662,6 +665,7 @@ public class CryptoHelper {
                                \r
                                // Securely delete the original file:\r
                                SecureDelete.delete(new File(fileUri.getPath()));\r
+                               status=true;\r
                                \r
                    } catch (ESJException e) {\r
                                Log.e(TAG, "Error encrypting file", e);\r
@@ -671,23 +675,30 @@ public class CryptoHelper {
                } catch (IOException e) {\r
                        Log.e(TAG, "IO Exception", e);\r
                }\r
-                   \r
+               \r
+               if (status==false) {\r
+                       return null;\r
+               }\r
                return Uri.parse("file://" + outputPath); // TODO: UUEncode\r
     }\r
 \r
     /**\r
-     * unencrypt encrypted string previously encrypted with\r
-     * encryptWithSessionKey()\r
+     * Unencrypt a file previously encrypted with\r
+     * encryptFileWithSessionKey().\r
      * \r
      * @author Peli\r
      * \r
-     * @param ciphertext\r
-     * @return decrypted String\r
+     * @param ctx Context of activity in order to store temp file\r
+     * @param fileUri Uri to either a stream or a file to read from\r
+     * @return If decryption is successful, returns Uri of a content \r
+     *                 provider to read the plaintext file.  Upon failure,\r
+     *                 return null.\r
      * @throws Exception\r
      */\r
-    public Uri decryptFileWithSessionKey(Context ctx, ContentResolver contentResolver, Uri fileUri) throws CryptoHelperException {\r
+    public Uri decryptFileWithSessionKey(Context ctx, Uri fileUri) throws CryptoHelperException {\r
                Log.d(TAG, "decryptFileWithSessionKey");\r
                Log.d(TAG, "fileUri="+fileUri.toString());\r
+               ContentResolver contentResolver = ctx.getContentResolver();\r
        status=false; // assume failure\r
                if(password == null) {\r
                    String msg = "Must call setPassword before running decrypt.";\r
@@ -696,13 +707,14 @@ public class CryptoHelper {
 \r
                String decryptSession;\r
                try {\r
+                       // create a random session name\r
                        decryptSession=generateSalt();\r
                } catch (NoSuchAlgorithmException e1) {\r
                        e1.printStackTrace();\r
                    String msg = "Decrypt error: "+e1.getLocalizedMessage();\r
                    throw new CryptoHelperException(msg);\r
                }\r
-               String outputFile = "";\r
+               String sessionFile = "";\r
                try {\r
                        InputStream is;\r
                        if (fileUri.getScheme().equals("file")) {\r
@@ -712,11 +724,13 @@ public class CryptoHelper {
                                is = contentResolver.openInputStream(fileUri);\r
                                if (debug) Log.d(TAG, "Decrypt: Input from " + fileUri.toString());\r
                        }\r
-                       outputFile = CryptoContentProvider.SESSION_FILE+"."+decryptSession;\r
-                       if (debug) Log.d(TAG, "Decrypt: Output to " + outputFile);\r
-                       String cache=Environment.getDownloadCacheDirectory().getAbsolutePath();\r
-                       if (debug) Log.d(TAG, "Decrypt: cache=" + cache);\r
-                       FileOutputStream os = ctx.openFileOutput(outputFile,\r
+                       sessionFile = CryptoContentProvider.SESSION_FILE+"."+decryptSession;\r
+                       if (debug) Log.d(TAG, "Decrypt: Output to " + sessionFile);\r
+                       \r
+                       // openFileOutput creates a file in /data/data/{packagename}/files/\r
+                       // In our case, /data/data/org.openintents.safe/files/\r
+                       // This file is owned and only readable by our application\r
+                       FileOutputStream os = ctx.openFileOutput(sessionFile,\r
                                        Context.MODE_PRIVATE);\r
        \r
                        int numReadTotal = 0;\r
@@ -750,10 +764,12 @@ public class CryptoHelper {
                                        }\r
                                } else {\r
                                        Log.e(TAG, "Unknown cipher version" + cipherVersion);\r
+                                       ctx.deleteFile(sessionFile);\r
                                        return null;\r
                                }\r
                        } catch (IndexOutOfBoundsException e) {\r
                                Log.e(TAG, "Invalid ciphertext (with session key)");\r
+                               ctx.deleteFile(sessionFile);\r
                                return null;\r
                        }\r
                        \r
@@ -789,10 +805,6 @@ public class CryptoHelper {
                                int offset = 0;\r
                                numRead = 0;\r
                                while ((numRead = is.read(bytesIn, 0, bytesLen)) >= 0) {\r
-                                       if ((numRead & 3) != 0) {\r
-                                               if (debug) Log.d(TAG, "Bytes read is inappropriate number: " + numRead);\r
-                                       }\r
-                                       \r
                                    tri.process(bytesIn, 0,\r
                                                bytesOut, 0, numRead);\r
                                    \r
@@ -810,18 +822,28 @@ public class CryptoHelper {
                                os.close();\r
 \r
                                // Securely delete the original file:\r
-//                             SecureDelete.delete(new File(fileUri.getPath()));\r
+                               SecureDelete.delete(new File(fileUri.getPath()));\r
+\r
+                               status=true;\r
                                \r
                    } catch (ESJException e) {\r
-                               Log.e(TAG, "Error encrypting file", e);\r
+                               Log.e(TAG, "Error decrypting file", e);\r
                }\r
                    \r
                } catch (FileNotFoundException e) {\r
-                       Log.e(TAG, "File not found", e);\r
+//                     Log.e(TAG, "File not found", e);\r
                } catch (IOException e) {\r
                        Log.e(TAG, "IOException", e);\r
                }\r
-//             return Uri.parse("file://" + outputPath); // TODO: UUEncode\r
+               if (status==false) {\r
+                       if (sessionFile.length()!=0) {\r
+                               if (debug) Log.d(TAG,"status==false, deleting sessionFile:"+sessionFile);\r
+                               ctx.deleteFile(sessionFile);\r
+                       }\r
+                       return null;\r
+               }\r
+               // after writing the decrypted content to a temporary file,\r
+               // pass back a Uri that can be used to read back the contents\r
                Uri uri=Uri.withAppendedPath(CryptoContentProvider.CONTENT_URI, "decrypt/" + decryptSession);\r
                return uri;\r
     }\r
index 8b56a01682697fc83ade94033c75492e081f9d45..e2fd9e783efcda7edc537d6ff2f50ead4db1935c 100644 (file)
@@ -323,7 +323,7 @@ public class IntentHandler extends Activity {
                                // Decrypt file from file URI\r
                                Uri fileUri = thisIntent.getData();\r
                                \r
-                               Uri newFileUri = ch.decryptFileWithSessionKey(this,getContentResolver(), fileUri);\r
+                               Uri newFileUri = ch.decryptFileWithSessionKey(this, fileUri);\r
                                \r
                                callbackIntent.setData(newFileUri);\r
                        }\r