+/* $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
\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
\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
\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
}\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
//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
} 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
\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
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
\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
} 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
\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
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
}\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
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
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