]> hydra-www.ietfng.org Git - android-vcpass-oisafe/commitdiff
OI About: read out translators from strings.xml
authorezelspinguin <ezelspinguin@72b678ce-9140-0410-bee8-679b907dd61a>
Tue, 27 Jan 2009 22:08:02 +0000 (22:08 +0000)
committerezelspinguin <ezelspinguin@72b678ce-9140-0410-bee8-679b907dd61a>
Tue, 27 Jan 2009 22:08:02 +0000 (22:08 +0000)
git-svn-id: http://openintents.googlecode.com/svn/trunk/Safe@1860 72b678ce-9140-0410-bee8-679b907dd61a

28 files changed:
AndroidManifest.xml
assets/help.html
res/layout-land/front_door.xml
res/layout/front_door.xml
res/layout/pass_view.xml
res/values/strings.xml
res/values/strings_permissions.xml [new file with mode: 0644]
src/org/openintents/safe/AskPassword.java
src/org/openintents/safe/Backup.java
src/org/openintents/safe/CategoryEdit.java
src/org/openintents/safe/CategoryList.java
src/org/openintents/safe/ChangePass.java
src/org/openintents/safe/CryptoHelper.java
src/org/openintents/safe/DBHelper.java
src/org/openintents/safe/FrontDoor.java
src/org/openintents/safe/IntentHandler.java [new file with mode: 0644]
src/org/openintents/safe/PassEdit.java
src/org/openintents/safe/PassEntry.java
src/org/openintents/safe/PassList.java
src/org/openintents/safe/PassView.java
src/org/openintents/safe/Preferences.java
src/org/openintents/safe/Restore.java
src/org/openintents/safe/RestoreDataSet.java
src/org/openintents/safe/RestoreHandler.java
src/org/openintents/safe/dialog/DialogHostingActivity.java
src/org/openintents/safe/dialog/FirstTimeWarningDialog.java [new file with mode: 0644]
src/org/openintents/safe/service/ServiceDispatch.aidl
src/org/openintents/safe/service/ServiceDispatchImpl.java

index 905513f71eeaf3d97c8ccf62436a42f3a255050d..a5cae24a93431ec1d1eb5c98cedf8745f6323964 100644 (file)
@@ -2,7 +2,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="org.openintents.safe" \r
     android:versionCode="1" \r
-    android:versionName="0.7.0">
+    android:versionName="1.0.0-beta1">
     <application android:icon="@drawable/icon_safe" android:allowClearUserData="true" android:debuggable="true" android:label="@string/app_name">\r
     \r
                <meta-data android:name="org.openintents.metadata.COMMENTS"\r
                <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                        <category android:name="android.intent.category.LAUNCHER" />\r
-            </intent-filter>
-            
+            </intent-filter>           
+        </activity>
+        
+        <activity class=".IntentHandler" android:name="IntentHandler" android:label="@string/app_name"
+                  android:permission="org.openintents.safe.ACCESS_INTENTS">
             <intent-filter android:label="@string/intent_encrypt">
                 <action android:name="org.openintents.action.ENCRYPT" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -48,9 +51,9 @@
                <intent-filter android:label="@string/intent_set_password">
                 <action android:name="org.openintents.action.SET_PASSWORD" />
                 <category android:name="android.intent.category.DEFAULT" />
-               </intent-filter>
-            
+               </intent-filter>        
         </activity>
+        
         <activity class=".PassGen" android:name="PassGen" android:label="@string/app_name" />
         <activity class=".CategoryEdit" android:name="CategoryEdit" android:label="@string/app_name" />
         <activity class=".PassView" android:name="PassView" android:label="@string/app_name" />
         </service>    
     </application> 
 
+    <permission android:name="org.openintents.safe.ACCESS_INTENTS"
+                       android:protectionLevel="dangerous"
+                       android:label="@string/oi_safe_permission_intents"
+               android:description="@string/oi_safe_permission_intents_descr" >
+     </permission>
+       
     <permission android:name="org.openintents.safe.ACCESS_SERVICE"
                        android:process=":remote"  android:exported="true"
                        android:protectionLevel="signature"
-                       android:label="@string/permlab_service"
-               android:description="@string/permdesc_service">         
+                       android:label="@string/oi_safe_permission_service"
+               android:description="@string/oi_safe_permission_service_descr">         
        </permission>
+    <uses-permission android:name="org.openintents.safe.ACCESS_INTENTS" />
     <uses-permission android:name="org.openintents.safe.ACCESS_SERVICE" />
     
 </manifest> 
\ No newline at end of file
index 135c035903ca7db040c1eeed1c76ad93bd825ae9..b5933ce8e45956ba357b2db26b44d82b69de361c 100644 (file)
@@ -66,9 +66,9 @@ body {
 <h2>Backup and Restore</h2>
 
 <p>You should backup your database of passwords from time to time.  To do this
-   use Menu -> Backup.   This will create a file named 'passwordsafe.xml' on
+   use Menu -> Backup.   This will create a file named 'oisafe.xml' on
    the sdcard.  Connect your phone to a computer and allow the USB mounting.
-   Look for 'passwordsafe.xml' and copy that to a safe location on your computer.
+   Look for 'oisafe.xml' and copy that to a safe location on your computer.
    The file is encrypted using the same encryption as the database on your phone.</p>
 
 <h2>Import and Export</h2>
@@ -94,8 +94,8 @@ body {
    put some data into the OI Safe and perform an export first, so
    that you can see what the CSV should look like.</p>
    
-<p>Use the name 'passwordsafe.csv' for your filename.   Connect the
-   phone to your computer and copy 'passwordsafe.csv' onto the phone.
+<p>Use the name 'oisafe.csv' for your filename.   Connect the
+   phone to your computer and copy 'oisafe.csv' onto the phone.
    The file must be in the base directory of the phone's USB storage.</p>
    
 <p>Once the CSV file is in place, use Menu -> Import to import the file.
@@ -104,7 +104,7 @@ body {
    the same time.</p>
 
 <p>Exporting is a snap.  Just use Menu -> Export.  This will create the
-   file 'passwordsafe.csv'.  Connect your phone to a computer to retrieve
+   file 'oisafe.csv'.  Connect your phone to a computer to retrieve
    this file.  
    <div class="warning">WARNING: The exported file is not encrypted!!
    After exporting and working with the file, you should delete it
@@ -128,15 +128,14 @@ body {
 <p>Any of the following actions will cause OI Safe to lock:</p>
 
 <ul>
+  <li>The auto lock timer expires.  This may be set to 1, 5 or 30 minutes.
+   The default is 5 minutes.</li>
   <li>The screen turns off.  This is a default behavior of the phone and is controlled
    in the phone's Settings -&gt; Sound &amp; display -> Screen timeout.   
    The default timeout is 1 minute.
    After 1 minute the phone will go to sleep.   When this happens, OI Safe will 
    lock itself.</li>
   <li>You select <i>Lock</i> from the Menu on the Categories screen.</li>
-  <li>Android only allows six active applications.  Once you launch the seventh,
-   the oldest used application will be killed.  If OI Safe is killed, than
-   the next time you launch it, it will be locked.</li>
 </ul>
 
 <p>Really, the easiest way to lock is to just hit the power button on the phone briefly and
index 173f0493eb9f126f1678bb3598b0d680598b21bc..6d3ef860c73979c6564550280af4ab989a9a626c 100644 (file)
                android:layout_width="wrap_content"\r
                android:layout_height="wrap_content" /> \r
 \r
+       <Button android:id="@+id/restore_button" \r
+               android:layout_below="@id/pass_confirm"\r
+               android:layout_toRightOf="@id/continue_button"\r
+               android:visibility="invisible" \r
+               android:text="@string/restore"\r
+               android:layout_width="wrap_content"\r
+               android:layout_height="wrap_content" /> \r
 </RelativeLayout>      
\ No newline at end of file
index 233390ba3931ce3dfacd0974a200dbf272f15e02..09d78c6a3f8b531eef25bf9607a552e71973dbf2 100644 (file)
@@ -77,4 +77,9 @@
                android:text="@string/continue_text"\r
                android:layout_width="wrap_content"\r
                android:layout_height="wrap_content" /> \r
+       <Button android:id="@+id/restore_button" \r
+               android:visibility="invisible" \r
+               android:text="@string/restore"\r
+               android:layout_width="wrap_content"\r
+               android:layout_height="wrap_content" /> \r
 </LinearLayout>        
\ No newline at end of file
index 0302e417bd915953ee61bb7d2f333c9a9668ad1a..d05799f6a7296aa9499dba1e51de16982e063d5c 100644 (file)
                android:layout_width="wrap_content"\r
                android:layout_height="wrap_content" \r
                android:text="@string/last_edited" />\r
+\r
+       <TextView android:id="@+id/uniquename"\r
+               android:layout_width="wrap_content"\r
+               android:layout_height="wrap_content" \r
+               android:text="" />\r
+\r
+       <TextView android:id="@+id/packageaccess"\r
+               android:layout_width="wrap_content"\r
+               android:layout_height="wrap_content" \r
+               android:text="" />\r
 
 </LinearLayout>\r
 </ScrollView>
\ No newline at end of file
index ac188d5be8727161de1dcc3d2af7cb11bf62a4b8..61abaf471fd45860aef993f66687db47179eb51e 100644 (file)
     <string name="open">Open</string>\r
     <string name="database_version_error_title">Database Version Error</string>\r
     <string name="database_version_error_msg">Sorry, but the version of the database is not supported.   You must install the version associated with this database.</string>\r
-       <string name="permlab_service">access stored password</string>\r
-    <string name="permdesc_service">Allows the application to encrypt and decrypt text, and access the passwords they have stored in OI Safe.</string> \r
        <string name="move">Move</string>\r
        <string name="move_select">Select Category</string>\r
        <string name="moved_to">Moved to</string>\r
        <string name="preferences">Preferences</string>\r
        <string name="pref_title_external_access">Allow external access</string>\r
-       <string name="pref_summary_external_access">Allow other applications to secure your data with OI Safe?</string>\r
+       <string name="pref_summary_external_access">Allow other applications to secure your data with OI Safe</string>\r
        <string name="dialog_title_external_access">OI Safe: Another application requests access.</string>\r
        <string name="dialog_summary_external_access">Allow other applications to secure your data with OI Safe.</string>\r
        <string name="dialog_comment_external_access">This can also be set in OI Safe\'s Menu / Preferences.</string>\r
        <string name="pref_title_lock_timeout">Auto lock timeout</string>\r
-       <string name="pref_summary_lock_timeout">Minutes before timeout occurs and safe is locked.</string>\r
+       <string name="pref_summary_lock_timeout">Minutes before timeout occurs and safe is locked</string>\r
        <string name="pref_dialog_title_lock_timeout">Auto lock timeout</string>\r
        <string name="intent_decrypt">Decrypt (OI Safe)</string>\r
        <string name="intent_encrypt">Encrypt (OI Safe)</string>\r
        <string name="entry_saved">Entry saved</string>\r
        <string name="last_edited">Last edited</string>\r
        <string name="last_edited_unknown">unknown</string>\r
+       <string name="crypto_error">Crypto error:</string>\r
+       <string name="uniquename">Unique name</string>\r
+       <string name="package_access">Package access</string>\r
+       <string name="not_found">not found</string>\r
+       <string name="dialog_title_first_time_warning">New master key</string>\r
+       <string name="dialog_summary_first_time_warning">A new random master key has been created. Use menu > backup and store this key in a safe place. Without this key you may loose encrypted data.</string>\r
 </resources>
diff --git a/res/values/strings_permissions.xml b/res/values/strings_permissions.xml
new file mode 100644 (file)
index 0000000..9939767
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- \r
+ * Copyright (C) 2007-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
\r
+<resources>\r
+       <!-- OI Safe -->\r
+       <string name="oi_safe_permission_service">access the master password</string>\r
+       <string name="oi_safe_permission_service_descr">Allows the application to access the master key used for encryption and decryption. Should never be used except by OI Safe.</string>    \r
+       <string name="oi_safe_permission_intents">access stored password</string>\r
+    <string name="oi_safe_permission_intents_descr">Allows the application to encrypt and decrypt text, and access the passwords they have stored in OI Safe.</string> \r
+</resources> \r
index 315f05aa78482a42399c3389f9d104109eac32a9..f5a4ca95074abd939df5c213ff17ad6ae578289f 100644 (file)
  */
 package org.openintents.safe;
 
+import java.io.File;
+import java.security.NoSuchAlgorithmException;
+
 import org.openintents.distribution.EulaActivity;
+import org.openintents.safe.dialog.DialogHostingActivity;
 import org.openintents.util.VersionUtils;
 
 import android.app.Activity;
@@ -49,6 +53,8 @@ public class AskPassword extends Activity {
        private static String TAG = "AskPassword";
        public static String EXTRA_IS_LOCAL = "org.openintents.safe.bundle.EXTRA_IS_REMOTE";
 
+    public static final int REQUEST_RESTORE = 0;
+
        private EditText pbeKey;
        private DBHelper dbHelper;
        private TextView introText;
@@ -56,6 +62,7 @@ public class AskPassword extends Activity {
        private TextView remoteAsk;
        private EditText confirmPass;
        private String PBEKey;
+       private String salt;
        private String masterKey;
        private CryptoHelper ch;
        private boolean firstTime = false;
@@ -78,7 +85,7 @@ public class AskPassword extends Activity {
 
                dbHelper = new DBHelper(this);
                        
-               ch = new CryptoHelper(CryptoHelper.EncryptionStrong);
+               ch = new CryptoHelper();
                if (dbHelper.needsUpgrade()) {
                        switch (dbHelper.fetchVersion()) {
                        case 2:
@@ -101,12 +108,14 @@ public class AskPassword extends Activity {
                remoteAsk = (TextView) findViewById(R.id.remote);
                confirmPass = (EditText) findViewById(R.id.pass_confirm);
                confirmText = (TextView) findViewById(R.id.confirm_lbl);
+               salt = dbHelper.fetchSalt();
                masterKey = dbHelper.fetchMasterKey();
                if (masterKey.length() == 0) {
                        firstTime = true;
                        introText.setVisibility(View.VISIBLE);
                        confirmText.setVisibility(View.VISIBLE);
                        confirmPass.setVisibility(View.VISIBLE);
+                       checkForBackup();
                }
                if (! isLocal) {
                        if (remoteAsk != null) {
@@ -122,7 +131,6 @@ public class AskPassword extends Activity {
                                // For this version of CryptoHelper, we use the user-entered password.
                                // All other versions should be instantiated with the generated master
                                // password.
-                               ch.setPassword(PBEKey);
 
                                // Password must be at least 4 characters
                                if (PBEKey.length() < 4) {
@@ -150,15 +158,29 @@ public class AskPassword extends Activity {
                                                                .show();
                                                return;
                                        }
-                                       masterKey = CryptoHelper.generateMasterKey();
+                                       try {
+                                               salt = CryptoHelper.generateSalt();
+                                               masterKey = CryptoHelper.generateMasterKey();
+                                       } catch (NoSuchAlgorithmException e1) {
+                                               e1.printStackTrace();
+                                               Toast.makeText(AskPassword.this,getString(R.string.crypto_error)
+                                                       + e1.getMessage(), Toast.LENGTH_SHORT).show();
+                                               return;
+                                       }
                                        if (debug) Log.i(TAG, "Saving Password: " + masterKey);
                                        try {
+                                               ch.init(CryptoHelper.EncryptionStrong,salt);
+                                               ch.setPassword(PBEKey);
                                                String encryptedMasterKey = ch.encrypt(masterKey);
+                                               dbHelper.storeSalt(salt);
                                                dbHelper.storeMasterKey(encryptedMasterKey);
                                        } catch (CryptoHelperException e) {
                                                Log.e(TAG, e.toString());
+                                               Toast.makeText(AskPassword.this,getString(R.string.crypto_error)
+                                                       + e.getMessage(), Toast.LENGTH_SHORT).show();
+                                               return;
                                        }
-                               } else if (!checkUserPassword()) {
+                               } else if (!checkUserPassword(PBEKey)) {
                                        // Check the user's password and display a
                                        // message if it's wrong
                                        Toast.makeText(AskPassword.this, R.string.invalid_password,
@@ -175,9 +197,35 @@ public class AskPassword extends Activity {
                                // Return the master key to our caller.  We no longer need the
                                // user-entered PBEKey. The master key is used for everything
                                // from here on out.
+                               if (debug) Log.d(TAG,"calbackintent: masterKey="+masterKey+" salt="+salt);
                                callbackIntent.putExtra("masterKey", masterKey);
+                               callbackIntent.putExtra("salt", salt);
                                setResult(RESULT_OK, callbackIntent);
+                               
                                finish();
+                               
+                       }
+               });
+       }
+       
+       private void checkForBackup() {
+               String filename=CategoryList.BACKUP_FILENAME;
+               File restoreFile=new File(filename);
+               if (!restoreFile.exists()) {
+                       return;
+               }
+               Button restoreButton = (Button) findViewById(R.id.restore_button);
+               if (restoreButton == null) {
+                       if (debug) Log.d(TAG, "layout not created yet");
+                       return;
+               }
+               restoreButton.setVisibility(View.VISIBLE);
+               restoreButton.setOnClickListener(new View.OnClickListener() {
+
+                       public void onClick(View arg0) {
+                               Intent restore = new Intent(AskPassword.this, Restore.class);
+                               restore.putExtra(Restore.KEY_FIRST_TIME, true);
+                               startActivityForResult(restore,REQUEST_RESTORE);                
                        }
                });
        }
@@ -224,11 +272,15 @@ public class AskPassword extends Activity {
         * 
         * @return
         */
-       private boolean checkUserPassword() {
+       private boolean checkUserPassword(String password) {
                String encryptedMasterKey = dbHelper.fetchMasterKey();
                String decryptedMasterKey = "";
+               if (debug) Log.d(TAG,"checkUserPassword: encryptedMasterKey="+encryptedMasterKey);
                try {
+                       ch.init(CryptoHelper.EncryptionStrong,salt);
+                       ch.setPassword(password);
                        decryptedMasterKey = ch.decrypt(encryptedMasterKey);
+                       if (debug) Log.d(TAG,"decryptedMasterKey="+decryptedMasterKey);
                } catch (CryptoHelperException e) {
                        Log.e(TAG, e.toString());
                }
@@ -239,4 +291,19 @@ public class AskPassword extends Activity {
                masterKey=null;
                return false;
        }
+       
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent i) {
+       super.onActivityResult(requestCode, resultCode, i);
+
+       if ((requestCode== REQUEST_RESTORE) && (resultCode == RESULT_OK)) {
+               Log.d(TAG,"returning masterkey: "+CategoryList.getMasterKey());
+                       Intent callbackIntent = new Intent();
+                       callbackIntent.putExtra("salt", CategoryList.getSalt());
+                       callbackIntent.putExtra("masterKey", CategoryList.getMasterKey());
+                       setResult(RESULT_OK, callbackIntent);
+               finish();
+       }
+    }
+
 }
index 31a9e625294c2b14666c1d3a5122cb820b64e687..febfb0a21a42f8fe5b5207b8076dae53a5d287cc 100644 (file)
@@ -72,7 +72,12 @@ public class Backup {
                        serializer.startTag(null, "MasterKey");
                        serializer.text(masterKeyEncrypted);
                        serializer.endTag(null, "MasterKey");
-                       
+
+                       String salt = dbHelper.fetchSalt();
+                       serializer.startTag(null, "Salt");
+                       serializer.text(salt);
+                       serializer.endTag(null, "Salt");
+
                        List<CategoryEntry> crows;
                        crows = dbHelper.fetchAllCategoryRows();
                        HashMap<Long, ArrayList<String>> packageAccess=dbHelper.fetchPackageAccessAll();
index f3879996e1eea429fd9f5e69cb90acb9276ee25c..9bb38af8ab68115cf14c7a4fea4dc456db80cbcc 100644 (file)
@@ -32,19 +32,28 @@ import android.widget.Toast;
  */
 public class CategoryEdit extends Activity {
 
+       private static final boolean debug = false;
+    private static String TAG = "CategoryEdit";
+
     private EditText nameText;
     private Long RowId;
     private DBHelper dbHelper=null;
     private CryptoHelper ch;
 
-    private static String TAG = "CategoryEdit";
 
     public void onCreate(Bundle icicle) {
                super.onCreate(icicle);
-               Log.d(TAG, "onCreate");
+               if (debug) Log.d(TAG, "onCreate");
                
                ch = new CryptoHelper();
-               ch.setPassword(PassList.getMasterKey());
+               try {
+                       ch.init(CryptoHelper.EncryptionMedium,PassList.getSalt());
+                       ch.setPassword(PassList.getMasterKey());
+               } catch (CryptoHelperException e1) {
+                       e1.printStackTrace();
+                       Toast.makeText(this,getString(R.string.crypto_error)
+                               + e1.getMessage(), Toast.LENGTH_SHORT).show();
+               }
 
                if (dbHelper == null){
                        dbHelper = new DBHelper(this);
@@ -98,7 +107,7 @@ public class CategoryEdit extends Activity {
     @Override
     protected void onPause() {
                super.onPause();
-               Log.d(TAG, "onPause");
+               if (debug) Log.d(TAG, "onPause");
                dbHelper.close();
                dbHelper = null;
     }
@@ -106,7 +115,7 @@ public class CategoryEdit extends Activity {
     @Override
     protected void onResume() {
                super.onResume();
-               Log.d(TAG, "onResume");
+               if (debug) Log.d(TAG, "onResume");
                if (dbHelper == null) {
                    dbHelper = new DBHelper(this);
                }
@@ -119,11 +128,11 @@ public class CategoryEdit extends Activity {
     }
 
     private void saveState() {
-       Log.d(TAG, "saveState");
+       if (debug) Log.d(TAG, "saveState");
                CategoryEntry entry =  new CategoryEntry();
        
                String namePlain = nameText.getText().toString();
-               Log.d(TAG, "name: " + namePlain);
+               if (debug) Log.d(TAG, "name: " + namePlain);
                
                try {
                    entry.name = ch.encrypt(namePlain);
@@ -133,11 +142,11 @@ public class CategoryEdit extends Activity {
        
        
                if(RowId == null || RowId == -1) {
-                       Log.d(TAG, "addCategory");
+                       if (debug) Log.d(TAG, "addCategory");
                    dbHelper.addCategory(entry);
                } else {
-                       Log.d(TAG, "updateCategory");
-                       Log.d(TAG, "RowId: " + String.valueOf(RowId));
+                       if (debug) Log.d(TAG, "updateCategory");
+                       if (debug) Log.d(TAG, "RowId: " + String.valueOf(RowId));
                    dbHelper.updateCategory(RowId, entry);
                }
     }
@@ -146,7 +155,7 @@ public class CategoryEdit extends Activity {
      * 
      */
     private void populateFields() {
-       Log.d(TAG, "populateFields");
+       if (debug) Log.d(TAG, "populateFields");
                if (RowId != null) {
                    CategoryEntry row = dbHelper.fetchCategory(RowId);
                    if (row.id > -1) {
index eb2c17a31d6cfead2cb23c8ccbbad306af8bae0e..85095e7e0b08cd0ac19642afdeb6cada33c06936 100644 (file)
@@ -31,6 +31,7 @@ import java.util.Set;
 import org.openintents.distribution.AboutDialog;
 import org.openintents.intents.AboutMiniIntents;
 import org.openintents.intents.CryptoIntents;
+import org.openintents.safe.dialog.DialogHostingActivity;
 import org.openintents.safe.service.ServiceDispatchImpl;
 import org.openintents.util.IntentUtils;
 
@@ -44,9 +45,11 @@ import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.preference.PreferenceManager;
 import android.util.Log;
 import android.view.ContextMenu;
 import android.view.Menu;
@@ -117,6 +120,7 @@ public class CategoryList extends ListActivity {
 
        private Thread backupThread=null;
 
+       private static String salt;
     private static String masterKey;                   
 
     private List<CategoryEntry> rows;
@@ -241,7 +245,27 @@ public class CategoryList extends ListActivity {
                        startActivity(frontdoor);               
                        finish();
        }
+
+        showFirstTimeWarningDialog();
     }
+
+       /**
+        * 
+        */
+       private void showFirstTimeWarningDialog() {
+               SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
+        boolean firstTimeWarning = sp.getBoolean(Preferences.PREFERENCE_FIRST_TIME_WARNING, false);
+        
+               if (!firstTimeWarning) {
+                       Intent i = new Intent(this, DialogHostingActivity.class);
+                       i.putExtra(DialogHostingActivity.EXTRA_DIALOG_ID, DialogHostingActivity.DIALOG_ID_FIRST_TIME_WARNING);
+                       startActivity(i);
+                       
+            SharedPreferences.Editor editor = sp.edit();
+            editor.putBoolean(Preferences.PREFERENCE_FIRST_TIME_WARNING, true);
+            editor.commit();
+               }
+       }
     
     @Override
     protected void onPause() {
@@ -360,7 +384,15 @@ public class CategoryList extends ListActivity {
                if(masterKey == null) {
                    masterKey = "";
                }
-               ch.setPassword(masterKey);
+               try {
+                       ch.init(CryptoHelper.EncryptionMedium,salt);
+                       ch.setPassword(masterKey);
+               } catch (CryptoHelperException e1) {
+                       e1.printStackTrace();
+                       Toast.makeText(this,getString(R.string.crypto_error)
+                               + e1.getMessage(), Toast.LENGTH_SHORT).show();
+                       return;
+               }
        
                List<String> items = new ArrayList<String>();
                if (dbHelper==null) {
@@ -449,6 +481,14 @@ public class CategoryList extends ListActivity {
                return super.onCreateOptionsMenu(menu);
     }
 
+    static void setSalt(String saltIn) {
+               salt = saltIn;
+    }
+
+    static String getSalt() {
+               return salt;
+    }
+
     static void setMasterKey(String key) {
                masterKey = key;
     }
@@ -641,11 +681,14 @@ public class CategoryList extends ListActivity {
                        if(masterKey == null) {
                            masterKey = "";
                        }
+                       ch.init(CryptoHelper.EncryptionMedium,salt);
                        ch.setPassword(masterKey);
 
                    entry.name = ch.encrypt(namePlain);
                } catch(CryptoHelperException e) {
                    Log.e(TAG,e.toString());
+                       Toast.makeText(this,getString(R.string.crypto_error)
+                               + e.getMessage(), Toast.LENGTH_SHORT).show();
                }
            return dbHelper.addCategory(entry);
     }
@@ -668,7 +711,15 @@ public class CategoryList extends ListActivity {
                        if(masterKey == null) {
                            masterKey = "";
                        }
-                       ch.setPassword(masterKey);
+                       try {
+                               ch.init(CryptoHelper.EncryptionMedium,salt);
+                               ch.setPassword(masterKey);
+                       } catch (CryptoHelperException e1) {
+                               e1.printStackTrace();
+                               Toast.makeText(this,getString(R.string.crypto_error)
+                                       + e1.getMessage(), Toast.LENGTH_SHORT).show();
+                               return false;
+                       }
                
                        HashMap<Long, String> categories = new HashMap<Long, String>();
                        
@@ -964,6 +1015,7 @@ public class CategoryList extends ListActivity {
                                    Log.e(TAG,e.toString());
                                    continue;
                                }
+                               entry.id=0;
                            dbHelper.addPassword(entry);
                        newEntries++;
                    }
@@ -994,7 +1046,13 @@ public class CategoryList extends ListActivity {
                if(masterKey == null) {
                    masterKey = "";
                }
-               ch.setPassword(masterKey);
+               try {
+                       ch.init(CryptoHelper.EncryptionMedium,salt);
+                       ch.setPassword(masterKey);
+               } catch (CryptoHelperException e1) {
+                       e1.printStackTrace();
+                       return null;
+               }
        
                HashMap<String,Long> categories = new HashMap<String,Long>();
                List<CategoryEntry> rows;
index b64dca6e6594f9e103a51c7c9b0489752392cfeb..0e06de3038c549991df8c97fbc292f1461b4f438 100644 (file)
@@ -191,11 +191,12 @@ public class ChangePass extends Activity {
        
                DBHelper dbHelper= new DBHelper(this);
 
-               CryptoHelper ch = new CryptoHelper(CryptoHelper.EncryptionStrong);
+               CryptoHelper ch = new CryptoHelper();
 
                String encryptedMasterKey = dbHelper.fetchMasterKey();
                String decryptedMasterKey = "";
                try {
+                       ch.init(CryptoHelper.EncryptionStrong, dbHelper.fetchSalt());
                        ch.setPassword(oldPass);
                        decryptedMasterKey = ch.decrypt(encryptedMasterKey);
                        if (ch.getStatus()==true) {     // successful decryption?
@@ -214,6 +215,8 @@ public class ChangePass extends Activity {
 
                } catch (CryptoHelperException e) {
                        Log.e(TAG, e.toString());
+                       Toast.makeText(this,getString(R.string.crypto_error)
+                               + e.getMessage(), Toast.LENGTH_SHORT).show();
                }
 
                dbHelper.close();
@@ -255,8 +258,8 @@ public class ChangePass extends Activity {
                                row.plainName = ch.decrypt(row.name);
                    } catch (CryptoHelperException e) {
                                if (debug) Log.e(TAG,e.toString());
-                   Toast.makeText(ChangePass.this, e.toString(),
-                           Toast.LENGTH_SHORT).show();
+                               Toast.makeText(this,getString(R.string.crypto_error)
+                                       + e.getMessage(), Toast.LENGTH_SHORT).show();
                                return;
                    }
                }
@@ -270,8 +273,8 @@ public class ChangePass extends Activity {
                                row.plainNote = ch.decrypt(row.note);
                    } catch (CryptoHelperException e) {
                                if (debug) Log.e(TAG,e.toString());
-                   Toast.makeText(ChangePass.this, e.toString(),
-                           Toast.LENGTH_SHORT).show();
+                               Toast.makeText(this,getString(R.string.crypto_error)
+                                       + e.getMessage(), Toast.LENGTH_SHORT).show();
                                return;
                    }
                }
@@ -287,8 +290,8 @@ public class ChangePass extends Activity {
                                row.name = ch.encrypt(row.plainName);
                    } catch (CryptoHelperException e) {
                                if (debug) Log.e(TAG,e.toString());
-                   Toast.makeText(ChangePass.this, e.toString(),
-                           Toast.LENGTH_SHORT).show();
+                               Toast.makeText(this,getString(R.string.crypto_error)
+                                       + e.getMessage(), Toast.LENGTH_SHORT).show();
                                return;
                    }
                }
@@ -302,8 +305,8 @@ public class ChangePass extends Activity {
                                row.note = ch.encrypt(row.plainNote);
                    } catch (CryptoHelperException e) {
                                if (debug) Log.e(TAG,e.toString());
-                   Toast.makeText(ChangePass.this, e.toString(),
-                           Toast.LENGTH_SHORT).show();
+                               Toast.makeText(this,getString(R.string.crypto_error)
+                                       + e.getMessage(), Toast.LENGTH_SHORT).show();
                                return;
                    }
                }
@@ -331,8 +334,8 @@ public class ChangePass extends Activity {
                        dbHelper.storeMasterKey(cryptKey);
                } catch (CryptoHelperException e) {
                        Log.e(TAG, e.toString());
-            Toast.makeText(ChangePass.this, e.toString(),
-                    Toast.LENGTH_SHORT).show();
+                       Toast.makeText(this,getString(R.string.crypto_error)
+                               + e.getMessage(), Toast.LENGTH_SHORT).show();
                        dbHelper.rollback();
                        dbHelper.close();
                        return;
@@ -359,10 +362,11 @@ public class ChangePass extends Activity {
                DBHelper dbHelper= new DBHelper(this);
                String confirmKey = dbHelper.fetchMasterKey();
 
-               CryptoHelper ch = new CryptoHelper(CryptoHelper.EncryptionStrong);
-               ch.setPassword(pass);
+               CryptoHelper ch = new CryptoHelper();
 
                try {
+                       ch.init(CryptoHelper.EncryptionStrong, dbHelper.fetchSalt());
+                       ch.setPassword(pass);
                        ch.decrypt(confirmKey);
                } catch (CryptoHelperException e) {
                        Log.e(TAG, e.toString());
index 3da0d185f9f0d25fa5933c87619265c476e6c686..802e6654a73b4290ed3fac7957afff872470e7dc 100644 (file)
@@ -24,6 +24,7 @@ import java.security.InvalidKeyException;
 import java.security.MessageDigest;\r
 import java.security.NoSuchAlgorithmException;\r
 import java.security.NoSuchProviderException;\r
+import java.security.SecureRandom;\r
 import java.security.spec.InvalidKeySpecException;\r
 \r
 import javax.crypto.BadPaddingException;\r
@@ -36,8 +37,6 @@ import javax.crypto.SecretKeyFactory;
 import javax.crypto.spec.PBEKeySpec;\r
 import javax.crypto.spec.PBEParameterSpec;\r
 \r
-//import org.bouncycastle.jce.provider.BouncyCastleProvider;\r
-\r
 import android.util.Log;\r
 \r
 /**\r
@@ -53,6 +52,7 @@ import android.util.Log;
  */\r
 public class CryptoHelper {\r
 \r
+       private static final boolean debug = true;\r
     private static String TAG = "CryptoHelper";\r
     protected static PBEKeySpec pbeKeySpec;\r
     protected static PBEParameterSpec pbeParamSpec;\r
@@ -71,10 +71,7 @@ public class CryptoHelper {
     protected static Cipher pbeCipher;\r
     private boolean status=false;      // status of the last encrypt/decrypt\r
 \r
-    private static final byte[] salt = {\r
-               (byte)0xfc, (byte)0x76, (byte)0x80, (byte)0xae,\r
-               (byte)0xfd, (byte)0x82, (byte)0xbe, (byte)0xee,\r
-    };\r
+    private static byte[] salt = null; \r
 \r
     private static final int count = 20;\r
 \r
@@ -82,15 +79,22 @@ public class CryptoHelper {
      * Constructor which defaults to a medium encryption level.\r
      */\r
     public CryptoHelper() {\r
-       initialize(EncryptionMedium);\r
+//     initialize(EncryptionMedium);\r
     }\r
     /**\r
      * Constructor which allows the specification of the encryption level.\r
      * \r
-     * @param Strength encryption strength\r
+     * @param strength encryption strength\r
+     * @param salt salt to be used\r
      */\r
-    public CryptoHelper(int Strength) {\r
-        initialize(Strength);\r
+    public void init(int strength, String salt) throws CryptoHelperException {\r
+       try {\r
+                       setSalt(salt);\r
+               initialize(strength);\r
+               } catch (CryptoHelperException e) {\r
+                       e.printStackTrace();\r
+                       throw e;\r
+               }\r
     }\r
     /**\r
      * Initialize the class.  Sets the encryption level for the instance\r
@@ -117,12 +121,32 @@ public class CryptoHelper {
                    Log.e(TAG,"CryptoHelper(): "+e.toString());         \r
                }\r
     }\r
+\r
+    /**\r
+     * Generate a random salt for use with the cipher.\r
+     * \r
+     * @author Randy McEoin\r
+     * @return String version of the 8 byte salt\r
+     */\r
+    public static String generateSalt() throws NoSuchAlgorithmException {\r
+       byte[] salt = new byte[8];\r
+       SecureRandom sr;\r
+               try {\r
+                       sr = SecureRandom.getInstance("SHA1PRNG");\r
+                       sr.nextBytes(salt);\r
+                       if (debug) Log.d(TAG,"generateSalt: salt="+salt.toString());\r
+               } catch (NoSuchAlgorithmException e) {\r
+                       e.printStackTrace();\r
+                       throw e;\r
+               }\r
+               return toHexString(salt);\r
+    }\r
     /**\r
      * @author Isaac Potoczny-Jones\r
      * \r
      * @return null if failure, otherwise hex string version of key\r
      */\r
-       public static String generateMasterKey () {\r
+       public static String generateMasterKey () throws NoSuchAlgorithmException {\r
                try {\r
                        KeyGenerator keygen;\r
                        keygen = KeyGenerator.getInstance("AES");\r
@@ -131,8 +155,8 @@ public class CryptoHelper {
                        return toHexString(genDesKey.getEncoded());\r
                } catch (NoSuchAlgorithmException e) {\r
                        Log.e(TAG,"generateMasterKey(): "+e.toString());\r
+                       throw e;\r
                }\r
-               return null; //error case.\r
        }\r
 \r
     /**\r
@@ -218,6 +242,7 @@ public class CryptoHelper {
      * @throws Exception\r
      */\r
     public void setPassword(String pass) {\r
+       if (debug) Log.d(TAG,"setPassword("+pass+")");\r
                password = pass;\r
                pbeKeySpec = new PBEKeySpec(password.toCharArray());\r
                try {\r
@@ -235,6 +260,20 @@ public class CryptoHelper {
                }\r
     }\r
 \r
+    private void setSalt(String saltIn) throws CryptoHelperException {\r
+       if (saltIn==null) {\r
+                       String msg = "Salt must not be null.";\r
+                   throw new CryptoHelperException(msg);\r
+       }\r
+               byte[] byteSaltIn=hexStringToBytes(saltIn);\r
+\r
+               if (byteSaltIn.length != 8) {\r
+                       String msg = "Salt must be 8 bytes in length.";\r
+                   throw new CryptoHelperException(msg);\r
+               }\r
+               salt=byteSaltIn;\r
+               if (debug) Log.d(TAG,"setSalt: salt="+toHexString(salt));\r
+    }\r
     /**\r
      * encrypt a string\r
      * \r
@@ -245,7 +284,11 @@ public class CryptoHelper {
     public String encrypt(String plaintext) throws CryptoHelperException {\r
                status=false; // assume failure\r
                if(password == null) {\r
-                   String msg = "Must call setPassword before runing encrypt.";\r
+                   String msg = "Must call setPassword before running encrypt.";\r
+                   throw new CryptoHelperException(msg);\r
+               }\r
+               if (salt==null) {\r
+                   String msg = "Must call setSalt before running encrypt.";\r
                    throw new CryptoHelperException(msg);\r
                }\r
                byte[] ciphertext = {};\r
@@ -281,6 +324,10 @@ public class CryptoHelper {
                    String msg = "Must call setPassword before running decrypt.";\r
                    throw new CryptoHelperException(msg);\r
                }\r
+               if (salt==null) {\r
+                   String msg = "Must call setSalt before running decrypt.";\r
+                   throw new CryptoHelperException(msg);\r
+               }\r
        \r
                if ((ciphertext==null) || (ciphertext=="")) {\r
                        return "";\r
@@ -313,4 +360,179 @@ public class CryptoHelper {
     public boolean getStatus() {\r
        return status;\r
     }\r
+    \r
+\r
+    /**\r
+     * encrypt a string using a random session key\r
+     * \r
+     * @author Peli\r
+     * \r
+     * @param plaintext\r
+     * @return encrypted String\r
+     * @throws Exception\r
+     */\r
+    public String encryptWithSessionKey(String plaintext) throws CryptoHelperException {\r
+       Log.i(TAG, "Encrypt with session key");\r
+               status=false; // assume failure\r
+               if(password == null) {\r
+                   String msg = "Must call setPassword before runing encrypt.";\r
+                   throw new CryptoHelperException(msg);\r
+               }\r
+               byte[] cipherSessionKey = {};\r
+               byte[] ciphertext = {};\r
+               \r
+               // First create a session key\r
+               SecretKey sessionKey = null;\r
+               byte[] sessionKeyEncoded = null;\r
+               String sessionKeyString = null;\r
+               try {\r
+                       KeyGenerator keygen;\r
+                       keygen = KeyGenerator.getInstance("AES");\r
+                       keygen.init(256); // needs 96 bytes\r
+                       //keygen.init(128); // needs 64 bytes\r
+                       sessionKey = keygen.generateKey();\r
+                       sessionKeyEncoded = sessionKey.getEncoded();\r
+                       sessionKeyString = new String(sessionKeyEncoded);\r
+               } catch (NoSuchAlgorithmException e) {\r
+                       Log.e(TAG,"generateMasterKey(): "+e.toString());\r
+               }\r
+\r
+               // Convert this to a Pbe key\r
+               PBEKeySpec sessionPbeKeySpec = new PBEKeySpec(sessionKeyString.toCharArray());\r
+               SecretKey sessionPbeKey = null;\r
+               try {\r
+                   sessionPbeKey = keyFac.generateSecret(sessionPbeKeySpec);\r
+               } catch (InvalidKeySpecException e) {\r
+                   Log.e(TAG,"setPassword(): "+e.toString());\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
+               } catch (IllegalBlockSizeException e) {\r
+                   Log.e(TAG,"encryptWithSessionKey(): "+e.toString());\r
+               } catch (BadPaddingException e) {\r
+                   Log.e(TAG,"encryptWithSessionKey(): "+e.toString());\r
+               } catch (InvalidKeyException e) {\r
+                   Log.e(TAG,"encryptWithSessionKey(): "+e.toString());\r
+               } catch (InvalidAlgorithmParameterException e) {\r
+                   Log.e(TAG,"encryptWithSessionKey(): "+e.toString());\r
+               }\r
+               \r
+               // Now encrypt the text using the session key\r
+           try {\r
+                       pbeCipher.init(Cipher.ENCRYPT_MODE, sessionPbeKey, pbeParamSpec);\r
+                   ciphertext = pbeCipher.doFinal(plaintext.getBytes());\r
+                   status=true;\r
+               } catch (IllegalBlockSizeException e) {\r
+                   Log.e(TAG,"encryptWithSessionKey2(): "+e.toString());\r
+               } catch (BadPaddingException e) {\r
+                   Log.e(TAG,"encryptWithSessionKey2(): "+e.toString());\r
+               } catch (InvalidKeyException e) {\r
+                   Log.e(TAG,"encryptWithSessionKey2(): "+e.toString());\r
+               } catch (InvalidAlgorithmParameterException e) {\r
+                   Log.e(TAG,"encryptWithSessionKey2(): "+e.toString());\r
+               }\r
+       \r
+               String stringCipherVersion = "A";\r
+               String stringCipherSessionKey = toHexString(cipherSessionKey);\r
+               String stringCiphertext=toHexString(ciphertext);\r
+               Log.i(TAG, "Length: " + stringCipherSessionKey.length() + ", " + stringCipherSessionKey);\r
+               \r
+               StringBuilder sb = new StringBuilder(stringCipherVersion.length() \r
+                               + stringCipherSessionKey.length() \r
+                               + stringCiphertext.length());\r
+               sb.append(stringCipherVersion);\r
+               sb.append(stringCipherSessionKey);\r
+               sb.append(stringCiphertext);\r
+               return sb.toString();\r
+    }\r
+\r
+    /**\r
+     * unencrypt encrypted string previously encrypted with\r
+     * encryptWithSessionKey()\r
+     * \r
+     * @author Peli\r
+     * \r
+     * @param ciphertext\r
+     * @return decrypted String\r
+     * @throws Exception\r
+     */\r
+    public String decryptWithSessionKey(String ciphertext) throws CryptoHelperException {\r
+               status=false; // assume failure\r
+               if(password == null) {\r
+                   String msg = "Must call setPassword before running decrypt.";\r
+                   throw new CryptoHelperException(msg);\r
+               }\r
+       \r
+               if ((ciphertext==null) || (ciphertext=="")) {\r
+                       return "";\r
+               }\r
+               String cipherVersion = null;\r
+               String cipherSessionKey = null;\r
+               \r
+               // Split cipher into session key and text\r
+               try {\r
+                       cipherVersion = ciphertext.substring(0,1);\r
+                       if (cipherVersion.equals("A")) {\r
+                               cipherSessionKey = ciphertext.substring(1,97); // 64 if init(128) had been chosen\r
+                               ciphertext = ciphertext.substring(97);\r
+                       } else {\r
+                               Log.e(TAG, "Unknown cipher version" + cipherVersion);\r
+                               return "";\r
+                       }\r
+               } catch (IndexOutOfBoundsException e) {\r
+                       Log.e(TAG, "Invalid ciphertext (with session key)");\r
+                       return "";\r
+               }\r
+               \r
+               // Decrypt the session key\r
+               byte[] byteCipherSessionKey=hexStringToBytes(cipherSessionKey);\r
+               byte[] byteSessionKey = {};\r
+               \r
+               try {\r
+                   pbeCipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);\r
+                   byteSessionKey = pbeCipher.doFinal(byteCipherSessionKey);\r
+                   status=true;\r
+               } catch (IllegalBlockSizeException e) {\r
+                   Log.e(TAG,"decrypt(): "+e.toString());\r
+               } catch (BadPaddingException e) {\r
+                   Log.e(TAG,"decrypt(): "+e.toString());\r
+               } catch (InvalidKeyException e) {\r
+                   Log.e(TAG,"decrypt(): "+e.toString());\r
+               } catch (InvalidAlgorithmParameterException e) {\r
+                   Log.e(TAG,"decrypt(): "+e.toString());\r
+               }\r
+\r
+               // Convert the session key into a Pbe key\r
+               String stringSessionKey = new String(byteSessionKey);\r
+               PBEKeySpec sessionPbeKeySpec = new PBEKeySpec(stringSessionKey.toCharArray());\r
+               SecretKey sessionPbeKey = null;\r
+               try {\r
+                   sessionPbeKey = keyFac.generateSecret(sessionPbeKeySpec);\r
+               } catch (InvalidKeySpecException e) {\r
+                   Log.e(TAG,"setPassword(): "+e.toString());\r
+               }\r
+\r
+               // Use the session key to decrypt the text\r
+               byte[] byteCiphertext=hexStringToBytes(ciphertext);\r
+               byte[] plaintext = {};\r
+               \r
+               try {\r
+                   pbeCipher.init(Cipher.DECRYPT_MODE, sessionPbeKey, pbeParamSpec);\r
+                   plaintext = pbeCipher.doFinal(byteCiphertext);\r
+                   status=true;\r
+               } catch (IllegalBlockSizeException e) {\r
+                   Log.e(TAG,"decrypt(): "+e.toString());\r
+               } catch (BadPaddingException e) {\r
+                   Log.e(TAG,"decrypt(): "+e.toString());\r
+               } catch (InvalidKeyException e) {\r
+                   Log.e(TAG,"decrypt(): "+e.toString());\r
+               } catch (InvalidAlgorithmParameterException e) {\r
+                   Log.e(TAG,"decrypt(): "+e.toString());\r
+               }\r
+               \r
+               return new String(plaintext);\r
+    }\r
 }\r
index 181531000290324b507c498637a951513eedb4ff..276e833870751a2e3561b733df1bc5b239ce6c2a 100644 (file)
@@ -44,6 +44,7 @@ public class DBHelper {
     private static final String TABLE_PASSWORDS = "passwords";
     private static final String TABLE_CATEGORIES = "categories";
     private static final String TABLE_MASTER_KEY = "master_key";
+    private static final String TABLE_SALT = "salt";
     private static final String TABLE_PACKAGE_ACCESS = "package_access";
     private static final int DATABASE_VERSION = 4;
     private static String TAG = "DBHelper";
@@ -89,6 +90,10 @@ public class DBHelper {
        "create table " + TABLE_MASTER_KEY + " ("
                + "encryptedkey text not null);";
 
+    private static final String SALT_CREATE = 
+       "create table " + TABLE_SALT + " ("
+               + "salt text not null);";
+
     private SQLiteDatabase db;
     private static boolean needsPrePopulation=false;
     private static boolean needsUpgrade=false;
@@ -148,6 +153,7 @@ public class DBHelper {
                        db.execSQL(PASSWORDS_CREATE);
                        db.execSQL(PACKAGE_ACCESS_CREATE);
                        db.execSQL(MASTER_KEY_CREATE);
+                       db.execSQL(SALT_CREATE);
                } catch (SQLException e)
                {
                        Log.d(TAG,"SQLite exception: " + e.getLocalizedMessage());
@@ -214,6 +220,49 @@ public class DBHelper {
                }
                return version;
     }
+    
+////////// Salt Functions ////////////////
+
+    /**
+     * Store the salt
+     * 
+     * @return String version of salt
+     */
+    public String fetchSalt() {
+       String salt="";
+        try {
+                       Cursor c = db.query(true, TABLE_SALT, new String[] {"salt"},
+                               null, null, null, null, null,null);
+                       if(c.getCount() > 0) {
+                           c.moveToFirst();
+                           salt=c.getString(0);
+                       }
+                       c.close();
+               } catch (SQLException e)
+               {
+                       Log.d(TAG,"SQLite exception: " + e.getLocalizedMessage());
+               }
+               return salt;
+    }
+    
+    /**
+     * Store the salt into the database.
+     * 
+     * @param salt String version of the salt
+     */
+    public void storeSalt(String salt) {
+               ContentValues args = new ContentValues();
+        try {
+                       db.delete(TABLE_SALT, "1=1", null);
+                       args.put("salt", salt);
+                       db.insert(TABLE_SALT, null, args);
+               } catch (SQLException e)
+               {
+                       Log.d(TAG,"SQLite exception: " + e.getLocalizedMessage());
+               }
+    }
+    
+////////// Master Key Functions ////////////////
 
     /**
      * 
@@ -634,12 +683,19 @@ public class DBHelper {
        }
 
        /**
+        * Add a password entry to the database.
+        * PassEntry.id should be set to 0, unless a specific
+        * row id is desired.
         * 
-        * @param entry
+        * @param entry PassEntry
+        * @return long row id of newly added entry 
         */
        public long addPassword(PassEntry entry) {
                long id = -1;
            ContentValues initialValues = new ContentValues();
+           if (entry.id!=0) {
+                       initialValues.put("id", entry.id);
+           }
                initialValues.put("category", entry.category);
                initialValues.put("password", entry.password);
            initialValues.put("description", entry.description);
index c24717749ee0ae218c6702ee91cb3419ce669888..58f69f0a777bdd3a913651d41bc7f1c1359db01c 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id$\r
+/* $Id: FrontDoor.java 1805 2009-01-20 04:05:01Z rmceoin $\r
  * \r
  * Copyright 2007-2008 Steven Osborn\r
  *\r
  */\r
 package org.openintents.safe;\r
 \r
-\r
-import java.util.ArrayList;\r
-\r
-import org.openintents.intents.CryptoIntents;\r
-import org.openintents.safe.dialog.DialogHostingActivity;\r
-import org.openintents.safe.service.ServiceDispatch;\r
-import org.openintents.safe.service.ServiceDispatchImpl;\r
-\r
 import android.app.Activity;\r
-import android.content.ComponentName;\r
-import android.content.Context;\r
 import android.content.Intent;\r
-import android.content.ServiceConnection;\r
-import android.content.SharedPreferences;\r
 import android.os.Bundle;\r
-import android.os.IBinder;\r
-import android.os.RemoteException;\r
-import android.preference.PreferenceManager;\r
-import android.util.Log;\r
-import android.widget.Toast;\r
 \r
 \r
 /**\r
  * FrontDoor Activity\r
  * \r
- * This activity just acts as a splash screen and gets the password from the\r
- * user that will be used to decrypt/encrypt password entries.\r
+ * This activity just makes sure we're entering the front door, not calling encrypt/decrypt\r
+ * or other intents.\r
  * \r
  * @author Steven Osborn - http://steven.bitsetters.com\r
  */\r
 public class FrontDoor extends Activity {\r
 \r
-       private static final boolean debug = false;\r
-       private static String TAG = "FrontDoor";\r
-       \r
-       private static final int REQUEST_CODE_ASK_PASSWORD = 1;\r
-       private static final int REQUEST_CODE_ALLOW_EXTERNAL_ACCESS = 2;\r
-       \r
-\r
-       private DBHelper dbHelper;\r
-       private String masterKey;\r
-       private CryptoHelper ch;\r
-\r
-       // service elements\r
-    private ServiceDispatch service;\r
-    private ServiceDispatchConnection conn;\r
-\r
-    SharedPreferences mPreferences;\r
-       //public static String SERVICE_NAME = "org.openintents.safe.service.ServiceDispatchImpl";\r
-       \r
        /** Called when the activity is first created. */\r
-       @Override\r
-       public void onCreate(Bundle icicle) {\r
-               super.onCreate(icicle);\r
-               mPreferences = PreferenceManager.getDefaultSharedPreferences(this);\r
-               initService(); // start up the PWS service so other applications can query.\r
-       }\r
-\r
-       //currently only handles result from askPassword function.\r
-       protected void onActivityResult (int requestCode, int resultCode, Intent data) {\r
-               if (resultCode == RESULT_OK) {\r
-                       switch (requestCode) {\r
-                       case REQUEST_CODE_ASK_PASSWORD:\r
-                               masterKey = data.getStringExtra("masterKey");\r
-                               String timeout = mPreferences.getString(Preferences.PREFERENCE_LOCK_TIMEOUT, Preferences.PREFERENCE_LOCK_TIMEOUT_DEFAULT_VALUE); \r
-                               int timeoutMinutes=5; // default to 5\r
-                               try {\r
-                                       timeoutMinutes = Integer.valueOf(timeout);\r
-                               } catch (NumberFormatException e) {\r
-                                       Log.d(TAG,"why is lock_timeout busted?");\r
-                               }\r
-                               try {\r
-                                       service.setTimeoutMinutes(timeoutMinutes);\r
-                                       service.setPassword(masterKey); // should already be connected.\r
-                               } catch (RemoteException e1) {\r
-                                       // TODO Auto-generated catch block\r
-                                       e1.printStackTrace();\r
-                               }\r
-\r
-                       boolean externalAccess = mPreferences.getBoolean(Preferences.PREFERENCE_ALLOW_EXTERNAL_ACCESS, false);\r
-                       boolean isLocal = isIntentLocal();\r
-                       \r
-                       if (isLocal || externalAccess) {\r
-                               actionDispatch();\r
-                       } else {\r
-                               // ask first\r
-                               showDialogAllowExternalAccess();\r
-                       }\r
-                               break;\r
-                       case REQUEST_CODE_ALLOW_EXTERNAL_ACCESS:\r
-\r
-                       actionDispatch();\r
-                               break;\r
-                       }\r
-                       \r
-               } else { // resultCode == RESULT_CANCELED\r
-                       setResult(RESULT_CANCELED);\r
-                       finish();\r
-               }\r
-       }\r
-\r
-       /**\r
-        * \r
-        */\r
-       private void showDialogAllowExternalAccess() {\r
-               Intent i = new Intent(this, DialogHostingActivity.class);\r
-               i.putExtra(DialogHostingActivity.EXTRA_DIALOG_ID, DialogHostingActivity.DIALOG_ID_ALLOW_EXTERNAL_ACCESS);\r
-               this.startActivityForResult(i, REQUEST_CODE_ALLOW_EXTERNAL_ACCESS);\r
-       }\r
-       \r
-       protected void actionDispatch () {    \r
-               final Intent thisIntent = getIntent();\r
-        final String action = thisIntent.getAction();\r
-       Intent callbackIntent = getIntent(); \r
-       int callbackResult = RESULT_CANCELED;\r
-               PassList.setMasterKey(masterKey);\r
-        CategoryList.setMasterKey(masterKey);\r
-        if (ch == null) {\r
-               ch = new CryptoHelper(CryptoHelper.EncryptionMedium);\r
-               ch.setPassword(masterKey);\r
-        }\r
-\r
-        boolean externalAccess = mPreferences.getBoolean(Preferences.PREFERENCE_ALLOW_EXTERNAL_ACCESS, false);\r
-\r
-        if (action == null || action.equals(Intent.ACTION_MAIN)){\r
-               //TODO: When launched from debugger, action is null. Other such cases?\r
-               Intent i = new Intent(getApplicationContext(),\r
-                               CategoryList.class);\r
-               startActivity(i);\r
-        } else if (externalAccess){\r
-\r
-               // which action?\r
-               if (action.equals (CryptoIntents.ACTION_ENCRYPT)) {\r
-                       callbackResult = encryptIntent(thisIntent, callbackIntent);\r
-               } else if (action.equals (CryptoIntents.ACTION_DECRYPT)) {\r
-                       callbackResult = decryptIntent(thisIntent, callbackIntent);\r
-               } else if (action.equals (CryptoIntents.ACTION_GET_PASSWORD)\r
-                               || action.equals (CryptoIntents.ACTION_SET_PASSWORD)) {\r
-                       try {\r
-                               callbackIntent = getSetPassword (thisIntent, callbackIntent);\r
-                       callbackResult = RESULT_OK;\r
-                       } catch (CryptoHelperException e) {\r
-                               Log.e(TAG, e.toString(), e);\r
-                               Toast.makeText(FrontDoor.this,\r
-                                               "There was a crypto error while retreiving the requested password: " + e.getMessage(),\r
-                                               Toast.LENGTH_SHORT).show();\r
-                       } catch (Exception e) {\r
-                               Log.e(TAG, e.toString(), e);\r
-                               //TODO: Turn this into a proper error dialog.\r
-                               Toast.makeText(FrontDoor.this,\r
-                                               "There was an error in retreiving the requested password: " + e.getMessage(),\r
-                                               Toast.LENGTH_SHORT).show();\r
-                       }\r
-               }\r
-               setResult(callbackResult, callbackIntent);\r
-        }\r
-        finish();\r
-       }\r
-\r
-\r
-       /**\r
-        * Encrypt all supported fields in the intent and return the result in callbackIntent.\r
-        * \r
-        * @param thisIntent\r
-        * @param callbackIntent\r
-        * @return callbackResult\r
-        */\r
-       private int encryptIntent(final Intent thisIntent, Intent callbackIntent) {\r
-               if (debug)\r
-                       Log.d(TAG, "encryptIntent()");\r
-               \r
-               int callbackResult = RESULT_CANCELED;\r
-               try {\r
-                       if (thisIntent.hasExtra(CryptoIntents.EXTRA_TEXT)) {\r
-                               // get the body text out of the extras.\r
-                               String inputBody = thisIntent.getStringExtra (CryptoIntents.EXTRA_TEXT);\r
-                               String outputBody = "";\r
-                               outputBody = ch.encrypt (inputBody);\r
-                               // stash the encrypted text in the extra\r
-                               callbackIntent.putExtra(CryptoIntents.EXTRA_TEXT, outputBody);\r
-                       }\r
-                       \r
-                       if (thisIntent.hasExtra(CryptoIntents.EXTRA_TEXT_ARRAY)) {\r
-                               String[] in = thisIntent.getStringArrayExtra(CryptoIntents.EXTRA_TEXT_ARRAY);\r
-                               String[] out = new String[in.length];\r
-                               for (int i = 0; i < in.length; i++) {\r
-                                       if (in[i] != null) {\r
-                                               out[i] = ch.encrypt(in[i]);\r
-                                       }\r
-                               }\r
-                               callbackIntent.putExtra(CryptoIntents.EXTRA_TEXT_ARRAY, out);\r
-                       }\r
-                       \r
-                       // Support for binary fields could be added here (like images?)\r
-                       \r
-                       callbackResult = RESULT_OK;\r
-               } catch (CryptoHelperException e) {\r
-                       Log.e(TAG, e.toString());\r
-               }\r
-               return callbackResult;\r
-       }\r
-\r
-       /**\r
-        * Decrypt all supported fields in the intent and return the result in callbackIntent.\r
-        * \r
-        * @param thisIntent\r
-        * @param callbackIntent\r
-        * @return callbackResult\r
-        */\r
-       private int decryptIntent(final Intent thisIntent, Intent callbackIntent) {\r
-       int callbackResult = RESULT_CANCELED;\r
-               try {\r
-\r
-                       if (thisIntent.hasExtra(CryptoIntents.EXTRA_TEXT)) {\r
-                               // get the body text out of the extras.\r
-                               String inputBody = thisIntent.getStringExtra (CryptoIntents.EXTRA_TEXT);\r
-                               String outputBody = "";\r
-                               outputBody = ch.decrypt (inputBody);\r
-                               // stash the encrypted text in the extra\r
-                               callbackIntent.putExtra(CryptoIntents.EXTRA_TEXT, outputBody);\r
-                       }\r
-                       \r
-                       if (thisIntent.hasExtra(CryptoIntents.EXTRA_TEXT_ARRAY)) {\r
-                               String[] in = thisIntent.getStringArrayExtra(CryptoIntents.EXTRA_TEXT_ARRAY);\r
-                               String[] out = new String[in.length];\r
-                               for (int i = 0; i < in.length; i++) {\r
-                                       if (in[i] != null) {\r
-                                               out[i] = ch.decrypt(in[i]);\r
-                                       }\r
-                               }\r
-                               callbackIntent.putExtra(CryptoIntents.EXTRA_TEXT_ARRAY, out);\r
-                       }\r
-                       \r
-                       // Support for binary fields could be added here (like images?)\r
-                       \r
-                       callbackResult = RESULT_OK;\r
-               } catch (CryptoHelperException e) {\r
-                       Log.e(TAG, e.toString());\r
-               }\r
-               return callbackResult;\r
-       }\r
-       \r
-       private Intent getSetPassword (Intent thisIntent, Intent callbackIntent) throws CryptoHelperException, Exception {\r
-               String action = thisIntent.getAction();\r
-        //TODO: Consider moving this elsewhere. Maybe DBHelper? Also move strings to resource.\r
-        //DBHelper dbHelper = new DBHelper(this);\r
-        Log.d(TAG, "GET_or_SET_PASSWORD");\r
-        String username = null;\r
-        String password = null;\r
-\r
-        String clearUniqueName = thisIntent.getStringExtra (CryptoIntents.EXTRA_UNIQUE_NAME);\r
-\r
-        if (clearUniqueName == null) throw new Exception ("EXTRA_UNIQUE_NAME not set.");\r
-        \r
-               if (dbHelper == null) {\r
-               // Need to open DBHelper here, because\r
-                       // onResume() is called after onActivityResult()\r
-                       dbHelper = new DBHelper(this);\r
-               }\r
-\r
-        String uniqueName = ch.encrypt(clearUniqueName);\r
-       PassEntry row = dbHelper.fetchPassword(uniqueName);\r
-       boolean passExists = row.id > 1;\r
-\r
-        String clearCallingPackage = getCallingPackage();\r
-        String callingPackage = ch.encrypt (clearCallingPackage);\r
-       if (passExists) { // check for permission to access this password.\r
-               ArrayList<String> packageAccess = dbHelper.fetchPackageAccess(row.id);\r
-               if (! PassEntry.checkPackageAccess(packageAccess, callingPackage)) {\r
-                       throw new Exception ("It is currently not permissible for this application to request this password.");\r
-               }\r
-            /*TODO: check if this package is in the package_access table corresponding to this password:\r
-             * "Application 'org.syntaxpolice.ServiceTest' wants to access the\r
-                               password for 'opensocial'.\r
-                               [ ] Grant access this time.\r
-                               [ ] Always grant access.\r
-                               [ ] Always grant access to all passwords in org.syntaxpolice.ServiceTest category?\r
-                               [ ] Don't grant access"\r
-             */\r
-       }\r
-       \r
-        if (action.equals (CryptoIntents.ACTION_GET_PASSWORD)) {\r
-               if (passExists) {\r
-                       username = ch.decrypt(row.username);\r
-                       password = ch.decrypt(row.password);\r
-               } else throw new Exception ("Could not find password with the unique name: " + clearUniqueName);\r
-\r
-               // stashing the return values:\r
-               callbackIntent.putExtra(CryptoIntents.EXTRA_USERNAME, username);\r
-               callbackIntent.putExtra(CryptoIntents.EXTRA_PASSWORD, password);\r
-        } else if (action.equals (CryptoIntents.ACTION_SET_PASSWORD)) {\r
-            String clearUsername  = thisIntent.getStringExtra (CryptoIntents.EXTRA_USERNAME);\r
-            String clearPassword = thisIntent.getStringExtra (CryptoIntents.EXTRA_PASSWORD);\r
-            if (clearPassword == null) {\r
-                       throw new Exception ("PASSWORD extra must be set.");\r
-            }  \r
-            row.username = ch.encrypt(clearUsername == null ? "" : clearUsername);\r
-            row.password = ch.encrypt(clearPassword);\r
-            // since this package is setting the password, it automatically gets access to it:\r
-               if (passExists) { //exists already \r
-                       if (clearUsername.equals("") && clearPassword.equals("")) {\r
-                               dbHelper.deletePassword(row.id);\r
-                       } else {\r
-                               dbHelper.updatePassword(row.id, row);\r
-                       }\r
-               } else {// add a new one\r
-                row.uniqueName = uniqueName;\r
-                   row.description=uniqueName; //for display purposes\r
-                // TODO: Should we send these fields in extras also?  If so, probably not using \r
-                // the openintents namespace?  If another application were to implement a keystore\r
-                // they might not want to use these.\r
-                   row.website = ""; \r
-                   row.note = "";\r
-\r
-                   String category = ch.encrypt("Application Data");\r
-                   CategoryEntry c = new CategoryEntry();\r
-                   c.name = category;\r
-                   row.category = dbHelper.addCategory(c); //doesn't add category if it already exists\r
-                   row.id = dbHelper.addPassword(row);\r
-               }  \r
-               dbHelper.addPackageAccess(row.id, callingPackage);//already encrypted\r
-    \r
-        }\r
-        return (callbackIntent);\r
-       }\r
-       \r
-       @Override\r
-       protected void onPause() {\r
-               super.onPause();\r
-\r
-               if (debug)\r
-                       Log.d(TAG, "onPause()");\r
-\r
-               releaseService();\r
-               dbHelper.close();\r
-               dbHelper = null;\r
-       }\r
-\r
-       @Override\r
-       protected void onResume() {\r
-               super.onPause();\r
-\r
-               if (debug)\r
-                       Log.d(TAG, "onResume()");\r
-               if (dbHelper == null) {\r
-                       dbHelper = new DBHelper(this);\r
-               }\r
-\r
-       }\r
-       \r
-       @Override\r
-       protected void onDestroy() {\r
-               super.onDestroy();\r
-               releaseService();\r
-       }\r
-\r
-\r
-       //--------------------------- service stuff ------------\r
-       private void initService() {\r
-\r
-        boolean isLocal = isIntentLocal();\r
-               conn = new ServiceDispatchConnection(isLocal);\r
-               Intent i = new Intent();\r
-               i.setClass(this, ServiceDispatchImpl.class);\r
-               startService(i);\r
-               bindService( i, conn, Context.BIND_AUTO_CREATE);\r
-       }\r
-\r
-       /**\r
-        * @return\r
-        */\r
-       private boolean isIntentLocal() {\r
-               String action = getIntent().getAction();\r
-        boolean isLocal = action == null || action.equals(Intent.ACTION_MAIN);\r
-               return isLocal;\r
-       }\r
-\r
-       private void releaseService() {\r
-               if (conn != null ) {\r
-                       unbindService( conn );\r
-                       conn = null;\r
-               }\r
-       }\r
-\r
-       class ServiceDispatchConnection implements ServiceConnection\r
-       {\r
-               boolean askPassIsLocal = false;\r
-               public ServiceDispatchConnection (Boolean isLocal) {\r
-                       askPassIsLocal = isLocal;\r
-               }\r
-               public void onServiceConnected(ComponentName className, \r
-                               IBinder boundService )\r
-               {\r
-                       service = ServiceDispatch.Stub.asInterface((IBinder)boundService);\r
-                       \r
-                       boolean promptforpassword = getIntent().getBooleanExtra(CryptoIntents.EXTRA_PROMPT, true);\r
-                       if (debug) Log.d(TAG, "Prompt for password: " + promptforpassword);\r
-                       try {\r
-                               if (service.getPassword() == null) {\r
-                                       if (promptforpassword) {\r
-                                               if (debug) Log.d(TAG, "ask for password");\r
-                                               // the service isn't running\r
-                                               Intent askPass = new Intent(getApplicationContext(),\r
-                                                               AskPassword.class);\r
-       \r
-                                               final Intent thisIntent = getIntent();\r
-                                               String inputBody = thisIntent.getStringExtra (CryptoIntents.EXTRA_TEXT);\r
-       \r
-                                               askPass.putExtra (CryptoIntents.EXTRA_TEXT, inputBody);\r
-                                               askPass.putExtra (AskPassword.EXTRA_IS_LOCAL, askPassIsLocal);\r
-                                               //TODO: Is there a way to make sure all the extras are set?     \r
-                                               startActivityForResult (askPass, REQUEST_CODE_ASK_PASSWORD);\r
-                                       } else {\r
-                                               if (debug) Log.d(TAG, "ask for password");\r
-                                               // Don't prompt but cancel\r
-                                               setResult(RESULT_CANCELED);\r
-                                       finish();\r
-                                       }\r
-\r
-                               } else {\r
-                                       if (debug) Log.d(TAG, "service already started");\r
-                                       //service already started, so don't need to ask pw.\r
-\r
-                               boolean externalAccess = mPreferences.getBoolean(Preferences.PREFERENCE_ALLOW_EXTERNAL_ACCESS, false);\r
-                               \r
-                               if (askPassIsLocal || externalAccess) {\r
-                                               masterKey = service.getPassword();\r
-                                               actionDispatch();\r
-                               } else {\r
-                                       showDialogAllowExternalAccess();\r
-                               }\r
-                               }\r
-                       } catch (RemoteException e) {\r
-                               Log.d(TAG, e.toString());\r
-                       }\r
-                       Log.d( TAG,"onServiceConnected" );\r
-               }\r
-               \r
-               public void onServiceDisconnected(ComponentName className)\r
-               {\r
-                       service = null;\r
-                       Log.d( TAG,"onServiceDisconnected" );\r
-               }\r
-       };\r
-\r
-}\r
+    @Override\r
+    public void onCreate(Bundle icicle) {\r
+       super.onCreate(icicle);\r
+       final Intent thisIntent = getIntent();\r
+       final String action = thisIntent.getAction();\r
+       if (action == null || action.equals(Intent.ACTION_MAIN)){\r
+               //TODO: When launched from debugger, action is null. Other such cases?\r
+               Intent i = new Intent(getApplicationContext(),\r
+                               IntentHandler.class);\r
+               startActivity(i);\r
+       }  // otherwise, do not start intents, those must be protected by permissions\r
+       finish();\r
+    }\r
+}
\ No newline at end of file
diff --git a/src/org/openintents/safe/IntentHandler.java b/src/org/openintents/safe/IntentHandler.java
new file mode 100644 (file)
index 0000000..092b65e
--- /dev/null
@@ -0,0 +1,531 @@
+/* $Id$\r
+ * \r
+ * Copyright 2007-2008 Steven Osborn\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
+\r
+import java.util.ArrayList;\r
+\r
+import org.openintents.intents.CryptoIntents;\r
+import org.openintents.safe.dialog.DialogHostingActivity;\r
+import org.openintents.safe.service.ServiceDispatch;\r
+import org.openintents.safe.service.ServiceDispatchImpl;\r
+\r
+import android.app.Activity;\r
+import android.content.ComponentName;\r
+import android.content.Context;\r
+import android.content.Intent;\r
+import android.content.ServiceConnection;\r
+import android.content.SharedPreferences;\r
+import android.os.Bundle;\r
+import android.os.IBinder;\r
+import android.os.RemoteException;\r
+import android.preference.PreferenceManager;\r
+import android.util.Log;\r
+import android.widget.Toast;\r
+\r
+\r
+/**\r
+ * FrontDoor Activity\r
+ * \r
+ * This activity just acts as a splash screen and gets the password from the\r
+ * user that will be used to decrypt/encrypt password entries.\r
+ * \r
+ * @author Steven Osborn - http://steven.bitsetters.com\r
+ */\r
+public class IntentHandler extends Activity {\r
+\r
+       private static final boolean debug = !false;\r
+       private static String TAG = "IntentHandler";\r
+       \r
+       private static final int REQUEST_CODE_ASK_PASSWORD = 1;\r
+       private static final int REQUEST_CODE_ALLOW_EXTERNAL_ACCESS = 2;\r
+       \r
+\r
+       private DBHelper dbHelper;\r
+       private String salt;\r
+       private String masterKey;\r
+       private CryptoHelper ch;\r
+\r
+       // service elements\r
+    private ServiceDispatch service;\r
+    private ServiceDispatchConnection conn;\r
+       private Intent mServiceIntent;\r
+\r
+    SharedPreferences mPreferences;\r
+       //public static String SERVICE_NAME = "org.openintents.safe.service.ServiceDispatchImpl";\r
+       \r
+       /** Called when the activity is first created. */\r
+       @Override\r
+       public void onCreate(Bundle icicle) {\r
+               super.onCreate(icicle);\r
+               mServiceIntent = null;\r
+               mPreferences = PreferenceManager.getDefaultSharedPreferences(this);\r
+               initService(); // start up the PWS service so other applications can query.\r
+       }\r
+\r
+       \r
+       //currently only handles result from askPassword function.\r
+       protected void onActivityResult (int requestCode, int resultCode, Intent data) {\r
+               if (debug) Log.d(TAG, "onActivityResult: requestCode == " + requestCode + ", resultCode == " + resultCode);\r
+       \r
+               switch (requestCode) {\r
+               case REQUEST_CODE_ASK_PASSWORD:\r
+                       if (resultCode == RESULT_OK) {\r
+\r
+                               if (service == null) {\r
+                                       mServiceIntent = data;\r
+                                       // setServiceParametersFromExtrasAndDispatchAction() is called in onServiceConnected.\r
+                                       return;\r
+                               }\r
+                               \r
+                               setServiceParametersFromExtrasAndDispatchAction(data);\r
+                               \r
+                       } else { // resultCode == RESULT_CANCELED\r
+                               setResult(RESULT_CANCELED);\r
+                               finish();\r
+                       }\r
+                       break;\r
+               case REQUEST_CODE_ALLOW_EXTERNAL_ACCESS:\r
+                       // Check again, regardless whether user pressed "OK" or "Cancel".\r
+                       // Also, DialogHostingActivity never returns a resultCode different than\r
+                       // RESULT_CANCELED.\r
+                       if (service == null) {\r
+                               if (debug) Log.i(TAG, "actionDispatch called later");\r
+                               // actionDispatch() is called in onServiceConnected.\r
+                       } else if (salt == null) {\r
+                               try {\r
+                               salt = service.getSalt();\r
+                                       masterKey = service.getPassword();\r
+                                       actionDispatch();\r
+                               } catch (RemoteException e) {\r
+                                       Log.d(TAG, e.toString());\r
+                                       // Not successful...\r
+                                       finish();\r
+                               }\r
+                       } else {\r
+                               if (debug) Log.i(TAG, "actionDispatch called right now");\r
+                               actionDispatch();\r
+                       }\r
+                       break;\r
+               }\r
+                       \r
+       }\r
+\r
+       /**\r
+        * @param data\r
+        */\r
+       private void setServiceParametersFromExtrasAndDispatchAction(Intent data) {\r
+               salt = data.getStringExtra("salt");\r
+               masterKey = data.getStringExtra("masterKey");\r
+               String timeout = mPreferences.getString(Preferences.PREFERENCE_LOCK_TIMEOUT, Preferences.PREFERENCE_LOCK_TIMEOUT_DEFAULT_VALUE); \r
+               int timeoutMinutes=5; // default to 5\r
+               try {\r
+                       timeoutMinutes = Integer.valueOf(timeout);\r
+               } catch (NumberFormatException e) {\r
+                       Log.d(TAG,"why is lock_timeout busted?");\r
+               }\r
+               \r
+               try {\r
+                       service.setTimeoutMinutes(timeoutMinutes);\r
+                       service.setSalt(salt);\r
+                       service.setPassword(masterKey); // should already be connected.\r
+               } catch (RemoteException e1) {\r
+                       // TODO Auto-generated catch block\r
+                       e1.printStackTrace();\r
+               }\r
+\r
+               boolean externalAccess = mPreferences.getBoolean(Preferences.PREFERENCE_ALLOW_EXTERNAL_ACCESS, false);\r
+               boolean isLocal = isIntentLocal();\r
+               \r
+               if (isLocal || externalAccess) {\r
+                       actionDispatch();\r
+               } else {\r
+                       // ask first\r
+                       if (debug) Log.d(TAG, "onActivityResult: showDialogAllowExternalAccess()");\r
+                       showDialogAllowExternalAccess();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * \r
+        */\r
+       private void showDialogAllowExternalAccess() {\r
+               Intent i = new Intent(this, DialogHostingActivity.class);\r
+               i.putExtra(DialogHostingActivity.EXTRA_DIALOG_ID, DialogHostingActivity.DIALOG_ID_ALLOW_EXTERNAL_ACCESS);\r
+               this.startActivityForResult(i, REQUEST_CODE_ALLOW_EXTERNAL_ACCESS);\r
+       }\r
+       \r
+       protected void actionDispatch () {    \r
+               final Intent thisIntent = getIntent();\r
+        final String action = thisIntent.getAction();\r
+       Intent callbackIntent = getIntent(); \r
+       int callbackResult = RESULT_CANCELED;\r
+               PassList.setSalt(salt);\r
+        CategoryList.setSalt(salt);\r
+               PassList.setMasterKey(masterKey);\r
+        CategoryList.setMasterKey(masterKey);\r
+        if (ch == null) {\r
+               ch = new CryptoHelper();\r
+        }\r
+        try {\r
+                       ch.init(CryptoHelper.EncryptionMedium,salt);\r
+               ch.setPassword(masterKey);\r
+               } catch (CryptoHelperException e1) {\r
+                       e1.printStackTrace();\r
+                       Toast.makeText(this, getString(R.string.crypto_error)\r
+                               + e1.getMessage(), Toast.LENGTH_SHORT).show();\r
+                       return;\r
+               }\r
+\r
+        boolean externalAccess = mPreferences.getBoolean(Preferences.PREFERENCE_ALLOW_EXTERNAL_ACCESS, false);\r
+\r
+        if (action == null || action.equals(Intent.ACTION_MAIN)){\r
+               //TODO: When launched from debugger, action is null. Other such cases?\r
+               Intent i = new Intent(getApplicationContext(),\r
+                               CategoryList.class);\r
+               startActivity(i);\r
+        } else if (externalAccess){\r
+\r
+               // which action?\r
+               if (action.equals (CryptoIntents.ACTION_ENCRYPT)) {\r
+                       callbackResult = encryptIntent(thisIntent, callbackIntent);\r
+               } else if (action.equals (CryptoIntents.ACTION_DECRYPT)) {\r
+                       callbackResult = decryptIntent(thisIntent, callbackIntent);\r
+               } else if (action.equals (CryptoIntents.ACTION_GET_PASSWORD)\r
+                               || action.equals (CryptoIntents.ACTION_SET_PASSWORD)) {\r
+                       try {\r
+                               callbackIntent = getSetPassword (thisIntent, callbackIntent);\r
+                       callbackResult = RESULT_OK;\r
+                       } catch (CryptoHelperException e) {\r
+                               Log.e(TAG, e.toString(), e);\r
+                               Toast.makeText(IntentHandler.this,\r
+                                               "There was a crypto error while retreiving the requested password: " + e.getMessage(),\r
+                                               Toast.LENGTH_SHORT).show();\r
+                       } catch (Exception e) {\r
+                               Log.e(TAG, e.toString(), e);\r
+                               //TODO: Turn this into a proper error dialog.\r
+                               Toast.makeText(IntentHandler.this,\r
+                                               "There was an error in retreiving the requested password: " + e.getMessage(),\r
+                                               Toast.LENGTH_SHORT).show();\r
+                       }\r
+               }\r
+               setResult(callbackResult, callbackIntent);\r
+        }\r
+        finish();\r
+       }\r
+\r
+\r
+       /**\r
+        * Encrypt all supported fields in the intent and return the result in callbackIntent.\r
+        * \r
+        * This is supposed to be called by outside functions, so we encrypt using a random session key.\r
+        * \r
+        * @param thisIntent\r
+        * @param callbackIntent\r
+        * @return callbackResult\r
+        */\r
+       private int encryptIntent(final Intent thisIntent, Intent callbackIntent) {\r
+               if (debug)\r
+                       Log.d(TAG, "encryptIntent()");\r
+               \r
+               int callbackResult = RESULT_CANCELED;\r
+               try {\r
+                       if (thisIntent.hasExtra(CryptoIntents.EXTRA_TEXT)) {\r
+                               // get the body text out of the extras.\r
+                               String inputBody = thisIntent.getStringExtra (CryptoIntents.EXTRA_TEXT);\r
+                               String outputBody = "";\r
+                               outputBody = ch.encryptWithSessionKey (inputBody);\r
+                               // stash the encrypted text in the extra\r
+                               callbackIntent.putExtra(CryptoIntents.EXTRA_TEXT, outputBody);\r
+                       }\r
+                       \r
+                       if (thisIntent.hasExtra(CryptoIntents.EXTRA_TEXT_ARRAY)) {\r
+                               String[] in = thisIntent.getStringArrayExtra(CryptoIntents.EXTRA_TEXT_ARRAY);\r
+                               String[] out = new String[in.length];\r
+                               for (int i = 0; i < in.length; i++) {\r
+                                       if (in[i] != null) {\r
+                                               out[i] = ch.encryptWithSessionKey(in[i]);\r
+                                       }\r
+                               }\r
+                               callbackIntent.putExtra(CryptoIntents.EXTRA_TEXT_ARRAY, out);\r
+                       }\r
+                       \r
+                       // Support for binary fields could be added here (like images?)\r
+                       \r
+                       callbackResult = RESULT_OK;\r
+               } catch (CryptoHelperException e) {\r
+                       Log.e(TAG, e.toString());\r
+               }\r
+               return callbackResult;\r
+       }\r
+\r
+       /**\r
+        * Decrypt all supported fields in the intent and return the result in callbackIntent.\r
+        * \r
+        * @param thisIntent\r
+        * @param callbackIntent\r
+        * @return callbackResult\r
+        */\r
+       private int decryptIntent(final Intent thisIntent, Intent callbackIntent) {\r
+       int callbackResult = RESULT_CANCELED;\r
+               try {\r
+\r
+                       if (thisIntent.hasExtra(CryptoIntents.EXTRA_TEXT)) {\r
+                               // get the body text out of the extras.\r
+                               String inputBody = thisIntent.getStringExtra (CryptoIntents.EXTRA_TEXT);\r
+                               String outputBody = "";\r
+                               outputBody = ch.decryptWithSessionKey (inputBody);\r
+                               // stash the encrypted text in the extra\r
+                               callbackIntent.putExtra(CryptoIntents.EXTRA_TEXT, outputBody);\r
+                       }\r
+                       \r
+                       if (thisIntent.hasExtra(CryptoIntents.EXTRA_TEXT_ARRAY)) {\r
+                               String[] in = thisIntent.getStringArrayExtra(CryptoIntents.EXTRA_TEXT_ARRAY);\r
+                               String[] out = new String[in.length];\r
+                               for (int i = 0; i < in.length; i++) {\r
+                                       if (in[i] != null) {\r
+                                               out[i] = ch.decryptWithSessionKey(in[i]);\r
+                                       }\r
+                               }\r
+                               callbackIntent.putExtra(CryptoIntents.EXTRA_TEXT_ARRAY, out);\r
+                       }\r
+                       \r
+                       // Support for binary fields could be added here (like images?)\r
+                       \r
+                       callbackResult = RESULT_OK;\r
+               } catch (CryptoHelperException e) {\r
+                       Log.e(TAG, e.toString());\r
+               }\r
+               return callbackResult;\r
+       }\r
+       \r
+       private Intent getSetPassword (Intent thisIntent, Intent callbackIntent) throws CryptoHelperException, Exception {\r
+               String action = thisIntent.getAction();\r
+        //TODO: Consider moving this elsewhere. Maybe DBHelper? Also move strings to resource.\r
+        //DBHelper dbHelper = new DBHelper(this);\r
+        if (debug) Log.d(TAG, "GET_or_SET_PASSWORD");\r
+        String username = null;\r
+        String password = null;\r
+\r
+        String clearUniqueName = thisIntent.getStringExtra (CryptoIntents.EXTRA_UNIQUE_NAME);\r
+\r
+        if (clearUniqueName == null) throw new Exception ("EXTRA_UNIQUE_NAME not set.");\r
+        \r
+               if (dbHelper == null) {\r
+               // Need to open DBHelper here, because\r
+                       // onResume() is called after onActivityResult()\r
+                       dbHelper = new DBHelper(this);\r
+               }\r
+\r
+        String uniqueName = ch.encrypt(clearUniqueName);\r
+       PassEntry row = dbHelper.fetchPassword(uniqueName);\r
+       boolean passExists = row.id > 1;\r
+\r
+        String clearCallingPackage = getCallingPackage();\r
+        String callingPackage = ch.encrypt (clearCallingPackage);\r
+       if (passExists) { // check for permission to access this password.\r
+               ArrayList<String> packageAccess = dbHelper.fetchPackageAccess(row.id);\r
+               if (! PassEntry.checkPackageAccess(packageAccess, callingPackage)) {\r
+                       throw new Exception ("It is currently not permissible for this application to request this password.");\r
+               }\r
+            /*TODO: check if this package is in the package_access table corresponding to this password:\r
+             * "Application 'org.syntaxpolice.ServiceTest' wants to access the\r
+                               password for 'opensocial'.\r
+                               [ ] Grant access this time.\r
+                               [ ] Always grant access.\r
+                               [ ] Always grant access to all passwords in org.syntaxpolice.ServiceTest category?\r
+                               [ ] Don't grant access"\r
+             */\r
+       }\r
+       \r
+        if (action.equals (CryptoIntents.ACTION_GET_PASSWORD)) {\r
+               if (passExists) {\r
+                       username = ch.decrypt(row.username);\r
+                       password = ch.decrypt(row.password);\r
+               } else throw new Exception ("Could not find password with the unique name: " + clearUniqueName);\r
+\r
+               // stashing the return values:\r
+               callbackIntent.putExtra(CryptoIntents.EXTRA_USERNAME, username);\r
+               callbackIntent.putExtra(CryptoIntents.EXTRA_PASSWORD, password);\r
+        } else if (action.equals (CryptoIntents.ACTION_SET_PASSWORD)) {\r
+            String clearUsername  = thisIntent.getStringExtra (CryptoIntents.EXTRA_USERNAME);\r
+            String clearPassword = thisIntent.getStringExtra (CryptoIntents.EXTRA_PASSWORD);\r
+            if (clearPassword == null) {\r
+                       throw new Exception ("PASSWORD extra must be set.");\r
+            }  \r
+            row.username = ch.encrypt(clearUsername == null ? "" : clearUsername);\r
+            row.password = ch.encrypt(clearPassword);\r
+            // since this package is setting the password, it automatically gets access to it:\r
+               if (passExists) { //exists already \r
+                       if (clearUsername.equals("") && clearPassword.equals("")) {\r
+                               dbHelper.deletePassword(row.id);\r
+                       } else {\r
+                               dbHelper.updatePassword(row.id, row);\r
+                       }\r
+               } else {// add a new one\r
+                row.uniqueName = uniqueName;\r
+                   row.description=uniqueName; //for display purposes\r
+                // TODO: Should we send these fields in extras also?  If so, probably not using \r
+                // the openintents namespace?  If another application were to implement a keystore\r
+                // they might not want to use these.\r
+                   row.website = ""; \r
+                   row.note = "";\r
+\r
+                   String category = ch.encrypt("Application Data");\r
+                   CategoryEntry c = new CategoryEntry();\r
+                   c.name = category;\r
+                   row.category = dbHelper.addCategory(c); //doesn't add category if it already exists\r
+                   row.id = 0; // entry is truly new\r
+                   row.id = dbHelper.addPassword(row);\r
+               }  \r
+               ArrayList<String> packageAccess = dbHelper.fetchPackageAccess(row.id);\r
+               if (! PassEntry.checkPackageAccess(packageAccess, callingPackage)) {\r
+               dbHelper.addPackageAccess(row.id, callingPackage);\r
+               }\r
+    \r
+        }\r
+        return (callbackIntent);\r
+       }\r
+       \r
+       @Override\r
+       protected void onPause() {\r
+               super.onPause();\r
+\r
+               if (debug)\r
+                       Log.d(TAG, "onPause()");\r
+\r
+               releaseService();\r
+               dbHelper.close();\r
+               dbHelper = null;\r
+       }\r
+\r
+       @Override\r
+       protected void onResume() {\r
+               super.onPause();\r
+\r
+               if (debug)\r
+                       Log.d(TAG, "onResume()");\r
+               if (dbHelper == null) {\r
+                       dbHelper = new DBHelper(this);\r
+               }\r
+\r
+       }\r
+       \r
+       @Override\r
+       protected void onDestroy() {\r
+               super.onDestroy();\r
+               releaseService();\r
+       }\r
+\r
+\r
+       //--------------------------- service stuff ------------\r
+       private void initService() {\r
+\r
+        boolean isLocal = isIntentLocal();\r
+               conn = new ServiceDispatchConnection(isLocal);\r
+               Intent i = new Intent();\r
+               i.setClass(this, ServiceDispatchImpl.class);\r
+               startService(i);\r
+               bindService( i, conn, Context.BIND_AUTO_CREATE);\r
+       }\r
+\r
+       /**\r
+        * @return\r
+        */\r
+       private boolean isIntentLocal() {\r
+               String action = getIntent().getAction();\r
+        boolean isLocal = action == null || action.equals(Intent.ACTION_MAIN);\r
+               return isLocal;\r
+       }\r
+\r
+       private void releaseService() {\r
+               if (conn != null ) {\r
+                       unbindService( conn );\r
+                       conn = null;\r
+               }\r
+       }\r
+\r
+       class ServiceDispatchConnection implements ServiceConnection\r
+       {\r
+               boolean askPassIsLocal = false;\r
+               public ServiceDispatchConnection (Boolean isLocal) {\r
+                       askPassIsLocal = isLocal;\r
+               }\r
+               public void onServiceConnected(ComponentName className, \r
+                               IBinder boundService )\r
+               {\r
+                       service = ServiceDispatch.Stub.asInterface((IBinder)boundService);\r
+                       \r
+                       if (mServiceIntent != null) {\r
+                               setServiceParametersFromExtrasAndDispatchAction(mServiceIntent);\r
+                               mServiceIntent = null;\r
+                               return;\r
+                       }\r
+                       \r
+                       boolean promptforpassword = getIntent().getBooleanExtra(CryptoIntents.EXTRA_PROMPT, true);\r
+                       if (debug) Log.d(TAG, "Prompt for password: " + promptforpassword);\r
+                       try {\r
+                               if (service.getPassword() == null) {\r
+                                       if (promptforpassword) {\r
+                                               if (debug) Log.d(TAG, "ask for password");\r
+                                               // the service isn't running\r
+                                               Intent askPass = new Intent(getApplicationContext(),\r
+                                                               AskPassword.class);\r
+       \r
+                                               final Intent thisIntent = getIntent();\r
+                                               String inputBody = thisIntent.getStringExtra (CryptoIntents.EXTRA_TEXT);\r
+       \r
+                                               askPass.putExtra (CryptoIntents.EXTRA_TEXT, inputBody);\r
+                                               askPass.putExtra (AskPassword.EXTRA_IS_LOCAL, askPassIsLocal);\r
+                                               //TODO: Is there a way to make sure all the extras are set?     \r
+                                               startActivityForResult (askPass, REQUEST_CODE_ASK_PASSWORD);\r
+                                       } else {\r
+                                               if (debug) Log.d(TAG, "ask for password");\r
+                                               // Don't prompt but cancel\r
+                                               setResult(RESULT_CANCELED);\r
+                                       finish();\r
+                                       }\r
+\r
+                               } else {\r
+                                       if (debug) Log.d(TAG, "service already started");\r
+                                       //service already started, so don't need to ask pw.\r
+\r
+                               boolean externalAccess = mPreferences.getBoolean(Preferences.PREFERENCE_ALLOW_EXTERNAL_ACCESS, false);\r
+                               \r
+                               if (askPassIsLocal || externalAccess) {\r
+                                       salt = service.getSalt();\r
+                                               masterKey = service.getPassword();\r
+                                               actionDispatch();\r
+                               } else {\r
+                                       if (debug) Log.d(TAG, "onServiceConnected: showDialogAllowExternalAccess()");\r
+                                       showDialogAllowExternalAccess();\r
+                               }\r
+                               }\r
+                       } catch (RemoteException e) {\r
+                               Log.d(TAG, e.toString());\r
+                       }\r
+                       if (debug) Log.d( TAG,"onServiceConnected" );\r
+               }\r
+               \r
+               public void onServiceDisconnected(ComponentName className)\r
+               {\r
+                       service = null;\r
+                       if (debug) Log.d( TAG,"onServiceDisconnected" );\r
+               }\r
+       };\r
+\r
+}\r
index 456257f9a58f1fe20b84c9971640a43793c68f81..725e7515feea30b7aafe247855159dcd2be0ce9c 100644 (file)
@@ -73,7 +73,14 @@ public class PassEdit extends Activity {
                setTitle(title);
 
                ch = new CryptoHelper();
-               ch.setPassword(PassList.getMasterKey());
+               try {
+                       ch.init(CryptoHelper.EncryptionMedium,PassList.getSalt());
+                       ch.setPassword(PassList.getMasterKey());
+               } catch (CryptoHelperException e1) {
+                       e1.printStackTrace();
+                       Toast.makeText(this,getString(R.string.crypto_error)
+                               + e1.getMessage(), Toast.LENGTH_SHORT).show();
+               }
 
                if (dbHelper == null) {
                        dbHelper = new DBHelper(this);
@@ -107,13 +114,21 @@ public class PassEdit extends Activity {
                                cb.setText(passwordText.getText().toString());
 
                                Intent i = new Intent(Intent.ACTION_VIEW);
-                               Uri u = Uri.parse(websiteText.getText().toString());
+                               String link = websiteText.getText().toString();
+                               Uri u = Uri.parse(link);
                                i.setData(u);
                                try {
                                        startActivity(i);
                                } catch (ActivityNotFoundException e) {
-                                       Toast.makeText(PassEdit.this, R.string.invalid_website,
-                                               Toast.LENGTH_SHORT).show();
+                                       // Let's try to catch the most common mistake: omitting http:
+                                       u = Uri.parse("http://" + link);
+                                       i.setData(u);
+                                       try {
+                                               startActivity(i);
+                                       } catch (ActivityNotFoundException e2) {
+                                               Toast.makeText(PassEdit.this, R.string.invalid_website,
+                                                       Toast.LENGTH_SHORT).show();
+                                       }
                                }
                        }
                });
@@ -179,6 +194,7 @@ public class PassEdit extends Activity {
                entryEdited = true;
 
                if (RowId == null || RowId == -1) {
+                       entry.id = 0;   // brand new entry
                        RowId = dbHelper.addPassword(entry);
                } else {
                        PassEntry storedEntry = dbHelper.fetchPassword (RowId);
index ca873b0055084151a0ea12f2929e2b2dd508e18d..6a7112a1d09230e0d1c0d7aeab1643469066113d 100644 (file)
@@ -31,7 +31,7 @@ public class PassEntry extends Object {
     public String username;\r
     public String website;\r
     public String uniqueName;\r
-   // public ArrayList<String> packageAccess;\r
+    public ArrayList<String> packageAccess;\r
     public String note;\r
     public String plainPassword;\r
     public String plainDescription;\r
index 1e284f8d36376650ae89b2f718ca1b7d56b9f829..4ac36468ba7f3ed74d3015d6597152c0487e3482 100644 (file)
@@ -78,6 +78,7 @@ public class PassList extends ListActivity {
     private static Long CategoryId=null;
     private Intent restartTimerIntent;
 
+    private static String salt;
     private static String masterKey;                   
 
     private List<PassEntry> rows;
@@ -203,7 +204,15 @@ public class PassList extends ListActivity {
                if(masterKey == null) {
                    masterKey = "";
                }
-               ch.setPassword(masterKey);
+               try {
+                       ch.init(CryptoHelper.EncryptionMedium, salt);
+                       ch.setPassword(masterKey);
+               } catch (CryptoHelperException e1) {
+                       e1.printStackTrace();
+                       Toast.makeText(this,getString(R.string.crypto_error)
+                                       + e1.getMessage(), Toast.LENGTH_SHORT).show();
+                       return;
+               }
        
                List<String> items = new ArrayList<String>();
                rows = dbHelper.fetchAllRows(CategoryId);
@@ -263,6 +272,14 @@ public class PassList extends ListActivity {
                return super.onCreateOptionsMenu(menu);
     }
 
+    static void setSalt(String saltIn) {
+               salt = saltIn;
+    }
+
+    static String getSalt() {
+               return salt;
+    }
+
     static void setMasterKey(String key) {
                masterKey = key;
     }
@@ -400,12 +417,14 @@ public class PassList extends ListActivity {
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent i) {
        super.onActivityResult(requestCode, resultCode, i);
+       //Log.d(TAG, "onActivityResult. requestCode: " + requestCode + ", resultCode: " + resultCode);
 
        if (dbHelper == null) {
                    dbHelper = new DBHelper(this);
                }
        if (((requestCode==REQUEST_VIEW_PASSWORD)&&(PassView.entryEdited)) ||
                ((requestCode==REQUEST_EDIT_PASSWORD)&&(PassEdit.entryEdited)) ||
+               ((requestCode==REQUEST_ADD_PASSWORD)&&(PassEdit.entryEdited)) ||
                        (resultCode==RESULT_OK)) {
                fillData();
        }
@@ -423,11 +442,14 @@ public class PassList extends ListActivity {
                if (ch==null) {
                        ch=new CryptoHelper();
                }
-               ch.setPassword(masterKey);
-           try {
+               try {
+                       ch.init(CryptoHelper.EncryptionMedium, salt);
+                       ch.setPassword(masterKey);
                        category.plainName = ch.decrypt(category.name);
            } catch (CryptoHelperException e) {
                        Log.e(TAG,e.toString());
+                       Toast.makeText(this,getString(R.string.crypto_error)
+                               + e.getMessage(), Toast.LENGTH_SHORT).show();
            }
            return category.plainName;
     }
index a45c6660d94f1a6da6eab317a820bda4f6196d2f..8554341da3baf815a3e8f4bcbc8c502fd365fee6 100644 (file)
  */
 package org.openintents.safe;
 
+import java.util.ArrayList;
+
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.ActivityNotFoundException;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.Uri;
 import android.os.Bundle;
 import android.text.ClipboardManager;
@@ -41,7 +46,7 @@ import android.widget.Toast;
  */
 public class PassView extends Activity {
 
-       private static boolean debug = true;
+       private static boolean debug = false;
        private static String TAG = "PassView";
 
        public static final int EDIT_PASSWORD_INDEX = Menu.FIRST;
@@ -55,6 +60,8 @@ public class PassView extends Activity {
        private EditText websiteText;
        private EditText noteText;
        private TextView lastEditedText;
+       private TextView uniqueNameText;
+       private TextView packageAccessText;
        private Long RowId;
        private Long CategoryId;
        private DBHelper dbHelper = null;
@@ -70,7 +77,14 @@ public class PassView extends Activity {
                setTitle(title);
 
                ch = new CryptoHelper();
-               ch.setPassword(PassList.getMasterKey());
+               try {
+                       ch.init(CryptoHelper.EncryptionMedium, PassList.getSalt());
+                       ch.setPassword(PassList.getMasterKey());
+               } catch (CryptoHelperException e1) {
+                       e1.printStackTrace();
+                       Toast.makeText(this,getString(R.string.crypto_error)
+                               + e1.getMessage(), Toast.LENGTH_SHORT).show();
+               }
 
                if (dbHelper == null) {
                        dbHelper = new DBHelper(this);
@@ -84,6 +98,8 @@ public class PassView extends Activity {
                passwordText = (EditText) findViewById(R.id.password);
                noteText = (EditText) findViewById(R.id.note);
                lastEditedText = (TextView) findViewById(R.id.last_edited);
+               uniqueNameText = (TextView) findViewById(R.id.uniquename);
+               packageAccessText = (TextView) findViewById(R.id.packageaccess);
                
                entryEdited=false;
 
@@ -268,6 +284,8 @@ public class PassView extends Activity {
                                String cryptUsername = row.username;
                                String cryptPass = row.password;
                                String cryptNote = row.note;
+                               String cryptUniqueName = row.uniqueName;
+                       ArrayList<String> packageAccess = dbHelper.fetchPackageAccess(row.id);
                                try {
                                        descriptionText.setText(ch.decrypt(cryptDesc));
                                        websiteText.setText(ch.decrypt(cryptWebsite));
@@ -280,7 +298,33 @@ public class PassView extends Activity {
                                        } else {
                                                lastEdited=getString(R.string.last_edited_unknown);
                                        }
-                                       lastEditedText.setText(getString(R.string.last_edited)+" "+lastEdited);
+                                       lastEditedText.setText(getString(R.string.last_edited)+": "+lastEdited);
+                                       if (cryptUniqueName!=null) {
+                                               String plainUniqueName=ch.decrypt(cryptUniqueName);
+                                               if (plainUniqueName!="") {
+                                                       uniqueNameText.setText(getString(R.string.uniquename)+
+                                                                       ": "+plainUniqueName);
+                                               }
+                                       }
+                                       String packages="";
+                                       if (packageAccess!=null) {
+                                               for (String packageName : packageAccess) {
+                                                       String plainPackageName=ch.decrypt(packageName);
+                                                       PackageManager pm=getPackageManager();
+                                                       String appLabel="";
+                                                       try {
+                                                               ApplicationInfo ai=pm.getApplicationInfo(plainPackageName,0);
+                                                               appLabel=pm.getApplicationLabel(ai).toString();
+                                                       } catch (NameNotFoundException e) {
+                                                               appLabel="("+getString(R.string.not_found)+")";
+                                                       }
+                                                       packages+=plainPackageName+" "+appLabel+" ";
+                                               }
+                                       }
+                                       if (packages!="") {
+                                               packageAccessText.setText(getString(R.string.package_access)+
+                                                               ": "+packages);
+                                       }
                                } catch (CryptoHelperException e) {
                                        Log.e(TAG, e.toString());
                                }
index 7256b7faa1ba4742389bd7d8d8990dfc204d286d..b55165b3203928fff3c81aee95843bc3d0c918a8 100644 (file)
@@ -8,6 +8,7 @@ public class Preferences extends PreferenceActivity {
        public static final String PREFERENCE_ALLOW_EXTERNAL_ACCESS = "external_access";
        public static final String PREFERENCE_LOCK_TIMEOUT = "lock_timeout";
        public static final String PREFERENCE_LOCK_TIMEOUT_DEFAULT_VALUE = "5";
+       public static final String PREFERENCE_FIRST_TIME_WARNING = "first_time_warning";
        
     @Override
     protected void onCreate(Bundle savedInstanceState) {
index 0cef10bf4ab7e5494715236bd71d69137bfb973e..a14f545bc6660ccf292181a38839c5aedae06816 100644 (file)
@@ -33,7 +33,9 @@ import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.os.Bundle;
+import android.preference.PreferenceManager;
 import android.util.Log;
 import android.view.View;
 import android.view.animation.Animation;
@@ -52,6 +54,9 @@ public class Restore extends Activity {
        private String masterKey="";
        private String filename=null;
        private RestoreDataSet restoreDataSet=null;
+       private boolean firstTime=false;
+
+    public static final String KEY_FIRST_TIME = "first_time";  // Intent keys
 
        @Override
        public void onCreate(Bundle icicle) {
@@ -59,7 +64,13 @@ public class Restore extends Activity {
 
                if (debug) Log.d(TAG,"onCreate()");
 
-               if (!CategoryList.isSignedIn()) {
+               firstTime = icicle != null ? icicle.getBoolean(Restore.KEY_FIRST_TIME) : false;
+               if (firstTime == false) {
+                   Bundle extras = getIntent().getExtras();            
+                   firstTime = extras != null ? extras.getBoolean(Restore.KEY_FIRST_TIME) : false;
+               }
+
+               if ((!firstTime) && (!CategoryList.isSignedIn())) {
                        Intent frontdoor = new Intent(this, FrontDoor.class);
                        startActivity(frontdoor);               
                        finish();
@@ -116,7 +127,7 @@ public class Restore extends Activity {
                
                if (debug) Log.d(TAG,"onResume()");
                
-               if (!CategoryList.isSignedIn()) {
+               if ((!firstTime) && (!CategoryList.isSignedIn())) {
                        Intent frontdoor = new Intent(this, FrontDoor.class);
                        startActivity(frontdoor);               
                        finish();
@@ -195,15 +206,20 @@ public class Restore extends Activity {
                                Toast.LENGTH_LONG).show();
                        return false;
                }
-               CryptoHelper ch=new CryptoHelper(CryptoHelper.EncryptionStrong);
-               ch.setPassword(masterPassword);
+               CryptoHelper ch=new CryptoHelper();
                
+               String salt=restoreDataSet.getSalt();
                String masterKeyEncrypted=restoreDataSet.getMasterKeyEncrypted();
                masterKey="";
                try {
+                       ch.init(CryptoHelper.EncryptionStrong, salt);
+                       ch.setPassword(masterPassword);
                        masterKey = ch.decrypt(masterKeyEncrypted);
                } catch (CryptoHelperException e) {
                        Log.e(TAG,e.toString());
+                       Toast.makeText(this, getString(R.string.crypto_error)
+                               + e.getMessage(), Toast.LENGTH_SHORT).show();
+                       return false;
                }
                if (ch.getStatus()==false) {
                        Toast.makeText(Restore.this, getString(R.string.restore_decrypt_error),
@@ -214,8 +230,16 @@ public class Restore extends Activity {
 
                        return false;
                }
-               ch=new CryptoHelper(CryptoHelper.EncryptionMedium);
-               ch.setPassword(masterKey);
+               ch=new CryptoHelper();
+               try {
+                       ch.init(CryptoHelper.EncryptionMedium, salt);
+                       ch.setPassword(masterKey);
+               } catch (CryptoHelperException e1) {
+                       e1.printStackTrace();
+                       Toast.makeText(this, getString(R.string.crypto_error)
+                               + e1.getMessage(), Toast.LENGTH_SHORT).show();
+                       return false;
+               }
                
                String firstCategory="";
                try {
@@ -261,7 +285,10 @@ public class Restore extends Activity {
                dbHelper.beginTransaction();
                dbHelper.deleteDatabase();
 
+               dbHelper.storeSalt(restoreDataSet.getSalt());
                dbHelper.storeMasterKey(restoreDataSet.getMasterKeyEncrypted());
+               CategoryList.setSalt(restoreDataSet.getSalt());
+               PassList.setSalt(restoreDataSet.getSalt());
                CategoryList.setMasterKey(masterKey);
                PassList.setMasterKey(masterKey);
                for (CategoryEntry category : restoreDataSet.getCategories()) {
@@ -271,7 +298,13 @@ public class Restore extends Activity {
                int totalPasswords=0;
                for (PassEntry password : restoreDataSet.getPass()) {
                        totalPasswords++;
-                       dbHelper.addPassword(password);
+                       long rowid=dbHelper.addPassword(password);
+                       if (password.packageAccess!=null) {
+                               for (String packageName : password.packageAccess) {
+                                       if (debug) Log.d(TAG,"packageName="+packageName);
+                                       dbHelper.addPackageAccess(rowid, packageName);
+                               }
+                       }
                }
                dbHelper.commit();
                dbHelper.close();
@@ -279,14 +312,16 @@ public class Restore extends Activity {
                Toast.makeText(Restore.this, getString(R.string.restore_complete)+
                        " "+Integer.toString(totalPasswords),
                        Toast.LENGTH_LONG).show();
+               
+
+               // Don't need to show warning anymore to back up, because user has used
+               // restore already.
+               SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putBoolean(Preferences.PREFERENCE_FIRST_TIME_WARNING, true);
+        editor.commit();
 
                setResult(RESULT_OK);
                finish();
-
-               /*
-               Message m = new Message();
-               m.what = CategoryList.MSG_FILLDATA;
-               myViewHandler.sendMessage(m);
-               */
        }
 }
index 5bfab40a1effc0f71654814cefb6ff61f18f63d8..67c749140388d880d09ec623a5dc64886c3adab3 100644 (file)
@@ -17,6 +17,7 @@
 package org.openintents.safe;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 import android.util.Log;
 
@@ -27,11 +28,14 @@ public class RestoreDataSet {
 
        private int version = 0;
        private String date = null;
+       private String salt = null;
        private String masterKeyEncrypted = null;
        private Long currentCategoryId = new Long(0);
        private CategoryEntry currentCategory = null;
        private ArrayList<CategoryEntry> categoryEntries = new ArrayList<CategoryEntry>();
        private PassEntry currentEntry = null;
+       private String currentRowID;
+       private String currentPackageAccess;
        private ArrayList<PassEntry> passEntries = new ArrayList<PassEntry>();
        private int totalEntries = 0;
        
@@ -47,6 +51,12 @@ public class RestoreDataSet {
        public void setDate(String extractedDate) {
                date = extractedDate;
        }
+       public String getSalt() {
+               return salt;
+       }
+       public void setSalt(String extractedSalt) {
+               salt = extractedSalt;
+       }
        public String getMasterKeyEncrypted() {
                return masterKeyEncrypted;
        }
@@ -74,23 +84,36 @@ public class RestoreDataSet {
        public void newEntry() {
                currentEntry = new PassEntry();
                currentEntry.category = currentCategoryId;
+               currentRowID="";
                currentEntry.description="";
                currentEntry.website="";
                currentEntry.username="";
                currentEntry.password="";
                currentEntry.note="";
+               currentEntry.uniqueName="";
+               currentEntry.packageAccess=null;
+               currentPackageAccess="";
        }
        public void storeEntry() {
                // only add an entry if we had all the fields
                if (debug) Log.d(TAG,currentEntry.description+" "+currentEntry.website+" "+
                                currentEntry.username+" "+currentEntry.password+" "+
-                               currentEntry.note);
+                               currentEntry.note+" "+currentPackageAccess);
                if ((currentEntry != null) &&
-                       (currentEntry.description!="") &&
-                       (currentEntry.website!="") &&
-                       (currentEntry.username!="") &&
-                       (currentEntry.password!="") &&
-                       (currentEntry.note!="")) {
+                       (currentEntry.description!="")) {
+                       try {
+                               currentEntry.id=Long.parseLong(currentRowID);
+                       } catch (NumberFormatException e) {
+                               currentEntry.id=0;
+                       }
+                       if (currentPackageAccess!="") {
+                               // strip the brackets [ and ]
+                               String packageList = currentPackageAccess.substring(1,
+                                               currentPackageAccess.length()-1);
+                               String[] packages = packageList.split(",");
+                               currentEntry.packageAccess=new ArrayList<String>(Arrays.asList(packages));
+                               if (debug) Log.d(TAG,"packageAccess="+currentEntry.packageAccess.toString());
+                       }
                        passEntries.add(currentEntry);
                        totalEntries++;
                }
@@ -99,6 +122,12 @@ public class RestoreDataSet {
        public int getTotalEntries() {
                return totalEntries;
        }
+       public void setRowID(String extractedRowID) {
+               if (debug) Log.d(TAG,"setRowID("+extractedRowID+")");
+               if (currentEntry != null) {
+                       currentRowID += extractedRowID;
+               }
+       }
        public void setDescription(String extractedDescription) {
                if (debug) Log.d(TAG,"setDescription("+extractedDescription+")");
                if (currentEntry != null) {
@@ -126,4 +155,15 @@ public class RestoreDataSet {
                        currentEntry.note += extractedNote;
                }
        }
+       public void setUniqueName(String extractedUniqueName) {
+               if (currentEntry != null) {
+                       currentEntry.uniqueName += extractedUniqueName;
+               }
+       }
+       public void setPackageAccess(String extractedPackageAccess) {
+               if (debug) Log.d(TAG,"setPackageAccess("+extractedPackageAccess+")");
+               if (currentPackageAccess != null) {
+                       currentPackageAccess += extractedPackageAccess;
+               }
+       }
 }
index 0dc766c4d6e78186e02ddc3d817275fa7a02f7c2..c31067dee7ebfa0a814355b9a837aa5f69bd6a9d 100644 (file)
@@ -32,14 +32,18 @@ public class RestoreHandler extends DefaultHandler {
     // ===========================================================
     
     private boolean in_oisafe = false;
+    private boolean in_salt = false;
     private boolean in_masterkey = false;
     private boolean in_category = false;
     private boolean in_entry = false;
+    private boolean in_rowid = false;
     private boolean in_description = false;
     private boolean in_website = false;
     private boolean in_username = false;
     private boolean in_password = false;
     private boolean in_note = false;
+    private boolean in_uniquename = false;
+    private boolean in_packageaccess = false;
     
     private RestoreDataSet myRestoreDataSet = new RestoreDataSet();
 
@@ -85,6 +89,11 @@ public class RestoreHandler extends DefaultHandler {
 
                        if (debug) Log.d(TAG,"found OISafe "+version+" date "+date);
                        
+               }else if (in_oisafe && localName.equals("Salt")) {
+                       in_salt = true;
+
+                       if (debug) Log.d(TAG,"found Salt");
+
                }else if (in_oisafe && localName.equals("MasterKey")) {
                        in_masterkey = true;
 
@@ -105,6 +114,8 @@ public class RestoreHandler extends DefaultHandler {
 
                        if (debug) Log.d(TAG,"found Entry");
 
+               }else if (in_entry && localName.equals("RowID")) {
+                       in_rowid = true;
                }else if (in_entry && localName.equals("Description")) {
                        in_description = true;
                }else if (in_entry && localName.equals("Website")) {
@@ -115,6 +126,10 @@ public class RestoreHandler extends DefaultHandler {
                        in_password = true;
                }else if (in_entry && localName.equals("Note")) {
                        in_note = true;
+               }else if (in_entry && localName.equals("UniqueName")) {
+                       in_uniquename = true;
+               }else if (in_entry && localName.equals("PackageAccess")) {
+                       in_packageaccess = true;
                }
        }
     
@@ -127,6 +142,8 @@ public class RestoreHandler extends DefaultHandler {
        
                if (localName.equals("OISafe")) {
                        in_oisafe = false;
+               }else if (in_oisafe && localName.equals("Salt")) {
+                       in_salt = false;
                }else if (in_oisafe && localName.equals("MasterKey")) {
                        in_masterkey = false;
                }else if (in_oisafe && localName.equals("Category")) {
@@ -139,6 +156,8 @@ public class RestoreHandler extends DefaultHandler {
 
                        myRestoreDataSet.storeEntry();
                        
+               }else if (in_entry && localName.equals("RowID")) {
+                       in_rowid = false;
                }else if (in_entry && localName.equals("Description")) {
                        in_description = false;
                }else if (in_entry && localName.equals("Website")) {
@@ -149,6 +168,10 @@ public class RestoreHandler extends DefaultHandler {
                        in_password = false;
                }else if (in_entry && localName.equals("Note")) {
                        in_note = false;
+               }else if (in_entry && localName.equals("UniqueName")) {
+                       in_uniquename = false;
+               }else if (in_entry && localName.equals("PackageAccess")) {
+                       in_packageaccess = false;
                }
        }
 
@@ -156,23 +179,26 @@ public class RestoreHandler extends DefaultHandler {
         * &lt;tag>characters&lt;/tag> */
        @Override
        public void characters(char ch[], int start, int length) {
-               if (in_masterkey){
+               if (in_salt){
+                       myRestoreDataSet.setSalt(new String(ch, start, length));
+               } else if (in_masterkey){
                        myRestoreDataSet.setMasterKeyEncrypted(new String(ch, start, length));
-               }
-               if (in_description){
+               } else if (in_rowid){
+                       myRestoreDataSet.setRowID(new String(ch, start, length));
+               } else if (in_description){
                        myRestoreDataSet.setDescription(new String(ch, start, length));
-               }
-               if (in_website){
+               } else if (in_website){
                        myRestoreDataSet.setWebsite(new String(ch, start, length));
-               }
-               if (in_username){
+               } else if (in_username){
                        myRestoreDataSet.setUsername(new String(ch, start, length));
-               }
-               if (in_password){
+               } else if (in_password){
                        myRestoreDataSet.setPassword(new String(ch, start, length));
-               }
-               if (in_note){
+               } else if (in_note){
                        myRestoreDataSet.setNote(new String(ch, start, length));
+               } else if (in_uniquename){
+                       myRestoreDataSet.setUniqueName(new String(ch, start, length));
+               } else if (in_packageaccess){
+                       myRestoreDataSet.setPackageAccess(new String(ch, start, length));
                }
        } 
 }
index ff42d42a2f4448d10d8254ffbc4c3241c37401d5..0c1a69795e8b5ac0e5c8d793f5672cfc5f090f5e 100644 (file)
@@ -16,23 +16,35 @@ import android.widget.EditText;
 \r
 public class DialogHostingActivity extends Activity {\r
 \r
-       private static final String TAG = "FilenameActivity";\r
+       private static final String TAG = "DialogHostingActivity";\r
+       private static final boolean debug = false;\r
 \r
        public static final int DIALOG_ID_SAVE = 1;\r
        public static final int DIALOG_ID_OPEN = 2;\r
        public static final int DIALOG_ID_NO_FILE_MANAGER_AVAILABLE = 3;\r
        public static final int DIALOG_ID_ALLOW_EXTERNAL_ACCESS = 4;\r
+       public static final int DIALOG_ID_FIRST_TIME_WARNING = 5;\r
        \r
        public static final String EXTRA_DIALOG_ID = "org.openintents.notepad.extra.dialog_id";\r
+\r
+       /**\r
+        * Whether dialog is simply pausing while hidden by another activity\r
+        * or when configuration changes.\r
+        * If this is false, then we can safely finish this activity if a dialog\r
+        * gets dismissed.\r
+        */\r
+       private boolean mIsPausing = false;\r
        \r
     EditText mEditText;\r
     \r
        @Override\r
        protected void onCreate(Bundle savedInstanceState) {\r
                super.onCreate(savedInstanceState);\r
+               if (debug) Log.d(TAG, "onCreate");\r
                \r
                Intent i = getIntent();\r
-               if (i != null) {\r
+               if (i != null && savedInstanceState == null) {\r
+                       if (debug) Log.d(TAG, "new dialog");\r
                        int dialogId = i.getIntExtra(EXTRA_DIALOG_ID, 0);\r
                        switch (dialogId) {\r
                        case DIALOG_ID_SAVE:\r
@@ -50,10 +62,12 @@ public class DialogHostingActivity extends Activity {
                                Log.i(TAG, "Show allow access dialog");\r
                                showDialog(DIALOG_ID_ALLOW_EXTERNAL_ACCESS);\r
                                break;\r
+                       case DIALOG_ID_FIRST_TIME_WARNING:\r
+                               Log.i(TAG, "Show first time warning dialog");\r
+                               showDialog(DIALOG_ID_FIRST_TIME_WARNING);\r
+                               break;\r
                        }\r
                }\r
-               \r
-               \r
        }\r
 \r
 \r
@@ -101,51 +115,54 @@ public class DialogHostingActivity extends Activity {
 \r
        @Override\r
        protected Dialog onCreateDialog(int id) {\r
+               if (debug) Log.d(TAG, "onCreateDialog");\r
 \r
+               Dialog dialog = null;\r
+               \r
                switch (id) {\r
                case DIALOG_ID_SAVE:\r
-                       return new FilenameDialog(this);\r
+                       dialog = new FilenameDialog(this);\r
+                       break;\r
                case DIALOG_ID_OPEN:\r
-                       return new FilenameDialog(this);\r
+                       dialog = new FilenameDialog(this);\r
+                       break;\r
                case DIALOG_ID_NO_FILE_MANAGER_AVAILABLE:\r
                        Log.i(TAG, "fmd - create");\r
-                       return new GetFromMarketDialog(this, \r
+                       dialog = new GetFromMarketDialog(this, \r
                                        RD.string.filemanager_not_available,\r
                                        RD.string.filemanager_get_oi_filemanager,\r
                                        RD.string.filemanager_market_uri);\r
+                       break;\r
                case DIALOG_ID_ALLOW_EXTERNAL_ACCESS:\r
-                       return new AllowExternalAccessDialog(this);\r
+                       dialog = new AllowExternalAccessDialog(this);\r
+                       break;\r
+               case DIALOG_ID_FIRST_TIME_WARNING:\r
+                       dialog = new FirstTimeWarningDialog(this);\r
+                       break;\r
+               }\r
+               if (dialog == null) {\r
+                       dialog = super.onCreateDialog(id);\r
+               }\r
+               if (dialog != null) {\r
+                       dialog.setOnDismissListener(mDismissListener);\r
                }\r
-               return null;\r
+               return dialog;\r
        }\r
 \r
        @Override\r
        protected void onPrepareDialog(int id, Dialog dialog) {\r
-               FilenameDialog fd;\r
+               super.onPrepareDialog(id, dialog);\r
                \r
-               dialog.setOnDismissListener(mDismissListener);\r
+               if (debug) Log.d(TAG, "onPrepareDialog");\r
+               \r
+               //dialog.setOnDismissListener(mDismissListener);\r
                \r
                switch (id) {\r
                case DIALOG_ID_SAVE:\r
-                       fd = (FilenameDialog) dialog;\r
-                       //fd.setTitle(R.string.menu_save_to_sdcard);\r
-                       \r
                        break;\r
                case DIALOG_ID_OPEN:\r
-                       fd = (FilenameDialog) dialog;\r
-                       //fd.setTitle(R.string.menu_open_from_sdcard);\r
                        break;\r
-                       \r
                case DIALOG_ID_NO_FILE_MANAGER_AVAILABLE:\r
-                       Log.i(TAG, "fmd - prepare");\r
-                       /*\r
-                       GetFileManagerFromMarketDialog gd = (GetFileManagerFromMarketDialog) dialog;\r
-                       gd.setMessageResource(R.string.filemanager_not_available);\r
-                       gd.setInfoResources(R.string.filemanager_get_oi_filemanager, \r
-                                       R.string.filemanager_market_uri, \r
-                                       R.drawable.ic_launcher_folder_small, \r
-                                       R.string.update_error);\r
-                                       */\r
                        break;\r
                }\r
        }\r
@@ -153,9 +170,27 @@ public class DialogHostingActivity extends Activity {
        OnDismissListener mDismissListener = new OnDismissListener() {\r
                \r
                public void onDismiss(DialogInterface dialoginterface) {\r
-                       DialogHostingActivity.this.finish();\r
+                       if (debug) Log.d(TAG, "Dialog dismissed. Pausing: " + mIsPausing);\r
+                       if (!mIsPausing) {\r
+                               if (debug) Log.d(TAG, "finish");\r
+                               // Dialog has been dismissed by user.\r
+                               DialogHostingActivity.this.finish();\r
+                       } else {\r
+                               // Probably just a screen orientation change. Don't finish yet.\r
+                               // Dialog has been dismissed by system.\r
+                       }\r
                }\r
                \r
        };\r
+\r
+       @Override\r
+       protected void onSaveInstanceState(Bundle outState) {\r
+               super.onSaveInstanceState(outState);\r
+               if (debug) Log.d(TAG, "onSaveInstanceState");\r
+               mIsPausing = true;\r
+               if (debug) Log.d(TAG, "onSaveInstanceState. Pausing: " + mIsPausing);\r
+       }\r
+       \r
+       \r
        \r
 }\r
diff --git a/src/org/openintents/safe/dialog/FirstTimeWarningDialog.java b/src/org/openintents/safe/dialog/FirstTimeWarningDialog.java
new file mode 100644 (file)
index 0000000..a1ef3b5
--- /dev/null
@@ -0,0 +1,47 @@
+package org.openintents.safe.dialog;\r
+\r
+import org.openintents.safe.Preferences;\r
+import org.openintents.safe.R;\r
+\r
+import android.app.AlertDialog;\r
+import android.content.Context;\r
+import android.content.DialogInterface;\r
+import android.content.SharedPreferences;\r
+import android.content.DialogInterface.OnClickListener;\r
+import android.os.Bundle;\r
+import android.preference.PreferenceManager;\r
+import android.view.LayoutInflater;\r
+import android.view.View;\r
+import android.widget.CheckBox;\r
+\r
+public class FirstTimeWarningDialog extends AlertDialog implements OnClickListener {\r
+       private static final String TAG = "FilenameDialog";\r
+\r
+    private static final String BUNDLE_TAGS = "tags";\r
+    \r
+       protected static final int DIALOG_ID_NO_FILE_MANAGER_AVAILABLE = 2;\r
+    \r
+    Context mContext;\r
+    \r
+    CheckBox mCheckBox;\r
+    \r
+    public FirstTimeWarningDialog(Context context) {\r
+        super(context);\r
+        mContext = context;\r
+        \r
+        setTitle(context.getText(R.string.dialog_title_first_time_warning));\r
+        setButton(context.getText(android.R.string.ok), (OnClickListener) null);\r
+        //setButton2(context.getText(android.R.string.cancel), (OnClickListener) null);\r
+        setIcon(android.R.drawable.ic_dialog_alert);\r
+        setMessage(context.getText(R.string.dialog_summary_first_time_warning));\r
+\r
+    }\r
+    \r
+          \r
+       public void onClick(DialogInterface dialog, int which) {\r
+       if (which == BUTTON1) {\r
+               // User pressed OK\r
+       }\r
+               \r
+       }\r
+}\r
index ba0600843eb1d6c4b598103e44ace16e5bb2d4e4..02d6e7b802186f1c1d9cf38824e9c935516adc62 100644 (file)
@@ -17,6 +17,8 @@
 package org.openintents.safe.service;\r
 \r
 interface ServiceDispatch {\r
+  void setSalt (String saltIn);\r
+  String getSalt ();\r
   void setPassword (String masterPasswordIn);\r
   String getPassword ();\r
   String encrypt (String clearText);\r
index 97a1c29441b67332e73ec4d2dca3381d8a53d01a..50ee4684e9005a46020236a09e60965d5cd7b310 100644 (file)
@@ -35,8 +35,10 @@ import android.util.Log;
 import android.os.CountDownTimer;
 
 public class ServiceDispatchImpl extends Service {
+       private static boolean debug = true;
        private static String TAG = "ServiceDispatchIMPL";
        private CryptoHelper ch;
+       private String salt;
        private String masterKey;
     private CountDownTimer t;
     private int timeoutMinutes = 5;
@@ -57,7 +59,10 @@ public class ServiceDispatchImpl extends Service {
       
       mIntentReceiver = new BroadcastReceiver() {
           public void onReceive(Context context, Intent intent) {
-              if (intent.getAction().equals(CryptoIntents.ACTION_RESTART_TIMER)) {
+              if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+                if (debug) Log.d(TAG,"caught ACTION_SCREEN_OFF");
+                stopSelf();
+              } else if (intent.getAction().equals(CryptoIntents.ACTION_RESTART_TIMER)) {
                        restartTimer();
               }
           }
@@ -65,9 +70,10 @@ public class ServiceDispatchImpl extends Service {
 
       IntentFilter filter = new IntentFilter();
       filter.addAction (CryptoIntents.ACTION_RESTART_TIMER);
+      filter.addAction (Intent.ACTION_SCREEN_OFF);
       registerReceiver(mIntentReceiver, filter);
       
-         Log.d( TAG,"onCreate" );
+         if (debug) Log.d( TAG,"onCreate" );
     }
     
     @Override
@@ -81,29 +87,29 @@ public class ServiceDispatchImpl extends Service {
          Intent intent = new Intent(CryptoIntents.ACTION_CRYPTO_LOGGED_OUT);
          sendBroadcast(intent);
          
-         Log.d( TAG,"onDestroy" );
+         if (debug) Log.d( TAG,"onDestroy" );
     }
     
     private void startTimer () {
-               Log.d(TAG,"startTimer with timeoutUntilStop="+timeoutUntilStop);
+               if (debug) Log.d(TAG,"startTimer with timeoutUntilStop="+timeoutUntilStop);
        t = new CountDownTimer(timeoutUntilStop, timeoutUntilStop) {
                public void onTick(long millisUntilFinished) {
                        //doing nothing.
-                         Log.d(TAG, "tick: " + millisUntilFinished );
+                         if (debug) Log.d(TAG, "tick: " + millisUntilFinished );
                }
 
                public void onFinish() {
-                       Log.d(TAG,"onFinish()");
+                       if (debug) Log.d(TAG,"onFinish()");
                        stopSelf(); // countdown is over, stop the service.
                }
        };
        t.start();
-               Log.d(TAG, "Timer started with: " + timeoutUntilStop );
+               if (debug) Log.d(TAG, "Timer started with: " + timeoutUntilStop );
     }
        
        public void restartTimer () {
        // must be started with startTimer first.
-               Log.d(TAG,"timer restarted");
+               if (debug) Log.d(TAG,"timer restarted");
        if (t != null) {
                t.cancel();
                t.start();
@@ -138,10 +144,24 @@ public class ServiceDispatchImpl extends Service {
                return (clearText);
        }
 
+       public void setSalt (String saltIn){
+                       salt = saltIn;
+       }
+
+               public String getSalt() {
+                       return salt;
+               }
+
        public void setPassword (String masterKeyIn){
                startTimer(); //should be initial timer start
-                       ch = new CryptoHelper(CryptoHelper.EncryptionMedium);
-                       ch.setPassword(masterKeyIn);
+                       ch = new CryptoHelper();
+                       try {
+                               ch.init(CryptoHelper.EncryptionMedium, salt);
+                               ch.setPassword(masterKeyIn);
+                       } catch (CryptoHelperException e) {
+                               e.printStackTrace();
+                               return;
+                       }
                        masterKey = masterKeyIn;
                        
                        ServiceNotification.setNotification(ServiceDispatchImpl.this);