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;