diff --git a/res/layout/entry_view.xml b/res/layout/entry_view.xml
index 85e437705..598bf1444 100644
--- a/res/layout/entry_view.xml
+++ b/res/layout/entry_view.xml
@@ -82,12 +82,38 @@
android:layout_alignLeft="@id/entry_title"
android:layout_alignParentRight="true"
android:layout_alignTop="@id/entry_created_label"/>
+
+
+
+
+
+
Twitter:
OK
Created:
+Modified:
+Accessed:
diff --git a/src/com/android/keepass/Database.java b/src/com/android/keepass/Database.java
index cab3df78e..3f9c566be 100644
--- a/src/com/android/keepass/Database.java
+++ b/src/com/android/keepass/Database.java
@@ -19,8 +19,10 @@
*/
package com.android.keepass;
+import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.HashMap;
@@ -34,12 +36,15 @@ import org.phoneid.keepassj2me.PwGroup;
import org.phoneid.keepassj2me.PwManager;
import com.android.keepass.keepasslib.InvalidKeyFileException;
+import com.android.keepass.keepasslib.PwManagerOutput;
+import com.android.keepass.keepasslib.PwManagerOutput.PwManagerOutputException;
public class Database {
public static HashMap> gGroups = new HashMap>();
public static HashMap> gEntries = new HashMap>();
public static PwGroup gRoot;
public static PwManager mPM;
+ public static String mFilename;
public static void LoadData(String filename, String password, String keyfile) throws InvalidCipherTextException, IOException, InvalidKeyFileException, FileNotFoundException {
FileInputStream fis;
@@ -52,6 +57,30 @@ public class Database {
mPM.constructTree(null);
populateGlobals(null);
}
+
+ mFilename = filename;
+ }
+
+ public static void SaveData() throws IOException, PwManagerOutputException {
+ SaveData(mFilename);
+ }
+
+ public static void SaveData(String filename) throws IOException, PwManagerOutputException {
+ File tempFile = new File(filename + ".tmp");
+ FileOutputStream fos = new FileOutputStream(tempFile);
+ PwManagerOutput pmo = new PwManagerOutput(mPM, fos);
+ pmo.output();
+ fos.close();
+
+ File orig = new File(filename);
+ orig.delete();
+
+ if ( ! tempFile.renameTo(orig) ) {
+ throw new IOException("Failed to store database.");
+ }
+
+ mFilename = filename;
+
}
private static void populateGlobals(PwGroup currentGroup) {
@@ -87,6 +116,7 @@ public class Database {
gEntries.clear();
gRoot = null;
mPM = null;
+ mFilename = null;
}
diff --git a/src/com/android/keepass/EntryActivity.java b/src/com/android/keepass/EntryActivity.java
index f08c363e0..88e1db841 100644
--- a/src/com/android/keepass/EntryActivity.java
+++ b/src/com/android/keepass/EntryActivity.java
@@ -20,6 +20,7 @@
package com.android.keepass;
import java.text.DateFormat;
+import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
@@ -36,7 +37,6 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
-import android.widget.Toast;
public class EntryActivity extends LockingActivity {
public static final String KEY_ENTRY = "entry";
@@ -72,6 +72,10 @@ public class EntryActivity extends LockingActivity {
assert(uuid != null);
mEntry = Database.gEntries.get(uuid).get();
+
+ // Update last access time.
+ Calendar cal = Calendar.getInstance();
+ mEntry.tLastAccess = cal.getTime();
fillData();
}
@@ -83,8 +87,9 @@ public class EntryActivity extends LockingActivity {
populateText(R.id.entry_password, getString(R.string.MaskedPassword));
DateFormat df = DateFormat.getInstance();
- String date = df.format(mEntry.tCreation);
- populateText(R.id.entry_created, date);
+ populateText(R.id.entry_created, df.format(mEntry.tCreation));
+ populateText(R.id.entry_modified, df.format(mEntry.tLastMod));
+ populateText(R.id.entry_accessed, df.format(mEntry.tLastAccess));
populateText(R.id.entry_comment, mEntry.additional);
TextView comment = (TextView)findViewById(R.id.entry_comment);
comment.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
diff --git a/src/com/android/keepass/PasswordActivity.java b/src/com/android/keepass/PasswordActivity.java
index a87a200bd..e6b73a3a0 100644
--- a/src/com/android/keepass/PasswordActivity.java
+++ b/src/com/android/keepass/PasswordActivity.java
@@ -237,22 +237,6 @@ public class PasswordActivity extends Activity {
public void run() {
mPd.dismiss();
- // TODO: Remove the below block
- ByteArrayOutputStream bActual = new ByteArrayOutputStream();
- PwManagerOutput pActual = new PwManagerOutput(Database.mPM, bActual, PwManagerOutput.DEBUG);
- try {
- pActual.output();
- } catch (PwManagerOutputException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
-
- // TODO: End block
-
if ( mMsg.length() > 0 ) {
Toast.makeText(PasswordActivity.this, mMsg, Toast.LENGTH_LONG).show();
}
diff --git a/src/com/android/keepass/keepasslib/PwManagerOutput.java b/src/com/android/keepass/keepasslib/PwManagerOutput.java
index 2ccdbed1b..9148018f4 100644
--- a/src/com/android/keepass/keepasslib/PwManagerOutput.java
+++ b/src/com/android/keepass/keepasslib/PwManagerOutput.java
@@ -79,47 +79,6 @@ public class PwManagerOutput {
byte[] finalKey = getFinalKey(header);
- /*
- // Bouncy Castle implementation
- PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding());
- cipher.init(true, new ParametersWithIV(new KeyParameter(finalKey), header.encryptionIV));
-
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- outputPlanGroupAndEntries(bos);
- bos.close();
-
- byte[] output = bos.toByteArray();
- byte[] encrypted = new byte[cipher.getOutputSize(output.length)];
- int bytes = cipher.processBytes(output, 0, output.length, encrypted, 0);
- try {
- bytes += cipher.doFinal(encrypted, bytes);
- } catch (DataLengthException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalStateException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InvalidCipherTextException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- mOS.write(encrypted, 0, bytes);
- */
-
- /*
- BufferedBlockCipherOutputStream bbcos = new BufferedBlockCipherOutputStream(mOS, cipher);
- outputPlanGroupAndEntries(bbcos);
- bbcos.close();
- */
- /*
- try {
- bbcos.close();
- } catch (IOException e) {
- throw new PwManagerOutputException("Failed to close encryption stream.");
- }
- */
-
Cipher cipher;
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
@@ -139,7 +98,6 @@ public class PwManagerOutput {
} catch (IOException e) {
throw new PwManagerOutputException("Failed to output final encrypted part.");
}
- //
}
public PwDbHeader outputHeader(OutputStream os) throws PwManagerOutputException {
@@ -185,7 +143,6 @@ public class PwManagerOutput {
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
- assert true;
throw new PwManagerOutputException("SHA-256 not implemented here.");
}
diff --git a/src/org/phoneid/keepassj2me/ImporterV3.java b/src/org/phoneid/keepassj2me/ImporterV3.java
index 7b486b691..b370d17dd 100644
--- a/src/org/phoneid/keepassj2me/ImporterV3.java
+++ b/src/org/phoneid/keepassj2me/ImporterV3.java
@@ -52,7 +52,6 @@ import android.util.Log;
import com.android.keepass.keepasslib.InvalidKeyFileException;
import com.android.keepass.keepasslib.NullOutputStream;
-import com.android.keepass.keepasslib.PwManagerOutput.PwManagerOutputException;
/**
* Load a v3 database file.
@@ -336,12 +335,13 @@ public class ImporterV3 {
/**
* Encrypt the master key a few times to make brute-force key-search harder
+ * @throws IOException
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
* @throws ShortBufferException
*/
- public static byte[] transformMasterKey( byte[] pKeySeed, byte[] pKey, int rounds )
+ public static byte[] transformMasterKey( byte[] pKeySeed, byte[] pKey, int rounds ) throws IOException
/*throws InvalidKeyException,
IllegalBlockSizeException,
BadPaddingException,
@@ -350,6 +350,7 @@ public class ImporterV3 {
//KeePassMIDlet.logS("transformMasterKey, rounds=" + rounds);
//KeePassMIDlet.logS("transformMasterKey, pkey=" + new String(Hex.encode(pKey)));
+ // Bouncy castle implementation
byte[] newKey = new byte[pKey.length];
int i;
@@ -361,6 +362,7 @@ public class ImporterV3 {
for( i = 0; i < rounds; i++ )
cipher.processBytes (newKey, 0, newKey.length, newKey, 0);
+ /*
// Hash once with SHA-256
SHA256Digest md = new SHA256Digest();
md.update(newKey, 0, newKey.length );
@@ -368,6 +370,50 @@ public class ImporterV3 {
md.doFinal(newKey, 0);
return newKey;
+ */
+
+ /* Native implementation is not quite right yet
+ byte[] newKey = new byte[pKey.length];
+ Cipher cipher;
+ try {
+ cipher = Cipher.getInstance("AES");
+ } catch (NoSuchAlgorithmException e) {
+ throw new IOException("NoSuchAlgorithm: " + e.getMessage());
+ } catch (NoSuchPaddingException e) {
+ throw new IOException("NoSuchPadding: " + e.getMessage());
+ }
+
+ try {
+ cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(pKeySeed, "AES"));
+ } catch (InvalidKeyException e) {
+ throw new IOException("InvalidKeyException: " + e.getMessage());
+ }
+
+ // Encrypt key rounds times
+ System.arraycopy(pKey, 0, newKey, 0, pKey.length);
+ for (int i = 0; i < rounds; i++) {
+ try {
+ cipher.update(newKey, 0, newKey.length, newKey, 0);
+
+ } catch (ShortBufferException e) {
+ throw new IOException("Short buffer: " + e.getMessage());
+ }
+ }
+ */
+
+ // Hash the key
+ MessageDigest md = null;
+ try {
+ md = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ assert true;
+ throw new IOException("SHA-256 not implemented here: " + e.getMessage());
+ }
+
+ md.update(newKey);
+ return md.digest();
+
+
}
diff --git a/tests/src/com/android/keepass/tests/DatabaseTest.java b/tests/src/com/android/keepass/tests/DatabaseTest.java
new file mode 100644
index 000000000..0e4395bf5
--- /dev/null
+++ b/tests/src/com/android/keepass/tests/DatabaseTest.java
@@ -0,0 +1,16 @@
+package com.android.keepass.tests;
+
+import junit.framework.TestCase;
+
+import com.android.keepass.Database;
+
+public class DatabaseTest extends TestCase {
+ public void testDatabase() {
+ try {
+ Database.LoadData("/sdcard/test1.kdb", "12345", "");
+ Database.SaveData("/sdcard/test2.kdb");
+ } catch (Exception e) {
+ assertTrue(e.getMessage(), true);
+ }
+ }
+}
diff --git a/tests/src/com/android/keepass/tests/PwManagerOutputTest.java b/tests/src/com/android/keepass/tests/PwManagerOutputTest.java
index ac654cad2..e7bd2cf39 100644
--- a/tests/src/com/android/keepass/tests/PwManagerOutputTest.java
+++ b/tests/src/com/android/keepass/tests/PwManagerOutputTest.java
@@ -142,5 +142,5 @@ public class PwManagerOutputTest extends TestCase {
assertArrayEquals("Databases do not match.", bExpected.toByteArray(), bActual.toByteArray());
}
-
+
}
\ No newline at end of file
diff --git a/tests/src/com/android/keepass/tests/TestData.java b/tests/src/com/android/keepass/tests/TestData.java
index 0ddee73c1..6a6e531d6 100644
--- a/tests/src/com/android/keepass/tests/TestData.java
+++ b/tests/src/com/android/keepass/tests/TestData.java
@@ -20,7 +20,6 @@
package com.android.keepass.tests;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import org.bouncycastle.crypto.InvalidCipherTextException;