mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Get enough in to fully parse a .kdbx database and load it into java objects.
This commit is contained in:
15
LICENSE
15
LICENSE
@@ -119,3 +119,18 @@ Files under jni/openssl-0.98l/*:
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Files under biz.source_code.base64Coder
|
||||||
|
// Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
|
||||||
|
// www.source-code.biz, www.inventec.ch/chdh
|
||||||
|
//
|
||||||
|
// This module is multi-licensed and may be used under the terms
|
||||||
|
// of any of the following licenses:
|
||||||
|
//
|
||||||
|
// EPL, Eclipse Public License, http://www.eclipse.org/legal
|
||||||
|
// LGPL, GNU Lesser General Public License, http://www.gnu.org/licenses/lgpl.html
|
||||||
|
// AL, Apache License, http://www.apache.org/licenses
|
||||||
|
// BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php
|
||||||
|
//
|
||||||
|
// Please contact the author if you need another license.
|
||||||
|
// This module is provided "as is", without warranties of any kind.
|
||||||
|
|
||||||
|
|||||||
225
src/biz/source_code/base64Coder/Base64Coder.java
Normal file
225
src/biz/source_code/base64Coder/Base64Coder.java
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
// Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
|
||||||
|
// www.source-code.biz, www.inventec.ch/chdh
|
||||||
|
//
|
||||||
|
// This module is multi-licensed and may be used under the terms
|
||||||
|
// of any of the following licenses:
|
||||||
|
//
|
||||||
|
// EPL, Eclipse Public License, http://www.eclipse.org/legal
|
||||||
|
// LGPL, GNU Lesser General Public License, http://www.gnu.org/licenses/lgpl.html
|
||||||
|
// AL, Apache License, http://www.apache.org/licenses
|
||||||
|
// BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php
|
||||||
|
//
|
||||||
|
// Please contact the author if you need another license.
|
||||||
|
// This module is provided "as is", without warranties of any kind.
|
||||||
|
|
||||||
|
package biz.source_code.base64Coder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Base64 encoder/decoder.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This class is used to encode and decode data in Base64 format as described in RFC 1521.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Project home page: <a href="http://www.source-code.biz/base64coder/java/">www.source-code.biz/base64coder/java</a><br>
|
||||||
|
* Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland<br>
|
||||||
|
* Multi-licensed: EPL / LGPL / AL / BSD.
|
||||||
|
*/
|
||||||
|
public class Base64Coder {
|
||||||
|
|
||||||
|
// The line separator string of the operating system.
|
||||||
|
private static final String systemLineSeparator = System.getProperty("line.separator");
|
||||||
|
|
||||||
|
// Mapping table from 6-bit nibbles to Base64 characters.
|
||||||
|
private static char[] map1 = new char[64];
|
||||||
|
static {
|
||||||
|
int i=0;
|
||||||
|
for (char c='A'; c<='Z'; c++) map1[i++] = c;
|
||||||
|
for (char c='a'; c<='z'; c++) map1[i++] = c;
|
||||||
|
for (char c='0'; c<='9'; c++) map1[i++] = c;
|
||||||
|
map1[i++] = '+'; map1[i++] = '/'; }
|
||||||
|
|
||||||
|
// Mapping table from Base64 characters to 6-bit nibbles.
|
||||||
|
private static byte[] map2 = new byte[128];
|
||||||
|
static {
|
||||||
|
for (int i=0; i<map2.length; i++) map2[i] = -1;
|
||||||
|
for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a string into Base64 format.
|
||||||
|
* No blanks or line breaks are inserted.
|
||||||
|
* @param s A String to be encoded.
|
||||||
|
* @return A String containing the Base64 encoded data.
|
||||||
|
*/
|
||||||
|
public static String encodeString (String s) {
|
||||||
|
return new String(encode(s.getBytes())); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a byte array into Base 64 format and breaks the output into lines of 76 characters.
|
||||||
|
* This method is compatible with <code>sun.misc.BASE64Encoder.encodeBuffer(byte[])</code>.
|
||||||
|
* @param in An array containing the data bytes to be encoded.
|
||||||
|
* @return A String containing the Base64 encoded data, broken into lines.
|
||||||
|
*/
|
||||||
|
public static String encodeLines (byte[] in) {
|
||||||
|
return encodeLines(in, 0, in.length, 76, systemLineSeparator); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a byte array into Base 64 format and breaks the output into lines.
|
||||||
|
* @param in An array containing the data bytes to be encoded.
|
||||||
|
* @param iOff Offset of the first byte in <code>in</code> to be processed.
|
||||||
|
* @param iLen Number of bytes to be processed in <code>in</code>, starting at <code>iOff</code>.
|
||||||
|
* @param lineLen Line length for the output data. Should be a multiple of 4.
|
||||||
|
* @param lineSeparator The line separator to be used to separate the output lines.
|
||||||
|
* @return A String containing the Base64 encoded data, broken into lines.
|
||||||
|
*/
|
||||||
|
public static String encodeLines (byte[] in, int iOff, int iLen, int lineLen, String lineSeparator) {
|
||||||
|
int blockLen = (lineLen*3) / 4;
|
||||||
|
if (blockLen <= 0) throw new IllegalArgumentException();
|
||||||
|
int lines = (iLen+blockLen-1) / blockLen;
|
||||||
|
int bufLen = ((iLen+2)/3)*4 + lines*lineSeparator.length();
|
||||||
|
StringBuilder buf = new StringBuilder(bufLen);
|
||||||
|
int ip = 0;
|
||||||
|
while (ip < iLen) {
|
||||||
|
int l = Math.min(iLen-ip, blockLen);
|
||||||
|
buf.append (encode(in, iOff+ip, l));
|
||||||
|
buf.append (lineSeparator);
|
||||||
|
ip += l; }
|
||||||
|
return buf.toString(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a byte array into Base64 format.
|
||||||
|
* No blanks or line breaks are inserted in the output.
|
||||||
|
* @param in An array containing the data bytes to be encoded.
|
||||||
|
* @return A character array containing the Base64 encoded data.
|
||||||
|
*/
|
||||||
|
public static char[] encode (byte[] in) {
|
||||||
|
return encode(in, 0, in.length); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a byte array into Base64 format.
|
||||||
|
* No blanks or line breaks are inserted in the output.
|
||||||
|
* @param in An array containing the data bytes to be encoded.
|
||||||
|
* @param iLen Number of bytes to process in <code>in</code>.
|
||||||
|
* @return A character array containing the Base64 encoded data.
|
||||||
|
*/
|
||||||
|
public static char[] encode (byte[] in, int iLen) {
|
||||||
|
return encode(in, 0, iLen); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a byte array into Base64 format.
|
||||||
|
* No blanks or line breaks are inserted in the output.
|
||||||
|
* @param in An array containing the data bytes to be encoded.
|
||||||
|
* @param iOff Offset of the first byte in <code>in</code> to be processed.
|
||||||
|
* @param iLen Number of bytes to process in <code>in</code>, starting at <code>iOff</code>.
|
||||||
|
* @return A character array containing the Base64 encoded data.
|
||||||
|
*/
|
||||||
|
public static char[] encode (byte[] in, int iOff, int iLen) {
|
||||||
|
int oDataLen = (iLen*4+2)/3; // output length without padding
|
||||||
|
int oLen = ((iLen+2)/3)*4; // output length including padding
|
||||||
|
char[] out = new char[oLen];
|
||||||
|
int ip = iOff;
|
||||||
|
int iEnd = iOff + iLen;
|
||||||
|
int op = 0;
|
||||||
|
while (ip < iEnd) {
|
||||||
|
int i0 = in[ip++] & 0xff;
|
||||||
|
int i1 = ip < iEnd ? in[ip++] & 0xff : 0;
|
||||||
|
int i2 = ip < iEnd ? in[ip++] & 0xff : 0;
|
||||||
|
int o0 = i0 >>> 2;
|
||||||
|
int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
|
||||||
|
int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
|
||||||
|
int o3 = i2 & 0x3F;
|
||||||
|
out[op++] = map1[o0];
|
||||||
|
out[op++] = map1[o1];
|
||||||
|
out[op] = op < oDataLen ? map1[o2] : '='; op++;
|
||||||
|
out[op] = op < oDataLen ? map1[o3] : '='; op++; }
|
||||||
|
return out; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a string from Base64 format.
|
||||||
|
* No blanks or line breaks are allowed within the Base64 encoded input data.
|
||||||
|
* @param s A Base64 String to be decoded.
|
||||||
|
* @return A String containing the decoded data.
|
||||||
|
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||||
|
*/
|
||||||
|
public static String decodeString (String s) {
|
||||||
|
return new String(decode(s)); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a byte array from Base64 format and ignores line separators, tabs and blanks.
|
||||||
|
* CR, LF, Tab and Space characters are ignored in the input data.
|
||||||
|
* This method is compatible with <code>sun.misc.BASE64Decoder.decodeBuffer(String)</code>.
|
||||||
|
* @param s A Base64 String to be decoded.
|
||||||
|
* @return An array containing the decoded data bytes.
|
||||||
|
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||||
|
*/
|
||||||
|
public static byte[] decodeLines (String s) {
|
||||||
|
char[] buf = new char[s.length()];
|
||||||
|
int p = 0;
|
||||||
|
for (int ip = 0; ip < s.length(); ip++) {
|
||||||
|
char c = s.charAt(ip);
|
||||||
|
if (c != ' ' && c != '\r' && c != '\n' && c != '\t')
|
||||||
|
buf[p++] = c; }
|
||||||
|
return decode(buf, 0, p); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a byte array from Base64 format.
|
||||||
|
* No blanks or line breaks are allowed within the Base64 encoded input data.
|
||||||
|
* @param s A Base64 String to be decoded.
|
||||||
|
* @return An array containing the decoded data bytes.
|
||||||
|
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||||
|
*/
|
||||||
|
public static byte[] decode (String s) {
|
||||||
|
return decode(s.toCharArray()); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a byte array from Base64 format.
|
||||||
|
* No blanks or line breaks are allowed within the Base64 encoded input data.
|
||||||
|
* @param in A character array containing the Base64 encoded data.
|
||||||
|
* @return An array containing the decoded data bytes.
|
||||||
|
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||||
|
*/
|
||||||
|
public static byte[] decode (char[] in) {
|
||||||
|
return decode(in, 0, in.length); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a byte array from Base64 format.
|
||||||
|
* No blanks or line breaks are allowed within the Base64 encoded input data.
|
||||||
|
* @param in A character array containing the Base64 encoded data.
|
||||||
|
* @param iOff Offset of the first character in <code>in</code> to be processed.
|
||||||
|
* @param iLen Number of characters to process in <code>in</code>, starting at <code>iOff</code>.
|
||||||
|
* @return An array containing the decoded data bytes.
|
||||||
|
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||||
|
*/
|
||||||
|
public static byte[] decode (char[] in, int iOff, int iLen) {
|
||||||
|
if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
|
||||||
|
while (iLen > 0 && in[iOff+iLen-1] == '=') iLen--;
|
||||||
|
int oLen = (iLen*3) / 4;
|
||||||
|
byte[] out = new byte[oLen];
|
||||||
|
int ip = iOff;
|
||||||
|
int iEnd = iOff + iLen;
|
||||||
|
int op = 0;
|
||||||
|
while (ip < iEnd) {
|
||||||
|
int i0 = in[ip++];
|
||||||
|
int i1 = in[ip++];
|
||||||
|
int i2 = ip < iEnd ? in[ip++] : 'A';
|
||||||
|
int i3 = ip < iEnd ? in[ip++] : 'A';
|
||||||
|
if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
|
||||||
|
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
|
||||||
|
int b0 = map2[i0];
|
||||||
|
int b1 = map2[i1];
|
||||||
|
int b2 = map2[i2];
|
||||||
|
int b3 = map2[i3];
|
||||||
|
if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
|
||||||
|
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
|
||||||
|
int o0 = ( b0 <<2) | (b1>>>4);
|
||||||
|
int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
|
||||||
|
int o2 = ((b2 & 3)<<6) | b3;
|
||||||
|
out[op++] = (byte)o0;
|
||||||
|
if (op<oLen) out[op++] = (byte)o1;
|
||||||
|
if (op<oLen) out[op++] = (byte)o2; }
|
||||||
|
return out; }
|
||||||
|
|
||||||
|
// Dummy constructor.
|
||||||
|
private Base64Coder() {}
|
||||||
|
|
||||||
|
} // end class Base64Coder
|
||||||
@@ -50,7 +50,6 @@ import com.keepassdroid.database.load.ImporterFactory;
|
|||||||
import com.keepassdroid.database.load.ImporterV3;
|
import com.keepassdroid.database.load.ImporterV3;
|
||||||
import com.keepassdroid.database.save.PwDbOutput;
|
import com.keepassdroid.database.save.PwDbOutput;
|
||||||
import com.keepassdroid.search.SearchDbHelper;
|
import com.keepassdroid.search.SearchDbHelper;
|
||||||
import com.keepassdroid.utils.Types;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author bpellin
|
* @author bpellin
|
||||||
@@ -206,7 +205,7 @@ public class Database {
|
|||||||
|
|
||||||
for (int i = 0; i < childEntries.size(); i++ ) {
|
for (int i = 0; i < childEntries.size(); i++ ) {
|
||||||
PwEntry cur = childEntries.elementAt(i);
|
PwEntry cur = childEntries.elementAt(i);
|
||||||
entries.put(Types.bytestoUUID(cur.uuid), new WeakReference<PwEntry>(cur));
|
entries.put(cur.getUUID(), new WeakReference<PwEntry>(cur));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < childGroups.size(); i++ ) {
|
for (int i = 0; i < childGroups.size(); i++ ) {
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public class EntryActivity extends LockCloseActivity {
|
|||||||
public static void Launch(Activity act, PwEntry pw, int pos) {
|
public static void Launch(Activity act, PwEntry pw, int pos) {
|
||||||
Intent i = new Intent(act, EntryActivity.class);
|
Intent i = new Intent(act, EntryActivity.class);
|
||||||
|
|
||||||
i.putExtra(KEY_ENTRY, pw.uuid);
|
i.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pw.getUUID()));
|
||||||
i.putExtra(KEY_REFRESH_POS, pos);
|
i.putExtra(KEY_REFRESH_POS, pos);
|
||||||
|
|
||||||
act.startActivityForResult(i,0);
|
act.startActivityForResult(i,0);
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public class EntryEditActivity extends LockCloseActivity {
|
|||||||
|
|
||||||
Intent i = new Intent(act, EntryEditActivity.class);
|
Intent i = new Intent(act, EntryEditActivity.class);
|
||||||
|
|
||||||
i.putExtra(KEY_ENTRY, pw.uuid);
|
i.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pw.getUUID()));
|
||||||
|
|
||||||
act.startActivityForResult(i, 0);
|
act.startActivityForResult(i, 0);
|
||||||
}
|
}
|
||||||
@@ -155,7 +155,7 @@ public class EntryEditActivity extends LockCloseActivity {
|
|||||||
newEntry.parent = mEntry.parent;
|
newEntry.parent = mEntry.parent;
|
||||||
newEntry.tCreation = mEntry.tCreation;
|
newEntry.tCreation = mEntry.tCreation;
|
||||||
newEntry.tExpire = mEntry.tExpire;
|
newEntry.tExpire = mEntry.tExpire;
|
||||||
newEntry.uuid = mEntry.uuid;
|
newEntry.setUUID(mEntry.getUUID());
|
||||||
|
|
||||||
Date now = Calendar.getInstance().getTime();
|
Date now = Calendar.getInstance().getTime();
|
||||||
newEntry.tLastAccess = new PwDate(now);
|
newEntry.tLastAccess = new PwDate(now);
|
||||||
|
|||||||
65
src/com/keepassdroid/crypto/PwStreamCipherFactory.java
Normal file
65
src/com/keepassdroid/crypto/PwStreamCipherFactory.java
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009 Brian Pellin.
|
||||||
|
*
|
||||||
|
* This file is part of KeePassDroid.
|
||||||
|
*
|
||||||
|
* KeePassDroid is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* KeePassDroid is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.keepassdroid.crypto;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
import org.bouncycastle.crypto.StreamCipher;
|
||||||
|
import org.bouncycastle.crypto.engines.Salsa20Engine;
|
||||||
|
import org.bouncycastle.crypto.params.KeyParameter;
|
||||||
|
import org.bouncycastle.crypto.params.ParametersWithIV;
|
||||||
|
|
||||||
|
import com.keepassdroid.database.CrsAlgorithm;
|
||||||
|
|
||||||
|
public class PwStreamCipherFactory {
|
||||||
|
public static StreamCipher getInstance(CrsAlgorithm alg, byte[] key) {
|
||||||
|
if ( alg == CrsAlgorithm.Salsa20 ) {
|
||||||
|
return getSalsa20(key);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final byte[] SALSA_IV = new byte[]{ (byte)0xE8, 0x30, 0x09, 0x4B,
|
||||||
|
(byte)0x97, 0x20, 0x5D, 0x2A };
|
||||||
|
|
||||||
|
private static StreamCipher getSalsa20(byte[] key) {
|
||||||
|
// Build stream cipher key
|
||||||
|
MessageDigest md;
|
||||||
|
try {
|
||||||
|
md = MessageDigest.getInstance("SHA-256");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new RuntimeException("SHA 256 not supported");
|
||||||
|
}
|
||||||
|
byte[] key32 = md.digest(key);
|
||||||
|
|
||||||
|
KeyParameter keyParam = new KeyParameter(key32);
|
||||||
|
ParametersWithIV ivParam = new ParametersWithIV(keyParam, SALSA_IV);
|
||||||
|
|
||||||
|
StreamCipher cipher = new Salsa20Engine();
|
||||||
|
cipher.init(true, ivParam);
|
||||||
|
|
||||||
|
return cipher;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,10 +19,27 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
public class CrsAlgorithm {
|
public enum CrsAlgorithm {
|
||||||
public static final int Null = 0;
|
|
||||||
public static final int ArcFourVariant = 1;
|
|
||||||
public static final int Salsa20 = 2;
|
|
||||||
|
|
||||||
public static final int Count = 3;
|
Null(0),
|
||||||
|
ArcFourVariant(1),
|
||||||
|
Salsa20(2);
|
||||||
|
|
||||||
|
public static final int count = 3;
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
private CrsAlgorithm(int num) {
|
||||||
|
id = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CrsAlgorithm fromId(int num) {
|
||||||
|
for ( CrsAlgorithm e : CrsAlgorithm.values() ) {
|
||||||
|
if ( e.id == num ) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
46
src/com/keepassdroid/database/ITimeLogger.java
Normal file
46
src/com/keepassdroid/database/ITimeLogger.java
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Brian Pellin.
|
||||||
|
*
|
||||||
|
* This file is part of KeePassDroid.
|
||||||
|
*
|
||||||
|
* KeePassDroid is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* KeePassDroid is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public interface ITimeLogger {
|
||||||
|
Date getLastModificationTime();
|
||||||
|
void setLastModificationTime(Date date);
|
||||||
|
|
||||||
|
Date getCreationTime();
|
||||||
|
void setCreationTime(Date date);
|
||||||
|
|
||||||
|
Date getLastAccessTime();
|
||||||
|
void setLastAccessTime(Date date);
|
||||||
|
|
||||||
|
Date getExpiryTime();
|
||||||
|
void setExpiryTime(Date date);
|
||||||
|
|
||||||
|
boolean expires();
|
||||||
|
void setExpires(boolean exp);
|
||||||
|
|
||||||
|
long getUsageCount();
|
||||||
|
void setUsageCount(long count);
|
||||||
|
|
||||||
|
Date getLocationChanged();
|
||||||
|
void setLocationChanged(Date date);
|
||||||
|
|
||||||
|
}
|
||||||
32
src/com/keepassdroid/database/PwCustomIcon.java
Normal file
32
src/com/keepassdroid/database/PwCustomIcon.java
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Brian Pellin.
|
||||||
|
*
|
||||||
|
* This file is part of KeePassDroid.
|
||||||
|
*
|
||||||
|
* KeePassDroid is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* KeePassDroid is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class PwCustomIcon {
|
||||||
|
private UUID uuid;
|
||||||
|
private byte[] imageData;
|
||||||
|
|
||||||
|
public PwCustomIcon(UUID u, byte[] data) {
|
||||||
|
uuid = u;
|
||||||
|
imageData = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,7 @@ public abstract class PwDatabase {
|
|||||||
|
|
||||||
public byte masterKey[] = new byte[32];
|
public byte masterKey[] = new byte[32];
|
||||||
public byte[] finalKey;
|
public byte[] finalKey;
|
||||||
|
public String name = "KeePass database";
|
||||||
|
|
||||||
public void makeFinalKey(byte[] masterSeed, byte[] masterSeed2, int numRounds) throws IOException {
|
public void makeFinalKey(byte[] masterSeed, byte[] masterSeed2, int numRounds) throws IOException {
|
||||||
|
|
||||||
|
|||||||
@@ -48,9 +48,6 @@ public class PwDatabaseV3 extends PwDatabase {
|
|||||||
// Constants
|
// Constants
|
||||||
// private static final int PWM_SESSION_KEY_SIZE = 12;
|
// private static final int PWM_SESSION_KEY_SIZE = 12;
|
||||||
|
|
||||||
// Descriptive name for database, used in GUI.
|
|
||||||
public String name = "KeePass database";
|
|
||||||
|
|
||||||
// Special entry for settings
|
// Special entry for settings
|
||||||
public PwEntry metaInfo;
|
public PwEntry metaInfo;
|
||||||
|
|
||||||
@@ -153,7 +150,7 @@ public class PwDatabaseV3 extends PwDatabase {
|
|||||||
groups.addElement(group);
|
groups.addElement(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addEntry(PwEntryV3 entry)
|
public void addEntry(PwEntry entry)
|
||||||
{
|
{
|
||||||
entries.addElement(entry);
|
entries.addElement(entry);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,17 @@
|
|||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
import org.w3c.dom.Node;
|
|
||||||
import org.w3c.dom.NodeList;
|
|
||||||
|
|
||||||
import com.keepassdroid.database.exception.InconsistentDBException;
|
import com.keepassdroid.database.exception.InconsistentDBException;
|
||||||
import com.keepassdroid.database.exception.InvalidKeyFileException;
|
import com.keepassdroid.database.exception.InvalidKeyFileException;
|
||||||
|
|
||||||
@@ -38,10 +40,48 @@ public class PwDatabaseV4 extends PwDatabase {
|
|||||||
public UUID dataCipher;
|
public UUID dataCipher;
|
||||||
public PwCompressionAlgorithm compressionAlgorithm;
|
public PwCompressionAlgorithm compressionAlgorithm;
|
||||||
public long numKeyEncRounds;
|
public long numKeyEncRounds;
|
||||||
private Document doc;
|
public Date nameChanged;
|
||||||
|
public String description;
|
||||||
|
public Date descriptionChanged;
|
||||||
|
public String defaultUserName;
|
||||||
|
public Date defaultUserNameChanged;
|
||||||
|
public long maintenanceHistoryDays;
|
||||||
|
public boolean recycleBinEnabled;
|
||||||
|
public UUID recycleBinUUID;
|
||||||
|
public Date recycleBinChanged;
|
||||||
|
public UUID entryTemplatesGroup;
|
||||||
|
public Date entryTemplatesGroupChanged;
|
||||||
|
public UUID lastSelectedGroup;
|
||||||
|
public UUID lastTopVisibleGroup;
|
||||||
|
public MemoryProtectionConfig memoryProtection = new MemoryProtectionConfig();
|
||||||
|
public List<PwDeletedObject> deletedObjects = new ArrayList<PwDeletedObject>();
|
||||||
|
public List<PwCustomIcon> customIcons;
|
||||||
|
public Map<String, String> customData = new HashMap<String, String>();
|
||||||
|
|
||||||
|
public class MemoryProtectionConfig {
|
||||||
|
public boolean protectTitle = false;
|
||||||
|
public boolean protectUserName = false;
|
||||||
|
public boolean protectPassword = false;
|
||||||
|
public boolean protectUrl = false;
|
||||||
|
public boolean protectNotes = false;
|
||||||
|
|
||||||
|
public boolean autoEnableVisualHiding = false;
|
||||||
|
|
||||||
|
public boolean GetProtection(String field) {
|
||||||
|
if ( field.equalsIgnoreCase(PwDefsV4.TITLE_FIELD)) return protectTitle;
|
||||||
|
if ( field.equalsIgnoreCase(PwDefsV4.USERNAME_FIELD)) return protectUserName;
|
||||||
|
if ( field.equalsIgnoreCase(PwDefsV4.PASSWORD_FIELD)) return protectPassword;
|
||||||
|
if ( field.equalsIgnoreCase(PwDefsV4.URL_FIELD)) return protectUrl;
|
||||||
|
if ( field.equalsIgnoreCase(PwDefsV4.NOTES_FIELD)) return protectNotes;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final UUID UUID_ZERO = new UUID(0,0);
|
||||||
|
|
||||||
//private Vector<PwGroupV4> groups = new Vector<PwGroupV4>();
|
//private Vector<PwGroupV4> groups = new Vector<PwGroupV4>();
|
||||||
private PwGroupV4 rootGroup;
|
public PwGroupV4 rootGroup;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getMasterKey(String key, String keyFileName)
|
public byte[] getMasterKey(String key, String keyFileName)
|
||||||
@@ -83,19 +123,8 @@ public class PwDatabaseV4 extends PwDatabase {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void parseDB(Document d) throws InconsistentDBException {
|
public void parseDB(InputStream in) throws InconsistentDBException {
|
||||||
doc = d;
|
//TODO Implement Me
|
||||||
|
|
||||||
NodeList list = doc.getElementsByTagName("Root");
|
|
||||||
|
|
||||||
int len = list.getLength();
|
|
||||||
if ( len < 0 || len > 1 ) {
|
|
||||||
throw new InconsistentDBException("Missing root node");
|
|
||||||
}
|
|
||||||
|
|
||||||
Node root = list.item(0);
|
|
||||||
|
|
||||||
rootGroup = new PwGroupV4(root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public class PwDbHeaderV4 extends PwDbHeader {
|
|||||||
private PwDatabaseV4 db;
|
private PwDatabaseV4 db;
|
||||||
public byte[] protectedStreamKey;
|
public byte[] protectedStreamKey;
|
||||||
public byte[] streamStartBytes;
|
public byte[] streamStartBytes;
|
||||||
public int innerRandomStream;
|
public CrsAlgorithm innerRandomStream;
|
||||||
|
|
||||||
public PwDbHeaderV4(PwDatabaseV4 d) {
|
public PwDbHeaderV4(PwDatabaseV4 d) {
|
||||||
db = d;
|
db = d;
|
||||||
@@ -190,11 +190,11 @@ public class PwDbHeaderV4 extends PwDbHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int id = LEDataInputStream.readInt(streamID, 0);
|
int id = LEDataInputStream.readInt(streamID, 0);
|
||||||
if ( id < 0 || id >= CrsAlgorithm.Count ) {
|
if ( id < 0 || id >= CrsAlgorithm.count ) {
|
||||||
throw new IOException("Invalid stream id.");
|
throw new IOException("Invalid stream id.");
|
||||||
}
|
}
|
||||||
|
|
||||||
innerRandomStream = id;
|
innerRandomStream = CrsAlgorithm.fromId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Determines if this is a supported version.
|
/** Determines if this is a supported version.
|
||||||
|
|||||||
34
src/com/keepassdroid/database/PwDefsV4.java
Normal file
34
src/com/keepassdroid/database/PwDefsV4.java
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Brian Pellin.
|
||||||
|
*
|
||||||
|
* This file is part of KeePassDroid.
|
||||||
|
*
|
||||||
|
* KeePassDroid is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* KeePassDroid is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
|
public class PwDefsV4 {
|
||||||
|
|
||||||
|
public static final String TITLE_FIELD = "Title";
|
||||||
|
|
||||||
|
public static final String USERNAME_FIELD = "UserName";
|
||||||
|
|
||||||
|
public static final String PASSWORD_FIELD = "Password";
|
||||||
|
|
||||||
|
public static final String URL_FIELD = "URL";
|
||||||
|
|
||||||
|
public static final String NOTES_FIELD = "Notes";
|
||||||
|
|
||||||
|
}
|
||||||
40
src/com/keepassdroid/database/PwDeletedObject.java
Normal file
40
src/com/keepassdroid/database/PwDeletedObject.java
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Brian Pellin.
|
||||||
|
*
|
||||||
|
* This file is part of KeePassDroid.
|
||||||
|
*
|
||||||
|
* KeePassDroid is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* KeePassDroid is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class PwDeletedObject {
|
||||||
|
public UUID uuid;
|
||||||
|
private Date deletionTime;
|
||||||
|
|
||||||
|
public Date getDeletionTime() {
|
||||||
|
if ( deletionTime == null ) {
|
||||||
|
return new Date(System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
return deletionTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeletionTime(Date date) {
|
||||||
|
deletionTime = date;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,13 +20,15 @@
|
|||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public abstract class PwEntry implements Cloneable {
|
public abstract class PwEntry implements Cloneable {
|
||||||
|
|
||||||
public byte uuid[] = new byte[16];
|
//public byte uuid[] = new byte[16];
|
||||||
public String title;
|
public String title;
|
||||||
public String url;
|
public String url;
|
||||||
public String additional;
|
public String additional;
|
||||||
|
public int imageId;
|
||||||
|
|
||||||
public PwEntry() {
|
public PwEntry() {
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ public abstract class PwEntry implements Cloneable {
|
|||||||
throw new RuntimeException("Clone should be supported");
|
throw new RuntimeException("Clone should be supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
System.arraycopy(uuid, 0, newEntry.uuid, 0, uuid.length);
|
newEntry.setUUID(getUUID());
|
||||||
newEntry.title = title;
|
newEntry.title = title;
|
||||||
newEntry.url = url;
|
newEntry.url = url;
|
||||||
newEntry.additional = additional;
|
newEntry.additional = additional;
|
||||||
@@ -51,14 +53,16 @@ public abstract class PwEntry implements Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void assign(PwEntry source) {
|
public void assign(PwEntry source) {
|
||||||
System.arraycopy(source.uuid, 0, uuid, 0, source.uuid.length);
|
setUUID(source.getUUID());
|
||||||
title = source.title;
|
title = source.title;
|
||||||
url = source.url;
|
url = source.url;
|
||||||
additional = source.additional;
|
additional = source.additional;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void stampLastAccess();
|
public abstract void stampLastAccess();
|
||||||
|
|
||||||
|
public abstract UUID getUUID();
|
||||||
|
public abstract void setUUID(UUID u);
|
||||||
public abstract String getUsername();
|
public abstract String getUsername();
|
||||||
public abstract String getPassword();
|
public abstract String getPassword();
|
||||||
public abstract Date getCreate();
|
public abstract Date getCreate();
|
||||||
|
|||||||
@@ -29,11 +29,13 @@ import java.lang.ref.WeakReference;
|
|||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.Database;
|
||||||
|
import com.keepassdroid.utils.Types;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,11 +73,9 @@ public class PwEntryV3 extends PwEntry {
|
|||||||
|
|
||||||
|
|
||||||
public int groupId;
|
public int groupId;
|
||||||
public int imageId;
|
public String username;
|
||||||
|
|
||||||
public String username;
|
|
||||||
|
|
||||||
private byte[] password;
|
private byte[] password;
|
||||||
|
private byte[] uuid;
|
||||||
|
|
||||||
public PwDate tCreation;
|
public PwDate tCreation;
|
||||||
public PwDate tLastMod;
|
public PwDate tLastMod;
|
||||||
@@ -315,11 +315,6 @@ public class PwEntryV3 extends PwEntry {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date getAccess() {
|
public Date getAccess() {
|
||||||
return tLastAccess.getJDate();
|
return tLastAccess.getJDate();
|
||||||
@@ -345,4 +340,19 @@ public class PwEntryV3 extends PwEntry {
|
|||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID getUUID() {
|
||||||
|
return Types.bytestoUUID(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUUID(UUID u) {
|
||||||
|
uuid = Types.UUIDtoBytes(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,15 +19,50 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.w3c.dom.Node;
|
public class PwEntryV4 extends PwEntry implements ITimeLogger {
|
||||||
|
private static final String STR_USERNAME = "UserName";
|
||||||
public class PwEntryV4 extends PwEntry {
|
|
||||||
private Node node;
|
|
||||||
|
|
||||||
public PwEntryV4(Node n) {
|
public PwGroupV4 parent;
|
||||||
node = n;
|
public UUID uuid;
|
||||||
|
public Map<String, String> strings = new HashMap<String, String>();
|
||||||
|
public Map<String, byte[]> binaries = new HashMap<String, byte[]>();
|
||||||
|
public UUID customIconUuid;
|
||||||
|
public String foregroundColor;
|
||||||
|
public String backgroupColor;
|
||||||
|
public String overrideURL;
|
||||||
|
public AutoType autoType = new AutoType();
|
||||||
|
public List<PwEntryV4> history = new ArrayList<PwEntryV4>();
|
||||||
|
|
||||||
|
private Date parentGroupLastMod;
|
||||||
|
private Date creation;
|
||||||
|
private Date lastMod;
|
||||||
|
private Date lastAccess;
|
||||||
|
private Date expireDate;
|
||||||
|
private boolean expires = false;
|
||||||
|
private long usageCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
public class AutoType {
|
||||||
|
public boolean enabled;
|
||||||
|
public long obfuscationOptions;
|
||||||
|
public String defaultSequence;
|
||||||
|
|
||||||
|
private Map<String, String> windowSeqPairs = new HashMap<String, String>();
|
||||||
|
|
||||||
|
public void put(String key, String value) {
|
||||||
|
windowSeqPairs.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PwEntryV4() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -43,7 +78,7 @@ public class PwEntryV4 extends PwEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void assign(PwEntryV4 source) {
|
private void assign(PwEntryV4 source) {
|
||||||
node = source.node;
|
// TODO: Implement me
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -60,50 +95,131 @@ public class PwEntryV4 extends PwEntry {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
// TODO Implement me
|
return getString(STR_USERNAME);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
// TODO Implement me
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date getAccess() {
|
public Date getAccess() {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date getCreate() {
|
public Date getCreate() {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date getExpire() {
|
public Date getExpire() {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date getMod() {
|
public Date getMod() {
|
||||||
// TODO Auto-generated method stub
|
return parentGroupLastMod;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDisplayTitle() {
|
public String getDisplayTitle() {
|
||||||
// TOOD: Add special TAN handling for V4?
|
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PwGroupV4 getParent() {
|
public PwGroupV4 getParent() {
|
||||||
// TODO Auto-generated method stub
|
return parent;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID getUUID() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUUID(UUID u) {
|
||||||
|
uuid = u;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getString(String key) {
|
||||||
|
String value = strings.get(key);
|
||||||
|
|
||||||
|
if ( value == null ) return new String("");
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getCreationTime() {
|
||||||
|
return creation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getExpiryTime() {
|
||||||
|
return expireDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getLastAccessTime() {
|
||||||
|
return lastAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getLastModificationTime() {
|
||||||
|
return lastMod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getLocationChanged() {
|
||||||
|
return parentGroupLastMod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getUsageCount() {
|
||||||
|
return usageCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCreationTime(Date date) {
|
||||||
|
creation = date;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setExpiryTime(Date date) {
|
||||||
|
expireDate = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLastAccessTime(Date date) {
|
||||||
|
lastAccess = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLastModificationTime(Date date) {
|
||||||
|
lastMod = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLocationChanged(Date date) {
|
||||||
|
parentGroupLastMod = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsageCount(long count) {
|
||||||
|
usageCount = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean expires() {
|
||||||
|
return expires;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setExpires(boolean exp) {
|
||||||
|
expires = exp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
public abstract class PwGroup {
|
public abstract class PwGroup {
|
||||||
@@ -30,5 +31,7 @@ public abstract class PwGroup {
|
|||||||
public abstract PwGroupId getId();
|
public abstract PwGroupId getId();
|
||||||
|
|
||||||
public abstract String getName();
|
public abstract String getName();
|
||||||
|
|
||||||
|
public abstract Date getLastMod();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,4 +118,9 @@ public class PwGroupV3 extends PwGroup {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getLastMod() {
|
||||||
|
return tLastMod.getJDate();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,45 +19,70 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import org.w3c.dom.Node;
|
public class PwGroupV4 extends PwGroup implements ITimeLogger {
|
||||||
import org.w3c.dom.NodeList;
|
|
||||||
|
|
||||||
import com.keepassdroid.database.exception.InconsistentDBException;
|
public PwGroupV4 parent = null;
|
||||||
|
public UUID uuid;
|
||||||
public class PwGroupV4 extends PwGroup {
|
public String name;
|
||||||
|
public String notes;
|
||||||
private Node node;
|
public int iconId;
|
||||||
private PwGroup parent;
|
public UUID customIconUuid;
|
||||||
|
public boolean isExpanded;
|
||||||
public PwGroupV4(Node n) throws InconsistentDBException {
|
public String defaultAutoTypeSequence;
|
||||||
this(n, null);
|
public Boolean enableAutoType;
|
||||||
}
|
public Boolean enableSearching;
|
||||||
|
public UUID lastTopVisibleEntry;
|
||||||
public PwGroupV4(Node n, PwGroup p) throws InconsistentDBException {
|
private Date parentGroupLastMod;
|
||||||
node = n;
|
private Date creation;
|
||||||
parent = p;
|
private Date lastMod;
|
||||||
buildTree();
|
private Date lastAccess;
|
||||||
}
|
private Date expireDate;
|
||||||
|
private boolean expires = false;
|
||||||
private void buildTree() throws InconsistentDBException {
|
private long usageCount = 0;
|
||||||
NodeList children = node.getChildNodes();
|
|
||||||
|
|
||||||
|
public List<PwGroupV4> listGroups = new ArrayList<PwGroupV4>();
|
||||||
|
public List<PwEntryV4> listEntries = new ArrayList<PwEntryV4>();
|
||||||
|
|
||||||
|
public PwGroupV4() {
|
||||||
|
|
||||||
for ( int i = 0; i < children.getLength(); i++ ) {
|
|
||||||
Node child = children.item(i);
|
|
||||||
String name = child.getNodeName();
|
|
||||||
if ( name.equalsIgnoreCase("Group") ) {
|
|
||||||
PwGroupV4 group = new PwGroupV4(child, this);
|
|
||||||
childGroups.add(group);
|
|
||||||
} else if ( name.equalsIgnoreCase("Entry") ) {
|
|
||||||
PwEntryV4 entry = new PwEntryV4(child);
|
|
||||||
childEntries.add(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddGroup(PwGroupV4 subGroup, boolean takeOwnership) {
|
||||||
|
AddGroup(subGroup, takeOwnership, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddGroup(PwGroupV4 subGroup, boolean takeOwnership, boolean updateLocationChanged) {
|
||||||
|
if ( subGroup == null ) throw new RuntimeException("subGroup");
|
||||||
|
|
||||||
|
listGroups.add(subGroup);
|
||||||
|
|
||||||
|
if ( takeOwnership ) subGroup.parent = this;
|
||||||
|
|
||||||
|
if ( updateLocationChanged ) subGroup.parentGroupLastMod = new Date(System.currentTimeMillis());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEntry(PwEntryV4 pe, boolean takeOwnership) {
|
||||||
|
AddEntry(pe, takeOwnership, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEntry(PwEntryV4 pe, boolean takeOwnership, boolean updateLocationChanged) {
|
||||||
|
assert(pe != null);
|
||||||
|
|
||||||
|
listEntries.add(pe);
|
||||||
|
|
||||||
|
if ( takeOwnership ) pe.parent = this;
|
||||||
|
|
||||||
|
if ( updateLocationChanged ) pe.setLocationChanged(new Date(System.currentTimeMillis()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PwGroup getParent() {
|
public PwGroup getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
@@ -87,18 +112,88 @@ public class PwGroupV4 extends PwGroup {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PwGroupId getId() {
|
public PwGroupId getId() {
|
||||||
return new PwGroupIdV4(getUUID());
|
return new PwGroupIdV4(uuid);
|
||||||
}
|
|
||||||
|
|
||||||
public UUID getUUID() {
|
|
||||||
// TODO: Get UUID from document
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
// TODO Auto-generated method stub
|
return name;
|
||||||
return null;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getLastMod() {
|
||||||
|
return parentGroupLastMod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getCreationTime() {
|
||||||
|
return creation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getExpiryTime() {
|
||||||
|
return expireDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getLastAccessTime() {
|
||||||
|
return lastAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getLastModificationTime() {
|
||||||
|
return lastMod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getLocationChanged() {
|
||||||
|
return parentGroupLastMod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getUsageCount() {
|
||||||
|
return usageCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCreationTime(Date date) {
|
||||||
|
creation = date;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setExpiryTime(Date date) {
|
||||||
|
expireDate = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLastAccessTime(Date date) {
|
||||||
|
lastAccess = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLastModificationTime(Date date) {
|
||||||
|
lastMod = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLocationChanged(Date date) {
|
||||||
|
parentGroupLastMod = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsageCount(long count) {
|
||||||
|
usageCount = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean expires() {
|
||||||
|
return expires;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setExpires(boolean exp) {
|
||||||
|
expires = exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public abstract class AddEntry extends RunnableOnFinish {
|
|||||||
|
|
||||||
public static AddEntry getInstance(Database db, PwEntry entry, OnFinish finish) {
|
public static AddEntry getInstance(Database db, PwEntry entry, OnFinish finish) {
|
||||||
if ( entry instanceof PwEntryV3 ) {
|
if ( entry instanceof PwEntryV3 ) {
|
||||||
return new AddEntryV3(db, (PwEntryV3) entry, finish);
|
return new AddEntryV3(db, (PwEntry) entry, finish);
|
||||||
} else {
|
} else {
|
||||||
// TODO: Implement me
|
// TODO: Implement me
|
||||||
throw new RuntimeException("Not implemented yet.");
|
throw new RuntimeException("Not implemented yet.");
|
||||||
@@ -76,7 +76,7 @@ public abstract class AddEntry extends RunnableOnFinish {
|
|||||||
mDb.dirty.put(parent, new WeakReference<PwGroup>(parent));
|
mDb.dirty.put(parent, new WeakReference<PwGroup>(parent));
|
||||||
|
|
||||||
// Add entry to global
|
// Add entry to global
|
||||||
mDb.entries.put(Types.bytestoUUID(mEntry.uuid), new WeakReference<PwEntry>(mEntry));
|
mDb.entries.put(mEntry.getUUID(), new WeakReference<PwEntry>(mEntry));
|
||||||
|
|
||||||
if ( mDb.indexBuilt ) {
|
if ( mDb.indexBuilt ) {
|
||||||
// Add entry to search index
|
// Add entry to search index
|
||||||
|
|||||||
@@ -20,15 +20,15 @@
|
|||||||
package com.keepassdroid.database.edit;
|
package com.keepassdroid.database.edit;
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.Database;
|
||||||
import com.keepassdroid.database.PwDatabaseV3;
|
import com.keepassdroid.database.PwDatabase;
|
||||||
import com.keepassdroid.database.PwEntryV3;
|
import com.keepassdroid.database.PwEntry;
|
||||||
import com.keepassdroid.database.PwGroupV3;
|
import com.keepassdroid.database.PwGroupV3;
|
||||||
|
|
||||||
public class AddEntryV3 extends AddEntry {
|
public class AddEntryV3 extends AddEntry {
|
||||||
|
|
||||||
private PwEntryV3 mEntry;
|
private PwEntry mEntry;
|
||||||
|
|
||||||
protected AddEntryV3(Database db, PwEntryV3 entry, OnFinish finish) {
|
protected AddEntryV3(Database db, PwEntry entry, OnFinish finish) {
|
||||||
super(db, entry, finish);
|
super(db, entry, finish);
|
||||||
|
|
||||||
mEntry = entry;
|
mEntry = entry;
|
||||||
@@ -36,13 +36,13 @@ public class AddEntryV3 extends AddEntry {
|
|||||||
|
|
||||||
|
|
||||||
public void addEntry() {
|
public void addEntry() {
|
||||||
PwGroupV3 parent = mEntry.getParent();
|
PwGroupV3 parent = (PwGroupV3) mEntry.getParent();
|
||||||
|
|
||||||
// Add entry to group
|
// Add entry to group
|
||||||
parent.childEntries.add(mEntry);
|
parent.childEntries.add(mEntry);
|
||||||
|
|
||||||
// Add entry to PwDatabaseV3
|
// Add entry to PwDatabaseV3
|
||||||
PwDatabaseV3 pm = (PwDatabaseV3) mDb.pm;
|
PwDatabase pm = (PwDatabase) mDb.pm;
|
||||||
pm.getEntries().add(mEntry);
|
pm.getEntries().add(mEntry);
|
||||||
|
|
||||||
// Sort entries
|
// Sort entries
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ public class ImporterV3 extends Importer {
|
|||||||
return openDatabase(inStream, password, keyfile, new UpdateStatus());
|
return openDatabase(inStream, password, keyfile, new UpdateStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PwDatabaseV3 openDatabase( InputStream inStream, String password, String keyfile, UpdateStatus status )
|
public PwDatabase openDatabase( InputStream inStream, String password, String keyfile, UpdateStatus status )
|
||||||
throws IOException, InvalidKeyFileException, InvalidPasswordException, InvalidDBSignatureException, InvalidDBVersionException
|
throws IOException, InvalidKeyFileException, InvalidPasswordException, InvalidDBSignatureException, InvalidDBVersionException
|
||||||
{
|
{
|
||||||
PwDatabaseV3 newManager;
|
PwDatabaseV3 newManager;
|
||||||
@@ -375,7 +375,7 @@ public class ImporterV3 extends Importer {
|
|||||||
// Ignore field
|
// Ignore field
|
||||||
break;
|
break;
|
||||||
case 0x0001 :
|
case 0x0001 :
|
||||||
System.arraycopy(buf, offset, ent.uuid, 0, 16);
|
ent.setUUID(Types.bytestoUUID(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0002 :
|
case 0x0002 :
|
||||||
ent.groupId = LEDataInputStream.readInt(buf, offset);
|
ent.groupId = LEDataInputStream.readInt(buf, offset);
|
||||||
|
|||||||
@@ -19,30 +19,41 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database.load;
|
package com.keepassdroid.database.load;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Stack;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
|
||||||
|
|
||||||
import org.w3c.dom.Document;
|
import org.bouncycastle.crypto.StreamCipher;
|
||||||
import org.xml.sax.SAXException;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
import org.xmlpull.v1.XmlPullParserFactory;
|
||||||
|
|
||||||
|
import biz.source_code.base64Coder.Base64Coder;
|
||||||
|
|
||||||
import com.keepassdroid.UpdateStatus;
|
import com.keepassdroid.UpdateStatus;
|
||||||
import com.keepassdroid.crypto.CipherFactory;
|
import com.keepassdroid.crypto.CipherFactory;
|
||||||
|
import com.keepassdroid.crypto.PwStreamCipherFactory;
|
||||||
|
import com.keepassdroid.database.ITimeLogger;
|
||||||
import com.keepassdroid.database.PwCompressionAlgorithm;
|
import com.keepassdroid.database.PwCompressionAlgorithm;
|
||||||
|
import com.keepassdroid.database.PwCustomIcon;
|
||||||
import com.keepassdroid.database.PwDatabaseV4;
|
import com.keepassdroid.database.PwDatabaseV4;
|
||||||
import com.keepassdroid.database.PwDbHeaderV4;
|
import com.keepassdroid.database.PwDbHeaderV4;
|
||||||
import com.keepassdroid.database.exception.InconsistentDBException;
|
import com.keepassdroid.database.PwDeletedObject;
|
||||||
|
import com.keepassdroid.database.PwEntryV4;
|
||||||
|
import com.keepassdroid.database.PwGroupV4;
|
||||||
import com.keepassdroid.database.exception.InvalidDBSignatureException;
|
import com.keepassdroid.database.exception.InvalidDBSignatureException;
|
||||||
import com.keepassdroid.database.exception.InvalidDBVersionException;
|
import com.keepassdroid.database.exception.InvalidDBVersionException;
|
||||||
import com.keepassdroid.database.exception.InvalidKeyFileException;
|
import com.keepassdroid.database.exception.InvalidKeyFileException;
|
||||||
@@ -50,8 +61,12 @@ import com.keepassdroid.database.exception.InvalidPasswordException;
|
|||||||
import com.keepassdroid.stream.BetterCipherInputStream;
|
import com.keepassdroid.stream.BetterCipherInputStream;
|
||||||
import com.keepassdroid.stream.HashedBlockInputStream;
|
import com.keepassdroid.stream.HashedBlockInputStream;
|
||||||
import com.keepassdroid.stream.LEDataInputStream;
|
import com.keepassdroid.stream.LEDataInputStream;
|
||||||
|
import com.keepassdroid.utils.Types;
|
||||||
|
|
||||||
public class ImporterV4 extends Importer {
|
public class ImporterV4 extends Importer {
|
||||||
|
|
||||||
|
private StreamCipher randomStream;
|
||||||
|
private PwDatabaseV4 db;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PwDatabaseV4 openDatabase(InputStream inStream, String password,
|
public PwDatabaseV4 openDatabase(InputStream inStream, String password,
|
||||||
@@ -67,7 +82,7 @@ public class ImporterV4 extends Importer {
|
|||||||
InvalidKeyFileException, InvalidPasswordException,
|
InvalidKeyFileException, InvalidPasswordException,
|
||||||
InvalidDBSignatureException, InvalidDBVersionException {
|
InvalidDBSignatureException, InvalidDBVersionException {
|
||||||
|
|
||||||
PwDatabaseV4 db = new PwDatabaseV4();
|
db = new PwDatabaseV4();
|
||||||
|
|
||||||
PwDbHeaderV4 header = new PwDbHeaderV4(db);
|
PwDbHeaderV4 header = new PwDbHeaderV4(db);
|
||||||
|
|
||||||
@@ -112,6 +127,26 @@ public class ImporterV4 extends Importer {
|
|||||||
decompressed = hashed;
|
decompressed = hashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( header.protectedStreamKey == null ) {
|
||||||
|
assert(false);
|
||||||
|
throw new IOException("Invalid stream key.");
|
||||||
|
}
|
||||||
|
|
||||||
|
randomStream = PwStreamCipherFactory.getInstance(header.innerRandomStream, header.protectedStreamKey);
|
||||||
|
|
||||||
|
// TODO: Probably good to add a special note here for Arc4 mode
|
||||||
|
if ( randomStream == null ) {
|
||||||
|
throw new IOException("Protected stream type not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ReadXmlStreamed(decompressed);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// TODO: Measure whether this buffer is better or worse for performance
|
// TODO: Measure whether this buffer is better or worse for performance
|
||||||
BufferedInputStream bis2 = new BufferedInputStream(decompressed);
|
BufferedInputStream bis2 = new BufferedInputStream(decompressed);
|
||||||
|
|
||||||
@@ -124,18 +159,15 @@ public class ImporterV4 extends Importer {
|
|||||||
throw new IOException("Couldn't create document builder.");
|
throw new IOException("Couldn't create document builder.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Document doc;
|
Document doc;
|
||||||
try {
|
try {
|
||||||
doc = docB.parse(bis2);
|
doc = docB.parse(bis2);
|
||||||
} catch (SAXException e) {
|
} catch (SAXException e) {
|
||||||
throw new IOException("Failed to parse db xml: " + e.getLocalizedMessage());
|
throw new IOException("Failed to parse db xml: " + e.getLocalizedMessage());
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
try {
|
|
||||||
db.parseDB(doc);
|
|
||||||
} catch (InconsistentDBException e) {
|
|
||||||
throw new IOException(e.getLocalizedMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
FileOutputStream fos = new FileOutputStream("/sdcard/outputx.xml");
|
FileOutputStream fos = new FileOutputStream("/sdcard/outputx.xml");
|
||||||
@@ -151,6 +183,800 @@ public class ImporterV4 extends Importer {
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum KdbContext {
|
||||||
|
Null,
|
||||||
|
KeePassFile,
|
||||||
|
Meta,
|
||||||
|
Root,
|
||||||
|
MemoryProtection,
|
||||||
|
CustomIcons,
|
||||||
|
CustomIcon,
|
||||||
|
CustomData,
|
||||||
|
CustomDataItem,
|
||||||
|
RootDeletedObjects,
|
||||||
|
DeletedObject,
|
||||||
|
Group,
|
||||||
|
GroupTimes,
|
||||||
|
Entry,
|
||||||
|
EntryTimes,
|
||||||
|
EntryString,
|
||||||
|
EntryBinary,
|
||||||
|
EntryAutoType,
|
||||||
|
EntryAutoTypeItem,
|
||||||
|
EntryHistory
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String ElemDocNode = "KeePassFile";
|
||||||
|
private static final String ElemMeta = "Meta";
|
||||||
|
private static final String ElemRoot = "Root";
|
||||||
|
private static final String ElemGroup = "Group";
|
||||||
|
private static final String ElemEntry = "Entry";
|
||||||
|
|
||||||
|
private static final String ElemGenerator = "Generator";
|
||||||
|
private static final String ElemDbName = "DatabaseName";
|
||||||
|
private static final String ElemDbNameChanged = "DatabaseNameChanged";
|
||||||
|
private static final String ElemDbDesc = "DatabaseDescription";
|
||||||
|
private static final String ElemDbDescChanged = "DatabaseDescriptionChanged";
|
||||||
|
private static final String ElemDbDefaultUser = "DefaultUserName";
|
||||||
|
private static final String ElemDbDefaultUserChanged = "DefaultUserNameChanged";
|
||||||
|
private static final String ElemDbMntncHistoryDays = "MaintenanceHistoryDays";
|
||||||
|
private static final String ElemRecycleBinEnabled = "RecycleBinEnabled";
|
||||||
|
private static final String ElemRecycleBinUuid = "RecycleBinUUID";
|
||||||
|
private static final String ElemRecycleBinChanged = "RecycleBinChanged";
|
||||||
|
private static final String ElemEntryTemplatesGroup = "EntryTemplatesGroup";
|
||||||
|
private static final String ElemEntryTemplatesGroupChanged = "EntryTemplatesGroupChanged";
|
||||||
|
private static final String ElemLastSelectedGroup = "LastSelectedGroup";
|
||||||
|
private static final String ElemLastTopVisibleGroup = "LastTopVisibleGroup";
|
||||||
|
|
||||||
|
private static final String ElemMemoryProt = "MemoryProtection";
|
||||||
|
private static final String ElemProtTitle = "ProtectTitle";
|
||||||
|
private static final String ElemProtUserName = "ProtectUserName";
|
||||||
|
private static final String ElemProtPassword = "ProtectPassword";
|
||||||
|
private static final String ElemProtURL = "ProtectURL";
|
||||||
|
private static final String ElemProtNotes = "ProtectNotes";
|
||||||
|
private static final String ElemProtAutoHide = "AutoEnableVisualHiding";
|
||||||
|
|
||||||
|
private static final String ElemCustomIcons = "CustomIcons";
|
||||||
|
private static final String ElemCustomIconItem = "Icon";
|
||||||
|
private static final String ElemCustomIconItemID = "UUID";
|
||||||
|
private static final String ElemCustomIconItemData = "Data";
|
||||||
|
|
||||||
|
private static final String ElemAutoType = "AutoType";
|
||||||
|
private static final String ElemHistory = "History";
|
||||||
|
|
||||||
|
private static final String ElemName = "Name";
|
||||||
|
private static final String ElemNotes = "Notes";
|
||||||
|
private static final String ElemUuid = "UUID";
|
||||||
|
private static final String ElemIcon = "IconID";
|
||||||
|
private static final String ElemCustomIconID = "CustomIconUUID";
|
||||||
|
private static final String ElemFgColor = "ForegroundColor";
|
||||||
|
private static final String ElemBgColor = "BackgroundColor";
|
||||||
|
private static final String ElemOverrideUrl = "OverrideURL";
|
||||||
|
private static final String ElemTimes = "Times";
|
||||||
|
|
||||||
|
private static final String ElemCreationTime = "CreationTime";
|
||||||
|
private static final String ElemLastModTime = "LastModificationTime";
|
||||||
|
private static final String ElemLastAccessTime = "LastAccessTime";
|
||||||
|
private static final String ElemExpiryTime = "ExpiryTime";
|
||||||
|
private static final String ElemExpires = "Expires";
|
||||||
|
private static final String ElemUsageCount = "UsageCount";
|
||||||
|
private static final String ElemLocationChanged = "LocationChanged";
|
||||||
|
|
||||||
|
private static final String ElemGroupDefaultAutoTypeSeq = "DefaultAutoTypeSequence";
|
||||||
|
private static final String ElemEnableAutoType = "EnableAutoType";
|
||||||
|
private static final String ElemEnableSearching = "EnableSearching";
|
||||||
|
|
||||||
|
private static final String ElemString = "String";
|
||||||
|
private static final String ElemBinary = "Binary";
|
||||||
|
private static final String ElemKey = "Key";
|
||||||
|
private static final String ElemValue = "Value";
|
||||||
|
|
||||||
|
private static final String ElemAutoTypeEnabled = "Enabled";
|
||||||
|
private static final String ElemAutoTypeObfuscation = "DataTransferObfuscation";
|
||||||
|
private static final String ElemAutoTypeDefaultSeq = "DefaultSequence";
|
||||||
|
private static final String ElemAutoTypeItem = "Association";
|
||||||
|
private static final String ElemWindow = "Window";
|
||||||
|
private static final String ElemKeystrokeSequence = "KeystrokeSequence";
|
||||||
|
|
||||||
|
private static final String AttrProtected = "Protected";
|
||||||
|
|
||||||
|
private static final String ElemIsExpanded = "IsExpanded";
|
||||||
|
private static final String ElemLastTopVisibleEntry = "LastTopVisibleEntry";
|
||||||
|
|
||||||
|
private static final String ElemDeletedObjects = "DeletedObjects";
|
||||||
|
private static final String ElemDeletedObject = "DeletedObject";
|
||||||
|
private static final String ElemDeletionTime = "DeletionTime";
|
||||||
|
|
||||||
|
private static final String ValFalse = "False";
|
||||||
|
private static final String ValTrue = "True";
|
||||||
|
|
||||||
|
private static final String ElemCustomData = "CustomData";
|
||||||
|
private static final String ElemStringDictExItem = "Item";
|
||||||
|
|
||||||
|
private static final long DEFAULT_HISTORY_DAYS = 365;
|
||||||
|
|
||||||
|
|
||||||
|
private boolean readNextNode = true;
|
||||||
|
private Stack<PwGroupV4> ctxGroups = new Stack<PwGroupV4>();
|
||||||
|
private PwGroupV4 ctxGroup = null;
|
||||||
|
private PwEntryV4 ctxEntry = null;
|
||||||
|
private String ctxStringName = null;
|
||||||
|
private String ctxStringValue = null;
|
||||||
|
private String ctxBinaryName = null;
|
||||||
|
private byte[] ctxBinaryValue = null;
|
||||||
|
private String ctxATName = null;
|
||||||
|
private String ctxATSeq = null;
|
||||||
|
private boolean entryInHistory = false;
|
||||||
|
private PwEntryV4 ctxHistoryBase = null;
|
||||||
|
private PwDeletedObject ctxDeletedObject = null;
|
||||||
|
private UUID customIconID = PwDatabaseV4.UUID_ZERO;
|
||||||
|
private byte[] customIconData;
|
||||||
|
private String customDataKey = null;
|
||||||
|
private String customDataValue = null;
|
||||||
|
|
||||||
|
private void ReadXmlStreamed(InputStream readerStream) throws IOException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
ReadDocumentStreamed(CreatePullParser(readerStream));
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new IOException(e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static XmlPullParser CreatePullParser(InputStream readerStream) throws XmlPullParserException {
|
||||||
|
XmlPullParserFactory xppf = XmlPullParserFactory.newInstance();
|
||||||
|
xppf.setNamespaceAware(false);
|
||||||
|
|
||||||
|
XmlPullParser xpp = xppf.newPullParser();
|
||||||
|
xpp.setInput(readerStream, null);
|
||||||
|
|
||||||
|
return xpp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadDocumentStreamed(XmlPullParser xpp) throws XmlPullParserException, IOException {
|
||||||
|
|
||||||
|
ctxGroups.clear();
|
||||||
|
|
||||||
|
KdbContext ctx = KdbContext.Null;
|
||||||
|
|
||||||
|
readNextNode = true;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if ( readNextNode ) {
|
||||||
|
if( xpp.next() == XmlPullParser.END_DOCUMENT ) break;
|
||||||
|
} else {
|
||||||
|
readNextNode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ( xpp.getEventType() ) {
|
||||||
|
case XmlPullParser.START_TAG:
|
||||||
|
ctx = ReadXmlElement(ctx, xpp);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case XmlPullParser.END_TAG:
|
||||||
|
ctx = EndXmlElement(ctx, xpp);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error checks
|
||||||
|
if ( ctx != KdbContext.Null ) throw new IOException("Malformed");
|
||||||
|
if ( ctxGroups.size() != 0 ) throw new IOException("Malformed");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private KdbContext ReadXmlElement(KdbContext ctx, XmlPullParser xpp) throws XmlPullParserException, IOException {
|
||||||
|
String name = xpp.getName();
|
||||||
|
switch (ctx) {
|
||||||
|
case Null:
|
||||||
|
if ( name.equalsIgnoreCase(ElemDocNode) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.KeePassFile, xpp);
|
||||||
|
} else ReadUnknown(xpp);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KeePassFile:
|
||||||
|
if ( name.equalsIgnoreCase(ElemMeta) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.Meta, xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemRoot) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.Root, xpp);
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Meta:
|
||||||
|
if ( name.equalsIgnoreCase(ElemGenerator) ) {
|
||||||
|
ReadString(xpp); // Ignore
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemDbName) ) {
|
||||||
|
db.name = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemDbNameChanged) ) {
|
||||||
|
db.nameChanged = ReadTime(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemDbDesc) ) {
|
||||||
|
db.description = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemDbDescChanged) ) {
|
||||||
|
db.descriptionChanged = ReadTime(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemDbDefaultUser) ) {
|
||||||
|
db.defaultUserName = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemDbDefaultUserChanged) ) {
|
||||||
|
db.defaultUserNameChanged = ReadTime(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemDbMntncHistoryDays) ) {
|
||||||
|
db.maintenanceHistoryDays = ReadUInt(xpp, DEFAULT_HISTORY_DAYS);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemMemoryProt) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.MemoryProtection, xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemCustomIcons) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.CustomIcons, xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemRecycleBinEnabled) ) {
|
||||||
|
db.recycleBinEnabled = ReadBool(xpp, true);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemRecycleBinUuid) ) {
|
||||||
|
db.recycleBinUUID = ReadUuid(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemRecycleBinChanged) ) {
|
||||||
|
db.recycleBinChanged = ReadTime(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemEntryTemplatesGroup) ) {
|
||||||
|
db.entryTemplatesGroup = ReadUuid(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemEntryTemplatesGroupChanged) ) {
|
||||||
|
db.entryTemplatesGroupChanged = ReadTime(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemLastSelectedGroup) ) {
|
||||||
|
db.lastSelectedGroup = ReadUuid(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemLastTopVisibleGroup) ) {
|
||||||
|
db.lastTopVisibleGroup = ReadUuid(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemCustomData) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.CustomData, xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MemoryProtection:
|
||||||
|
if ( name.equalsIgnoreCase(ElemProtTitle) ) {
|
||||||
|
db.memoryProtection.protectTitle = ReadBool(xpp, false);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemProtUserName) ) {
|
||||||
|
db.memoryProtection.protectUserName = ReadBool(xpp, false);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemProtPassword) ) {
|
||||||
|
db.memoryProtection.protectPassword = ReadBool(xpp, false);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemProtURL) ) {
|
||||||
|
db.memoryProtection.protectUrl = ReadBool(xpp, false);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemProtNotes) ) {
|
||||||
|
db.memoryProtection.protectNotes = ReadBool(xpp, false);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemProtAutoHide) ) {
|
||||||
|
db.memoryProtection.autoEnableVisualHiding = ReadBool(xpp, false);
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CustomIcons:
|
||||||
|
if ( name.equalsIgnoreCase(ElemCustomIconItem) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.CustomIcon, xpp);
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CustomIcon:
|
||||||
|
if ( name.equalsIgnoreCase(ElemCustomIconItemID) ) {
|
||||||
|
customIconID = ReadUuid(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemCustomIconItemData) ) {
|
||||||
|
String strData = ReadString(xpp);
|
||||||
|
if ( strData != null && strData.length() > 0 ) {
|
||||||
|
customIconData = Base64Coder.decode(strData);
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CustomData:
|
||||||
|
if ( name.equalsIgnoreCase(ElemStringDictExItem) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.CustomDataItem, xpp);
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CustomDataItem:
|
||||||
|
if ( name.equalsIgnoreCase(ElemKey) ) {
|
||||||
|
customDataKey = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemValue) ) {
|
||||||
|
customDataValue = ReadString(xpp);
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Root:
|
||||||
|
if ( name.equalsIgnoreCase(ElemGroup) ) {
|
||||||
|
assert(ctxGroups.size() == 0);
|
||||||
|
if ( ctxGroups.size() != 0 ) throw new IOException("Group list should be empty.");
|
||||||
|
|
||||||
|
db.rootGroup = new PwGroupV4();
|
||||||
|
ctxGroups.push(db.rootGroup);
|
||||||
|
ctxGroup = ctxGroups.peek();
|
||||||
|
|
||||||
|
return SwitchContext(ctx, KdbContext.Group, xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemDeletedObjects) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.RootDeletedObjects, xpp);
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Group:
|
||||||
|
if ( name.equalsIgnoreCase(ElemUuid) ) {
|
||||||
|
ctxGroup.uuid = ReadUuid(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemName) ) {
|
||||||
|
ctxGroup.name = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemNotes) ) {
|
||||||
|
ctxGroup.notes = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemIcon) ) {
|
||||||
|
ctxGroup.iconId = (int)ReadUInt(xpp, 0);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemCustomIconID) ) {
|
||||||
|
ctxGroup.customIconUuid = ReadUuid(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemTimes) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.GroupTimes, xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemIsExpanded) ) {
|
||||||
|
ctxGroup.isExpanded = ReadBool(xpp, true);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemGroupDefaultAutoTypeSeq) ) {
|
||||||
|
ctxGroup.defaultAutoTypeSequence = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemEnableAutoType) ) {
|
||||||
|
ctxGroup.enableAutoType = StringToBoolean(ReadString(xpp));
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemEnableSearching) ) {
|
||||||
|
ctxGroup.enableSearching = StringToBoolean(ReadString(xpp));
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemLastTopVisibleEntry) ) {
|
||||||
|
ctxGroup.lastTopVisibleEntry = ReadUuid(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemGroup) ) {
|
||||||
|
ctxGroup = new PwGroupV4();
|
||||||
|
ctxGroups.peek().AddGroup(ctxGroup, true);
|
||||||
|
ctxGroups.push(ctxGroup);
|
||||||
|
|
||||||
|
return SwitchContext(ctx, KdbContext.Group, xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemEntry) ) {
|
||||||
|
ctxEntry = new PwEntryV4();
|
||||||
|
ctxGroup.AddEntry(ctxEntry, true);
|
||||||
|
|
||||||
|
entryInHistory = false;
|
||||||
|
return SwitchContext(ctx, KdbContext.Entry, xpp);
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Entry:
|
||||||
|
if ( name.equalsIgnoreCase(ElemUuid) ) {
|
||||||
|
ctxEntry.setUUID(ReadUuid(xpp));
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemIcon) ) {
|
||||||
|
ctxEntry.imageId = (int)ReadUInt(xpp, 0);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemCustomIconID) ) {
|
||||||
|
ctxEntry.customIconUuid = ReadUuid(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemFgColor) ) {
|
||||||
|
ctxEntry.foregroundColor = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemBgColor) ) {
|
||||||
|
ctxEntry.backgroupColor = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemOverrideUrl) ) {
|
||||||
|
ctxEntry.overrideURL = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemTimes) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.EntryTimes, xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemString) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.EntryString, xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemBinary) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.EntryBinary, xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemAutoType) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.EntryAutoType, xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemHistory) ) {
|
||||||
|
assert(!entryInHistory);
|
||||||
|
|
||||||
|
if ( ! entryInHistory ) {
|
||||||
|
ctxHistoryBase = ctxEntry;
|
||||||
|
return SwitchContext(ctx, KdbContext.EntryHistory, xpp);
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GroupTimes:
|
||||||
|
case EntryTimes:
|
||||||
|
ITimeLogger tl;
|
||||||
|
if ( ctx == KdbContext.GroupTimes ) {
|
||||||
|
tl = ctxGroup;
|
||||||
|
} else {
|
||||||
|
tl = ctxEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( name.equalsIgnoreCase(ElemLastModTime) ) {
|
||||||
|
tl.setLastModificationTime(ReadTime(xpp));
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemCreationTime) ) {
|
||||||
|
tl.setCreationTime(ReadTime(xpp));
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemLastAccessTime) ) {
|
||||||
|
tl.setLastAccessTime(ReadTime(xpp));
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemExpiryTime) ) {
|
||||||
|
tl.setExpiryTime(ReadTime(xpp));
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemExpires) ) {
|
||||||
|
tl.setExpires(ReadBool(xpp, false));
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemUsageCount) ) {
|
||||||
|
tl.setUsageCount(ReadULong(xpp, 0));
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemLocationChanged) ) {
|
||||||
|
tl.setLocationChanged(ReadTime(xpp));
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EntryString:
|
||||||
|
if ( name.equalsIgnoreCase(ElemKey) ) {
|
||||||
|
ctxStringName = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemValue) ) {
|
||||||
|
ctxStringValue = ReadProtectedString(xpp);
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EntryBinary:
|
||||||
|
if ( name.equalsIgnoreCase(ElemKey) ) {
|
||||||
|
ctxBinaryName = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemValue) ) {
|
||||||
|
ctxBinaryValue = ReadProtectedBinary(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EntryAutoType:
|
||||||
|
if ( name.equalsIgnoreCase(ElemAutoTypeEnabled) ) {
|
||||||
|
ctxEntry.autoType.enabled = ReadBool(xpp, true);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemAutoTypeObfuscation) ) {
|
||||||
|
ctxEntry.autoType.obfuscationOptions = ReadUInt(xpp, 0);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemAutoTypeDefaultSeq) ) {
|
||||||
|
ctxEntry.autoType.defaultSequence = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemAutoTypeItem) ) {
|
||||||
|
return SwitchContext(ctx, KdbContext.EntryAutoTypeItem, xpp);
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EntryAutoTypeItem:
|
||||||
|
if ( name.equalsIgnoreCase(ElemWindow) ) {
|
||||||
|
ctxATName = ReadString(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemKeystrokeSequence) ) {
|
||||||
|
ctxATSeq = ReadString(xpp);
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EntryHistory:
|
||||||
|
if ( name.equalsIgnoreCase(ElemEntry) ) {
|
||||||
|
ctxEntry = new PwEntryV4();
|
||||||
|
ctxHistoryBase.history.add(ctxEntry);
|
||||||
|
|
||||||
|
entryInHistory = true;
|
||||||
|
return SwitchContext(ctx, KdbContext.Entry, xpp);
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RootDeletedObjects:
|
||||||
|
if ( name.equalsIgnoreCase(ElemDeletedObject) ) {
|
||||||
|
ctxDeletedObject = new PwDeletedObject();
|
||||||
|
db.deletedObjects.add(ctxDeletedObject);
|
||||||
|
|
||||||
|
return SwitchContext(ctx, KdbContext.DeletedObject, xpp);
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DeletedObject:
|
||||||
|
if ( name.equalsIgnoreCase(ElemUuid) ) {
|
||||||
|
ctxDeletedObject.uuid = ReadUuid(xpp);
|
||||||
|
} else if ( name.equalsIgnoreCase(ElemDeletionTime) ) {
|
||||||
|
ctxDeletedObject.setDeletionTime(ReadTime(xpp));
|
||||||
|
} else {
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
private KdbContext EndXmlElement(KdbContext ctx, XmlPullParser xpp) throws XmlPullParserException {
|
||||||
|
assert(xpp.getEventType() == XmlPullParser.END_TAG);
|
||||||
|
|
||||||
|
String name = xpp.getName();
|
||||||
|
if ( ctx == KdbContext.KeePassFile && name.equalsIgnoreCase(ElemDocNode) ) {
|
||||||
|
return KdbContext.Null;
|
||||||
|
} else if ( ctx == KdbContext.Meta && name.equalsIgnoreCase(ElemMeta) ) {
|
||||||
|
return KdbContext.KeePassFile;
|
||||||
|
} else if ( ctx == KdbContext.Root && name.equalsIgnoreCase(ElemRoot) ) {
|
||||||
|
return KdbContext.KeePassFile;
|
||||||
|
} else if ( ctx == KdbContext.MemoryProtection && name.equalsIgnoreCase(ElemMemoryProt) ) {
|
||||||
|
return KdbContext.Meta;
|
||||||
|
} else if ( ctx == KdbContext.CustomIcons && name.equalsIgnoreCase(ElemCustomIcons) ) {
|
||||||
|
return KdbContext.Meta;
|
||||||
|
} else if ( ctx == KdbContext.CustomIcon && name.equalsIgnoreCase(ElemCustomIconItem) ) {
|
||||||
|
if ( ! customIconID.equals(PwDatabaseV4.UUID_ZERO) ) {
|
||||||
|
db.customIcons.add(new PwCustomIcon(customIconID, customIconData));
|
||||||
|
} else assert(false);
|
||||||
|
|
||||||
|
customIconID = PwDatabaseV4.UUID_ZERO;
|
||||||
|
customIconData = null;
|
||||||
|
|
||||||
|
return KdbContext.CustomIcons;
|
||||||
|
} else if ( ctx == KdbContext.CustomData && name.equalsIgnoreCase(ElemCustomData) ) {
|
||||||
|
return KdbContext.Meta;
|
||||||
|
} else if ( ctx == KdbContext.CustomDataItem && name.equalsIgnoreCase(ElemStringDictExItem) ) {
|
||||||
|
if ( customDataKey != null && customDataValue != null) {
|
||||||
|
db.customData.put(customDataKey, customDataValue);
|
||||||
|
} else assert(false);
|
||||||
|
|
||||||
|
customDataKey = null;
|
||||||
|
customDataValue = null;
|
||||||
|
|
||||||
|
return KdbContext.CustomData;
|
||||||
|
} else if ( ctx == KdbContext.Group && name.equalsIgnoreCase(ElemGroup) ) {
|
||||||
|
if ( ctxGroup.uuid == null || ctxGroup.uuid.equals(PwDatabaseV4.UUID_ZERO) ) {
|
||||||
|
ctxGroup.uuid = UUID.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxGroups.pop();
|
||||||
|
|
||||||
|
if ( ctxGroups.size() == 0 ) {
|
||||||
|
ctxGroup = null;
|
||||||
|
return KdbContext.Root;
|
||||||
|
} else {
|
||||||
|
ctxGroup = ctxGroups.peek();
|
||||||
|
return KdbContext.Group;
|
||||||
|
}
|
||||||
|
} else if ( ctx == KdbContext.GroupTimes && name.equalsIgnoreCase(ElemTimes) ) {
|
||||||
|
return KdbContext.Group;
|
||||||
|
} else if ( ctx == KdbContext.Entry && name.equalsIgnoreCase(ElemEntry) ) {
|
||||||
|
if ( ctxEntry.uuid == null || ctxEntry.uuid.equals(PwDatabaseV4.UUID_ZERO) ) {
|
||||||
|
ctxEntry.uuid = UUID.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( entryInHistory ) {
|
||||||
|
ctxEntry = ctxHistoryBase;
|
||||||
|
return KdbContext.EntryHistory;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KdbContext.Group;
|
||||||
|
} else if ( ctx == KdbContext.EntryTimes && name.equalsIgnoreCase(ElemTimes) ) {
|
||||||
|
return KdbContext.Entry;
|
||||||
|
} else if ( ctx == KdbContext.EntryString && name.equalsIgnoreCase(ElemString) ) {
|
||||||
|
ctxEntry.strings.put(ctxStringName, ctxStringValue);
|
||||||
|
ctxStringName = null;
|
||||||
|
ctxStringValue = null;
|
||||||
|
|
||||||
|
return KdbContext.Entry;
|
||||||
|
} else if ( ctx == KdbContext.EntryBinary && name.equalsIgnoreCase(ElemBinary) ) {
|
||||||
|
ctxEntry.binaries.put(ctxBinaryName, ctxBinaryValue);
|
||||||
|
ctxBinaryName = null;
|
||||||
|
ctxBinaryValue = null;
|
||||||
|
|
||||||
|
return KdbContext.Entry;
|
||||||
|
} else if ( ctx == KdbContext.EntryAutoType && name.equalsIgnoreCase(ElemAutoType) ) {
|
||||||
|
return KdbContext.Entry;
|
||||||
|
} else if ( ctx == KdbContext.EntryAutoTypeItem && name.equalsIgnoreCase(ElemAutoTypeItem) ) {
|
||||||
|
ctxEntry.autoType.put(ctxATName, ctxATSeq);
|
||||||
|
ctxATName = null;
|
||||||
|
ctxATSeq = null;
|
||||||
|
|
||||||
|
return KdbContext.EntryAutoType;
|
||||||
|
} else if ( ctx == KdbContext.EntryHistory && name.equalsIgnoreCase(ElemHistory) ) {
|
||||||
|
entryInHistory = false;
|
||||||
|
return KdbContext.Entry;
|
||||||
|
} else if ( ctx == KdbContext.RootDeletedObjects && name.equalsIgnoreCase(ElemDeletedObjects) ) {
|
||||||
|
return KdbContext.Root;
|
||||||
|
} else if ( ctx == KdbContext.DeletedObject && name.equalsIgnoreCase(ElemDeletedObject) ) {
|
||||||
|
ctxDeletedObject = null;
|
||||||
|
return KdbContext.RootDeletedObjects;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
|
||||||
|
throw new RuntimeException("Invalid end element");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd'T'HH:mm:ss'Z'");
|
||||||
|
private Date ReadTime(XmlPullParser xpp) throws IOException, XmlPullParserException {
|
||||||
|
String sDate = ReadString(xpp);
|
||||||
|
|
||||||
|
Date utcDate;
|
||||||
|
try {
|
||||||
|
utcDate = dateFormat.parse(sDate);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new IOException(e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return utcDate;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadUnknown(XmlPullParser xpp) throws XmlPullParserException, IOException {
|
||||||
|
assert(false);
|
||||||
|
|
||||||
|
if ( xpp.isEmptyElementTag() ) return;
|
||||||
|
|
||||||
|
String unknownName = xpp.getName();
|
||||||
|
ProcessNode(xpp);
|
||||||
|
|
||||||
|
while (xpp.next() != XmlPullParser.END_DOCUMENT ) {
|
||||||
|
if ( xpp.getEventType() == XmlPullParser.END_TAG ) break;
|
||||||
|
if ( xpp.getEventType() == XmlPullParser.START_TAG ) continue;
|
||||||
|
|
||||||
|
ReadUnknown(xpp);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(xpp.getName() == unknownName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean ReadBool(XmlPullParser xpp, boolean bDefault) throws IOException, XmlPullParserException {
|
||||||
|
String str = ReadString(xpp);
|
||||||
|
|
||||||
|
if ( str.equalsIgnoreCase("true") ) {
|
||||||
|
return true;
|
||||||
|
} else if ( str.equalsIgnoreCase("false") ) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return bDefault;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UUID ReadUuid(XmlPullParser xpp) throws IOException, XmlPullParserException {
|
||||||
|
String encoded = ReadString(xpp);
|
||||||
|
|
||||||
|
if (encoded == null || encoded.length() == 0 ) {
|
||||||
|
return PwDatabaseV4.UUID_ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Switch to framework Base64 once API level 8 is the minimum
|
||||||
|
byte[] buf = Base64Coder.decode(encoded);
|
||||||
|
|
||||||
|
return Types.bytestoUUID(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long MAX_UINT = 4294967296L; // 2^32
|
||||||
|
private long ReadUInt(XmlPullParser xpp, long uDefault) throws IOException, XmlPullParserException {
|
||||||
|
long u;
|
||||||
|
|
||||||
|
u = ReadULong(xpp, uDefault);
|
||||||
|
if ( u < 0 || u > MAX_UINT ) {
|
||||||
|
throw new NumberFormatException("Outside of the uint size");
|
||||||
|
}
|
||||||
|
|
||||||
|
return u;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private long ReadULong(XmlPullParser xpp, long uDefault) throws IOException, XmlPullParserException {
|
||||||
|
String str = ReadString(xpp);
|
||||||
|
|
||||||
|
long u;
|
||||||
|
try {
|
||||||
|
u = Long.parseLong(str);
|
||||||
|
} catch( NumberFormatException e) {
|
||||||
|
u = uDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
return u;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String ReadProtectedString(XmlPullParser xpp) throws XmlPullParserException, IOException {
|
||||||
|
byte[] buf = ProcessNode(xpp);
|
||||||
|
|
||||||
|
if ( buf != null) {
|
||||||
|
try {
|
||||||
|
return new String(buf, "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new IOException(e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReadString(xpp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] ReadProtectedBinary(XmlPullParser xpp) throws XmlPullParserException, IOException {
|
||||||
|
byte[] buf = ProcessNode(xpp);
|
||||||
|
|
||||||
|
if ( buf != null ) return buf;
|
||||||
|
|
||||||
|
String base64 = ReadString(xpp);
|
||||||
|
if ( base64.length() == 0 ) return new byte[0];
|
||||||
|
|
||||||
|
return Base64Coder.decode(base64);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String ReadString(XmlPullParser xpp) throws IOException, XmlPullParserException {
|
||||||
|
byte[] buf = ProcessNode(xpp);
|
||||||
|
|
||||||
|
if ( buf != null ) {
|
||||||
|
try {
|
||||||
|
return new String(buf, "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new IOException(e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//readNextNode = false;
|
||||||
|
return xpp.nextText();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String ReadStringRaw(XmlPullParser xpp) throws XmlPullParserException, IOException {
|
||||||
|
|
||||||
|
//readNextNode = false;
|
||||||
|
return xpp.nextText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] ProcessNode(XmlPullParser xpp) throws XmlPullParserException, IOException {
|
||||||
|
assert(xpp.getEventType() == XmlPullParser.START_TAG);
|
||||||
|
|
||||||
|
byte[] buf = null;
|
||||||
|
|
||||||
|
if ( xpp.getAttributeCount() > 0 ) {
|
||||||
|
String protect = xpp.getAttributeValue(null, AttrProtected);
|
||||||
|
if ( protect != null && protect.equalsIgnoreCase(ValTrue) ) {
|
||||||
|
String encrypted = ReadStringRaw(xpp);
|
||||||
|
|
||||||
|
if ( encrypted.length() > 0 ) {
|
||||||
|
buf = Base64Coder.decode(encrypted);
|
||||||
|
byte[] plainText = new byte[buf.length];
|
||||||
|
|
||||||
|
randomStream.processBytes(buf, 0, buf.length, plainText, 0);
|
||||||
|
|
||||||
|
return plainText;
|
||||||
|
} else {
|
||||||
|
buf = new byte[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
private KdbContext SwitchContext(KdbContext ctxCurrent, KdbContext ctxNew,
|
||||||
|
XmlPullParser xpp) throws XmlPullParserException, IOException {
|
||||||
|
|
||||||
|
if ( xpp.isEmptyElementTag() ) {
|
||||||
|
xpp.next(); // Consume the end tag
|
||||||
|
return ctxCurrent;
|
||||||
|
}
|
||||||
|
return ctxNew;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Boolean StringToBoolean(String str) {
|
||||||
|
if ( str == null || str.length() == 0 ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String trimmed = str.trim();
|
||||||
|
if ( trimmed.equalsIgnoreCase("true") ) {
|
||||||
|
return true;
|
||||||
|
} else if ( trimmed.equalsIgnoreCase("false") ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ public class PwEntryOutputV3 {
|
|||||||
// UUID
|
// UUID
|
||||||
mOS.write(UUID_FIELD_TYPE);
|
mOS.write(UUID_FIELD_TYPE);
|
||||||
mOS.write(UUID_FIELD_SIZE);
|
mOS.write(UUID_FIELD_SIZE);
|
||||||
mOS.write(mPE.uuid);
|
mOS.write(Types.UUIDtoBytes(mPE.getUUID()));
|
||||||
|
|
||||||
// Group ID
|
// Group ID
|
||||||
mOS.write(GROUPID_FIELD_TYPE);
|
mOS.write(GROUPID_FIELD_TYPE);
|
||||||
|
|||||||
@@ -32,10 +32,8 @@ import android.util.Log;
|
|||||||
|
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.Database;
|
||||||
import com.keepassdroid.database.PwEntry;
|
import com.keepassdroid.database.PwEntry;
|
||||||
import com.keepassdroid.database.PwEntryV3;
|
|
||||||
import com.keepassdroid.database.PwGroup;
|
import com.keepassdroid.database.PwGroup;
|
||||||
import com.keepassdroid.database.PwGroupV3;
|
import com.keepassdroid.database.PwGroupV3;
|
||||||
import com.keepassdroid.utils.Types;
|
|
||||||
|
|
||||||
public class SearchDbHelper {
|
public class SearchDbHelper {
|
||||||
private static final String DATABASE_NAME = "search";
|
private static final String DATABASE_NAME = "search";
|
||||||
@@ -100,7 +98,7 @@ public class SearchDbHelper {
|
|||||||
private ContentValues buildNewEntryContent(PwEntry entry) {
|
private ContentValues buildNewEntryContent(PwEntry entry) {
|
||||||
|
|
||||||
ContentValues cv = new ContentValues();
|
ContentValues cv = new ContentValues();
|
||||||
UUID uuid = Types.bytestoUUID(entry.uuid);
|
UUID uuid = entry.getUUID();
|
||||||
String uuidStr = uuid.toString();
|
String uuidStr = uuid.toString();
|
||||||
|
|
||||||
cv.put(KEY_UUID, uuidStr);
|
cv.put(KEY_UUID, uuidStr);
|
||||||
@@ -137,7 +135,7 @@ public class SearchDbHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deleteEntry(PwEntry entry) {
|
public void deleteEntry(PwEntry entry) {
|
||||||
UUID uuid = Types.bytestoUUID(entry.uuid);
|
UUID uuid = entry.getUUID();
|
||||||
String uuidStr = uuid.toString();
|
String uuidStr = uuid.toString();
|
||||||
|
|
||||||
mDb.delete(SEARCH_TABLE, KEY_UUID + " = ?", new String[] {uuidStr});
|
mDb.delete(SEARCH_TABLE, KEY_UUID + " = ?", new String[] {uuidStr});
|
||||||
@@ -157,7 +155,7 @@ public class SearchDbHelper {
|
|||||||
String sUUID = cursor.getString(0);
|
String sUUID = cursor.getString(0);
|
||||||
UUID uuid = UUID.fromString(sUUID);
|
UUID uuid = UUID.fromString(sUUID);
|
||||||
Log.d("TAG", uuid.toString());
|
Log.d("TAG", uuid.toString());
|
||||||
PwEntryV3 entry = (PwEntryV3) db.entries.get(uuid).get();
|
PwEntry entry = (PwEntry) db.entries.get(uuid).get();
|
||||||
group.childEntries.add(entry);
|
group.childEntries.add(entry);
|
||||||
|
|
||||||
cursor.moveToNext();
|
cursor.moveToNext();
|
||||||
|
|||||||
@@ -178,19 +178,31 @@ public class Types {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static UUID bytestoUUID(byte[] buf) {
|
public static UUID bytestoUUID(byte[] buf) {
|
||||||
|
return bytestoUUID(buf, 0);
|
||||||
long msb = 0;
|
}
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
msb = (msb << 8) | (buf[i] & 0xff);
|
public static UUID bytestoUUID(byte[] buf, int offset) {
|
||||||
}
|
|
||||||
|
|
||||||
long lsb = 0;
|
long lsb = 0;
|
||||||
for (int i = 8; i < 16; i++) {
|
for (int i = 15; i >= 8; i--) {
|
||||||
lsb = (lsb << 8) | (buf[i] & 0xff);
|
lsb = (lsb << 8) | (buf[i + offset] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
long msb = 0;
|
||||||
|
for (int i = 7; i >= 0; i--) {
|
||||||
|
msb = (msb << 8) | (buf[i + offset] & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new UUID(msb, lsb);
|
return new UUID(msb, lsb);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] UUIDtoBytes(UUID uuid) {
|
||||||
|
byte[] buf = new byte[16];
|
||||||
|
|
||||||
|
LEDataOutputStream.writeLong(uuid.getMostSignificantBits(), buf, 0);
|
||||||
|
LEDataOutputStream.writeLong(uuid.getLeastSignificantBits(), buf, 8);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
8
src/org/bouncycastle/crypto/CipherParameters.java
Normal file
8
src/org/bouncycastle/crypto/CipherParameters.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package org.bouncycastle.crypto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* all parameter classes implement this.
|
||||||
|
*/
|
||||||
|
public interface CipherParameters
|
||||||
|
{
|
||||||
|
}
|
||||||
30
src/org/bouncycastle/crypto/DataLengthException.java
Normal file
30
src/org/bouncycastle/crypto/DataLengthException.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package org.bouncycastle.crypto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this exception is thrown if a buffer that is meant to have output
|
||||||
|
* copied into it turns out to be too short, or if we've been given
|
||||||
|
* insufficient input. In general this exception will get thrown rather
|
||||||
|
* than an ArrayOutOfBounds exception.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class DataLengthException
|
||||||
|
extends RuntimeCryptoException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* base constructor.
|
||||||
|
*/
|
||||||
|
public DataLengthException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a DataLengthException with the given message.
|
||||||
|
*
|
||||||
|
* @param message the message to be carried with the exception.
|
||||||
|
*/
|
||||||
|
public DataLengthException(
|
||||||
|
String message)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/org/bouncycastle/crypto/MaxBytesExceededException.java
Normal file
28
src/org/bouncycastle/crypto/MaxBytesExceededException.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package org.bouncycastle.crypto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this exception is thrown whenever a cipher requires a change of key, iv
|
||||||
|
* or similar after x amount of bytes enciphered
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class MaxBytesExceededException
|
||||||
|
extends RuntimeCryptoException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* base constructor.
|
||||||
|
*/
|
||||||
|
public MaxBytesExceededException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create an with the given message.
|
||||||
|
*
|
||||||
|
* @param message the message to be carried with the exception.
|
||||||
|
*/
|
||||||
|
public MaxBytesExceededException(
|
||||||
|
String message)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/org/bouncycastle/crypto/RuntimeCryptoException.java
Normal file
27
src/org/bouncycastle/crypto/RuntimeCryptoException.java
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package org.bouncycastle.crypto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the foundation class for the exceptions thrown by the crypto packages.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class RuntimeCryptoException
|
||||||
|
extends RuntimeException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* base constructor.
|
||||||
|
*/
|
||||||
|
public RuntimeCryptoException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a RuntimeCryptoException with the given message.
|
||||||
|
*
|
||||||
|
* @param message the message to be carried with the exception.
|
||||||
|
*/
|
||||||
|
public RuntimeCryptoException(
|
||||||
|
String message)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/org/bouncycastle/crypto/StreamCipher.java
Normal file
53
src/org/bouncycastle/crypto/StreamCipher.java
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package org.bouncycastle.crypto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the interface stream ciphers conform to.
|
||||||
|
*/
|
||||||
|
public interface StreamCipher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initialise the cipher.
|
||||||
|
*
|
||||||
|
* @param forEncryption if true the cipher is initialised for
|
||||||
|
* encryption, if false for decryption.
|
||||||
|
* @param params the key and other data required by the cipher.
|
||||||
|
* @exception IllegalArgumentException if the params argument is
|
||||||
|
* inappropriate.
|
||||||
|
*/
|
||||||
|
public void init(boolean forEncryption, CipherParameters params)
|
||||||
|
throws IllegalArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the algorithm the cipher implements.
|
||||||
|
*
|
||||||
|
* @return the name of the algorithm the cipher implements.
|
||||||
|
*/
|
||||||
|
public String getAlgorithmName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* encrypt/decrypt a single byte returning the result.
|
||||||
|
*
|
||||||
|
* @param in the byte to be processed.
|
||||||
|
* @return the result of processing the input byte.
|
||||||
|
*/
|
||||||
|
public byte returnByte(byte in);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* process a block of bytes from in putting the result into out.
|
||||||
|
*
|
||||||
|
* @param in the input byte array.
|
||||||
|
* @param inOff the offset into the in array where the data to be processed starts.
|
||||||
|
* @param len the number of bytes to be processed.
|
||||||
|
* @param out the output buffer the processed bytes go into.
|
||||||
|
* @param outOff the offset into the output byte array the processed data starts at.
|
||||||
|
* @exception DataLengthException if the output buffer is too small.
|
||||||
|
*/
|
||||||
|
public void processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
|
||||||
|
throws DataLengthException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reset the cipher. This leaves it in the same state
|
||||||
|
* it was at after the last init (if there was one).
|
||||||
|
*/
|
||||||
|
public void reset();
|
||||||
|
}
|
||||||
377
src/org/bouncycastle/crypto/engines/Salsa20Engine.java
Normal file
377
src/org/bouncycastle/crypto/engines/Salsa20Engine.java
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
package org.bouncycastle.crypto.engines;
|
||||||
|
|
||||||
|
import org.bouncycastle.crypto.CipherParameters;
|
||||||
|
import org.bouncycastle.crypto.DataLengthException;
|
||||||
|
import org.bouncycastle.crypto.MaxBytesExceededException;
|
||||||
|
import org.bouncycastle.crypto.StreamCipher;
|
||||||
|
import org.bouncycastle.crypto.params.KeyParameter;
|
||||||
|
import org.bouncycastle.crypto.params.ParametersWithIV;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Salsa20Engine
|
||||||
|
implements StreamCipher
|
||||||
|
{
|
||||||
|
/** Constants */
|
||||||
|
private final static int stateSize = 16; // 16, 32 bit ints = 64 bytes
|
||||||
|
|
||||||
|
private final static byte[]
|
||||||
|
sigma = toByteArray("expand 32-byte k"),
|
||||||
|
tau = toByteArray("expand 16-byte k");
|
||||||
|
|
||||||
|
// No sure why the version in org.bouncycastle.util.Strings was throwing NoSuchMethodErrors
|
||||||
|
private static byte[] toByteArray(String string)
|
||||||
|
{
|
||||||
|
byte[] bytes = new byte[string.length()];
|
||||||
|
|
||||||
|
for (int i = 0; i != bytes.length; i++)
|
||||||
|
{
|
||||||
|
char ch = string.charAt(i);
|
||||||
|
|
||||||
|
bytes[i] = (byte)ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* variables to hold the state of the engine
|
||||||
|
* during encryption and decryption
|
||||||
|
*/
|
||||||
|
private int index = 0;
|
||||||
|
private int[] engineState = new int[stateSize]; // state
|
||||||
|
private int[] x = new int[stateSize] ; // internal buffer
|
||||||
|
private byte[] keyStream = new byte[stateSize * 4], // expanded state, 64 bytes
|
||||||
|
workingKey = null,
|
||||||
|
workingIV = null;
|
||||||
|
private boolean initialised = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* internal counter
|
||||||
|
*/
|
||||||
|
private int cW0, cW1, cW2;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initialise a Salsa20 cipher.
|
||||||
|
*
|
||||||
|
* @param forEncryption whether or not we are for encryption.
|
||||||
|
* @param params the parameters required to set up the cipher.
|
||||||
|
* @exception IllegalArgumentException if the params argument is
|
||||||
|
* inappropriate.
|
||||||
|
*/
|
||||||
|
public void init(
|
||||||
|
boolean forEncryption,
|
||||||
|
CipherParameters params)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Salsa20 encryption and decryption is completely
|
||||||
|
* symmetrical, so the 'forEncryption' is
|
||||||
|
* irrelevant. (Like 90% of stream ciphers)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!(params instanceof ParametersWithIV))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Salsa20 Init parameters must include an IV");
|
||||||
|
}
|
||||||
|
|
||||||
|
ParametersWithIV ivParams = (ParametersWithIV) params;
|
||||||
|
|
||||||
|
byte[] iv = ivParams.getIV();
|
||||||
|
|
||||||
|
if (iv == null || iv.length != 8)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Salsa20 requires exactly 8 bytes of IV");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(ivParams.getParameters() instanceof KeyParameter))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Salsa20 Init parameters must include a key");
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyParameter key = (KeyParameter) ivParams.getParameters();
|
||||||
|
|
||||||
|
workingKey = key.getKey();
|
||||||
|
workingIV = iv;
|
||||||
|
|
||||||
|
setKey(workingKey, workingIV);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAlgorithmName()
|
||||||
|
{
|
||||||
|
return "Salsa20";
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte returnByte(byte in)
|
||||||
|
{
|
||||||
|
if (limitExceeded())
|
||||||
|
{
|
||||||
|
throw new MaxBytesExceededException("2^70 byte limit per IV; Change IV");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
salsa20WordToByte(engineState, keyStream);
|
||||||
|
engineState[8]++;
|
||||||
|
if (engineState[8] == 0)
|
||||||
|
{
|
||||||
|
engineState[9]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte out = (byte)(keyStream[index]^in);
|
||||||
|
index = (index + 1) & 63;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processBytes(
|
||||||
|
byte[] in,
|
||||||
|
int inOff,
|
||||||
|
int len,
|
||||||
|
byte[] out,
|
||||||
|
int outOff)
|
||||||
|
{
|
||||||
|
if (!initialised)
|
||||||
|
{
|
||||||
|
throw new IllegalStateException(getAlgorithmName()+" not initialised");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((inOff + len) > in.length)
|
||||||
|
{
|
||||||
|
throw new DataLengthException("input buffer too short");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((outOff + len) > out.length)
|
||||||
|
{
|
||||||
|
throw new DataLengthException("output buffer too short");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limitExceeded(len))
|
||||||
|
{
|
||||||
|
throw new MaxBytesExceededException("2^70 byte limit per IV would be exceeded; Change IV");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
salsa20WordToByte(engineState, keyStream);
|
||||||
|
engineState[8]++;
|
||||||
|
if (engineState[8] == 0)
|
||||||
|
{
|
||||||
|
engineState[9]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out[i+outOff] = (byte)(keyStream[index]^in[i+inOff]);
|
||||||
|
index = (index + 1) & 63;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset()
|
||||||
|
{
|
||||||
|
setKey(workingKey, workingIV);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private implementation
|
||||||
|
|
||||||
|
private void setKey(byte[] keyBytes, byte[] ivBytes)
|
||||||
|
{
|
||||||
|
workingKey = keyBytes;
|
||||||
|
workingIV = ivBytes;
|
||||||
|
|
||||||
|
index = 0;
|
||||||
|
resetCounter();
|
||||||
|
int offset = 0;
|
||||||
|
byte[] constants;
|
||||||
|
|
||||||
|
// Key
|
||||||
|
engineState[1] = byteToIntLittle(workingKey, 0);
|
||||||
|
engineState[2] = byteToIntLittle(workingKey, 4);
|
||||||
|
engineState[3] = byteToIntLittle(workingKey, 8);
|
||||||
|
engineState[4] = byteToIntLittle(workingKey, 12);
|
||||||
|
|
||||||
|
if (workingKey.length == 32)
|
||||||
|
{
|
||||||
|
constants = sigma;
|
||||||
|
offset = 16;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
constants = tau;
|
||||||
|
}
|
||||||
|
|
||||||
|
engineState[11] = byteToIntLittle(workingKey, offset);
|
||||||
|
engineState[12] = byteToIntLittle(workingKey, offset+4);
|
||||||
|
engineState[13] = byteToIntLittle(workingKey, offset+8);
|
||||||
|
engineState[14] = byteToIntLittle(workingKey, offset+12);
|
||||||
|
engineState[0 ] = byteToIntLittle(constants, 0);
|
||||||
|
engineState[5 ] = byteToIntLittle(constants, 4);
|
||||||
|
engineState[10] = byteToIntLittle(constants, 8);
|
||||||
|
engineState[15] = byteToIntLittle(constants, 12);
|
||||||
|
|
||||||
|
// IV
|
||||||
|
engineState[6] = byteToIntLittle(workingIV, 0);
|
||||||
|
engineState[7] = byteToIntLittle(workingIV, 4);
|
||||||
|
engineState[8] = engineState[9] = 0;
|
||||||
|
|
||||||
|
initialised = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Salsa20 function
|
||||||
|
*
|
||||||
|
* @param input input data
|
||||||
|
*
|
||||||
|
* @return keystream
|
||||||
|
*/
|
||||||
|
private void salsa20WordToByte(int[] input, byte[] output)
|
||||||
|
{
|
||||||
|
System.arraycopy(input, 0, x, 0, input.length);
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
x[ 4] ^= rotl((x[ 0]+x[12]), 7);
|
||||||
|
x[ 8] ^= rotl((x[ 4]+x[ 0]), 9);
|
||||||
|
x[12] ^= rotl((x[ 8]+x[ 4]),13);
|
||||||
|
x[ 0] ^= rotl((x[12]+x[ 8]),18);
|
||||||
|
x[ 9] ^= rotl((x[ 5]+x[ 1]), 7);
|
||||||
|
x[13] ^= rotl((x[ 9]+x[ 5]), 9);
|
||||||
|
x[ 1] ^= rotl((x[13]+x[ 9]),13);
|
||||||
|
x[ 5] ^= rotl((x[ 1]+x[13]),18);
|
||||||
|
x[14] ^= rotl((x[10]+x[ 6]), 7);
|
||||||
|
x[ 2] ^= rotl((x[14]+x[10]), 9);
|
||||||
|
x[ 6] ^= rotl((x[ 2]+x[14]),13);
|
||||||
|
x[10] ^= rotl((x[ 6]+x[ 2]),18);
|
||||||
|
x[ 3] ^= rotl((x[15]+x[11]), 7);
|
||||||
|
x[ 7] ^= rotl((x[ 3]+x[15]), 9);
|
||||||
|
x[11] ^= rotl((x[ 7]+x[ 3]),13);
|
||||||
|
x[15] ^= rotl((x[11]+x[ 7]),18);
|
||||||
|
x[ 1] ^= rotl((x[ 0]+x[ 3]), 7);
|
||||||
|
x[ 2] ^= rotl((x[ 1]+x[ 0]), 9);
|
||||||
|
x[ 3] ^= rotl((x[ 2]+x[ 1]),13);
|
||||||
|
x[ 0] ^= rotl((x[ 3]+x[ 2]),18);
|
||||||
|
x[ 6] ^= rotl((x[ 5]+x[ 4]), 7);
|
||||||
|
x[ 7] ^= rotl((x[ 6]+x[ 5]), 9);
|
||||||
|
x[ 4] ^= rotl((x[ 7]+x[ 6]),13);
|
||||||
|
x[ 5] ^= rotl((x[ 4]+x[ 7]),18);
|
||||||
|
x[11] ^= rotl((x[10]+x[ 9]), 7);
|
||||||
|
x[ 8] ^= rotl((x[11]+x[10]), 9);
|
||||||
|
x[ 9] ^= rotl((x[ 8]+x[11]),13);
|
||||||
|
x[10] ^= rotl((x[ 9]+x[ 8]),18);
|
||||||
|
x[12] ^= rotl((x[15]+x[14]), 7);
|
||||||
|
x[13] ^= rotl((x[12]+x[15]), 9);
|
||||||
|
x[14] ^= rotl((x[13]+x[12]),13);
|
||||||
|
x[15] ^= rotl((x[14]+x[13]),18);
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
for (int i = 0; i < stateSize; i++)
|
||||||
|
{
|
||||||
|
intToByteLittle(x[i] + input[i], output, offset);
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = stateSize; i < x.length; i++)
|
||||||
|
{
|
||||||
|
intToByteLittle(x[i], output, offset);
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 32 bit word to 4 byte array in little endian order
|
||||||
|
*
|
||||||
|
* @param x value to 'unpack'
|
||||||
|
*
|
||||||
|
* @return value of x expressed as a byte[] array in little endian order
|
||||||
|
*/
|
||||||
|
private byte[] intToByteLittle(int x, byte[] out, int off)
|
||||||
|
{
|
||||||
|
out[off] = (byte)x;
|
||||||
|
out[off + 1] = (byte)(x >>> 8);
|
||||||
|
out[off + 2] = (byte)(x >>> 16);
|
||||||
|
out[off + 3] = (byte)(x >>> 24);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate left
|
||||||
|
*
|
||||||
|
* @param x value to rotate
|
||||||
|
* @param y amount to rotate x
|
||||||
|
*
|
||||||
|
* @return rotated x
|
||||||
|
*/
|
||||||
|
private int rotl(int x, int y)
|
||||||
|
{
|
||||||
|
return (x << y) | (x >>> -y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pack byte[] array into an int in little endian order
|
||||||
|
*
|
||||||
|
* @param x byte array to 'pack'
|
||||||
|
* @param offset only x[offset]..x[offset+3] will be packed
|
||||||
|
*
|
||||||
|
* @return x[offset]..x[offset+3] 'packed' into an int in little-endian order
|
||||||
|
*/
|
||||||
|
private int byteToIntLittle(byte[] x, int offset)
|
||||||
|
{
|
||||||
|
return ((x[offset] & 255)) |
|
||||||
|
((x[offset + 1] & 255) << 8) |
|
||||||
|
((x[offset + 2] & 255) << 16) |
|
||||||
|
(x[offset + 3] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetCounter()
|
||||||
|
{
|
||||||
|
cW0 = 0;
|
||||||
|
cW1 = 0;
|
||||||
|
cW2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean limitExceeded()
|
||||||
|
{
|
||||||
|
cW0++;
|
||||||
|
if (cW0 == 0)
|
||||||
|
{
|
||||||
|
cW1++;
|
||||||
|
if (cW1 == 0)
|
||||||
|
{
|
||||||
|
cW2++;
|
||||||
|
return (cW2 & 0x20) != 0; // 2^(32 + 32 + 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this relies on the fact len will always be positive.
|
||||||
|
*/
|
||||||
|
private boolean limitExceeded(int len)
|
||||||
|
{
|
||||||
|
if (cW0 >= 0)
|
||||||
|
{
|
||||||
|
cW0 += len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cW0 += len;
|
||||||
|
if (cW0 >= 0)
|
||||||
|
{
|
||||||
|
cW1++;
|
||||||
|
if (cW1 == 0)
|
||||||
|
{
|
||||||
|
cW2++;
|
||||||
|
return (cW2 & 0x20) != 0; // 2^(32 + 32 + 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/org/bouncycastle/crypto/params/KeyParameter.java
Normal file
30
src/org/bouncycastle/crypto/params/KeyParameter.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package org.bouncycastle.crypto.params;
|
||||||
|
|
||||||
|
import org.bouncycastle.crypto.CipherParameters;
|
||||||
|
|
||||||
|
public class KeyParameter
|
||||||
|
implements CipherParameters
|
||||||
|
{
|
||||||
|
private byte[] key;
|
||||||
|
|
||||||
|
public KeyParameter(
|
||||||
|
byte[] key)
|
||||||
|
{
|
||||||
|
this(key, 0, key.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyParameter(
|
||||||
|
byte[] key,
|
||||||
|
int keyOff,
|
||||||
|
int keyLen)
|
||||||
|
{
|
||||||
|
this.key = new byte[keyLen];
|
||||||
|
|
||||||
|
System.arraycopy(key, keyOff, this.key, 0, keyLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getKey()
|
||||||
|
{
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/org/bouncycastle/crypto/params/ParametersWithIV.java
Normal file
39
src/org/bouncycastle/crypto/params/ParametersWithIV.java
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package org.bouncycastle.crypto.params;
|
||||||
|
|
||||||
|
import org.bouncycastle.crypto.CipherParameters;
|
||||||
|
|
||||||
|
public class ParametersWithIV
|
||||||
|
implements CipherParameters
|
||||||
|
{
|
||||||
|
private byte[] iv;
|
||||||
|
private CipherParameters parameters;
|
||||||
|
|
||||||
|
public ParametersWithIV(
|
||||||
|
CipherParameters parameters,
|
||||||
|
byte[] iv)
|
||||||
|
{
|
||||||
|
this(parameters, iv, 0, iv.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParametersWithIV(
|
||||||
|
CipherParameters parameters,
|
||||||
|
byte[] iv,
|
||||||
|
int ivOff,
|
||||||
|
int ivLen)
|
||||||
|
{
|
||||||
|
this.iv = new byte[ivLen];
|
||||||
|
this.parameters = parameters;
|
||||||
|
|
||||||
|
System.arraycopy(iv, ivOff, this.iv, 0, ivLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getIV()
|
||||||
|
{
|
||||||
|
return iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CipherParameters getParameters()
|
||||||
|
{
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
247
src/org/bouncycastle/util/Strings.java
Normal file
247
src/org/bouncycastle/util/Strings.java
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
package org.bouncycastle.util;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
public final class Strings
|
||||||
|
{
|
||||||
|
public static String fromUTF8ByteArray(byte[] bytes)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int length = 0;
|
||||||
|
|
||||||
|
while (i < bytes.length)
|
||||||
|
{
|
||||||
|
length++;
|
||||||
|
if ((bytes[i] & 0xf0) == 0xf0)
|
||||||
|
{
|
||||||
|
// surrogate pair
|
||||||
|
length++;
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
else if ((bytes[i] & 0xe0) == 0xe0)
|
||||||
|
{
|
||||||
|
i += 3;
|
||||||
|
}
|
||||||
|
else if ((bytes[i] & 0xc0) == 0xc0)
|
||||||
|
{
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char[] cs = new char[length];
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
length = 0;
|
||||||
|
|
||||||
|
while (i < bytes.length)
|
||||||
|
{
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
if ((bytes[i] & 0xf0) == 0xf0)
|
||||||
|
{
|
||||||
|
int codePoint = ((bytes[i] & 0x03) << 18) | ((bytes[i+1] & 0x3F) << 12) | ((bytes[i+2] & 0x3F) << 6) | (bytes[i+3] & 0x3F);
|
||||||
|
int U = codePoint - 0x10000;
|
||||||
|
char W1 = (char)(0xD800 | (U >> 10));
|
||||||
|
char W2 = (char)(0xDC00 | (U & 0x3FF));
|
||||||
|
cs[length++] = W1;
|
||||||
|
ch = W2;
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
else if ((bytes[i] & 0xe0) == 0xe0)
|
||||||
|
{
|
||||||
|
ch = (char)(((bytes[i] & 0x0f) << 12)
|
||||||
|
| ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f));
|
||||||
|
i += 3;
|
||||||
|
}
|
||||||
|
else if ((bytes[i] & 0xd0) == 0xd0)
|
||||||
|
{
|
||||||
|
ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f));
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else if ((bytes[i] & 0xc0) == 0xc0)
|
||||||
|
{
|
||||||
|
ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f));
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ch = (char)(bytes[i] & 0xff);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cs[length++] = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new String(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toUTF8ByteArray(String string)
|
||||||
|
{
|
||||||
|
return toUTF8ByteArray(string.toCharArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toUTF8ByteArray(char[] string)
|
||||||
|
{
|
||||||
|
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
|
||||||
|
char[] c = string;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (i < c.length)
|
||||||
|
{
|
||||||
|
char ch = c[i];
|
||||||
|
|
||||||
|
if (ch < 0x0080)
|
||||||
|
{
|
||||||
|
bOut.write(ch);
|
||||||
|
}
|
||||||
|
else if (ch < 0x0800)
|
||||||
|
{
|
||||||
|
bOut.write(0xc0 | (ch >> 6));
|
||||||
|
bOut.write(0x80 | (ch & 0x3f));
|
||||||
|
}
|
||||||
|
// surrogate pair
|
||||||
|
else if (ch >= 0xD800 && ch <= 0xDFFF)
|
||||||
|
{
|
||||||
|
// in error - can only happen, if the Java String class has a
|
||||||
|
// bug.
|
||||||
|
if (i + 1 >= c.length)
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("invalid UTF-16 codepoint");
|
||||||
|
}
|
||||||
|
char W1 = ch;
|
||||||
|
ch = c[++i];
|
||||||
|
char W2 = ch;
|
||||||
|
// in error - can only happen, if the Java String class has a
|
||||||
|
// bug.
|
||||||
|
if (W1 > 0xDBFF)
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("invalid UTF-16 codepoint");
|
||||||
|
}
|
||||||
|
int codePoint = (((W1 & 0x03FF) << 10) | (W2 & 0x03FF)) + 0x10000;
|
||||||
|
bOut.write(0xf0 | (codePoint >> 18));
|
||||||
|
bOut.write(0x80 | ((codePoint >> 12) & 0x3F));
|
||||||
|
bOut.write(0x80 | ((codePoint >> 6) & 0x3F));
|
||||||
|
bOut.write(0x80 | (codePoint & 0x3F));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bOut.write(0xe0 | (ch >> 12));
|
||||||
|
bOut.write(0x80 | ((ch >> 6) & 0x3F));
|
||||||
|
bOut.write(0x80 | (ch & 0x3F));
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bOut.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A locale independent version of toUpperCase.
|
||||||
|
*
|
||||||
|
* @param string input to be converted
|
||||||
|
* @return a US Ascii uppercase version
|
||||||
|
*/
|
||||||
|
public static String toUpperCase(String string)
|
||||||
|
{
|
||||||
|
boolean changed = false;
|
||||||
|
char[] chars = string.toCharArray();
|
||||||
|
|
||||||
|
for (int i = 0; i != chars.length; i++)
|
||||||
|
{
|
||||||
|
char ch = chars[i];
|
||||||
|
if ('a' <= ch && 'z' >= ch)
|
||||||
|
{
|
||||||
|
changed = true;
|
||||||
|
chars[i] = (char)(ch - 'a' + 'A');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
return new String(chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A locale independent version of toLowerCase.
|
||||||
|
*
|
||||||
|
* @param string input to be converted
|
||||||
|
* @return a US ASCII lowercase version
|
||||||
|
*/
|
||||||
|
public static String toLowerCase(String string)
|
||||||
|
{
|
||||||
|
boolean changed = false;
|
||||||
|
char[] chars = string.toCharArray();
|
||||||
|
|
||||||
|
for (int i = 0; i != chars.length; i++)
|
||||||
|
{
|
||||||
|
char ch = chars[i];
|
||||||
|
if ('A' <= ch && 'Z' >= ch)
|
||||||
|
{
|
||||||
|
changed = true;
|
||||||
|
chars[i] = (char)(ch - 'A' + 'a');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
return new String(chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toByteArray(String string)
|
||||||
|
{
|
||||||
|
byte[] bytes = new byte[string.length()];
|
||||||
|
|
||||||
|
for (int i = 0; i != bytes.length; i++)
|
||||||
|
{
|
||||||
|
char ch = string.charAt(i);
|
||||||
|
|
||||||
|
bytes[i] = (byte)ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static String[] split(String input, char delimiter)
|
||||||
|
{
|
||||||
|
Vector v = new Vector();
|
||||||
|
boolean moreTokens = true;
|
||||||
|
String subString;
|
||||||
|
|
||||||
|
while (moreTokens)
|
||||||
|
{
|
||||||
|
int tokenLocation = input.indexOf(delimiter);
|
||||||
|
if (tokenLocation > 0)
|
||||||
|
{
|
||||||
|
subString = input.substring(0, tokenLocation);
|
||||||
|
v.addElement(subString);
|
||||||
|
input = input.substring(tokenLocation + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
moreTokens = false;
|
||||||
|
v.addElement(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] res = new String[v.size()];
|
||||||
|
|
||||||
|
for (int i = 0; i != res.length; i++)
|
||||||
|
{
|
||||||
|
res[i] = (String)v.elementAt(i);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,10 +23,10 @@ import static org.junit.Assert.assertArrayEquals;
|
|||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
|
||||||
import com.keepassdroid.database.PwDate;
|
import com.keepassdroid.database.PwDate;
|
||||||
import com.keepassdroid.stream.LEDataInputStream;
|
import com.keepassdroid.stream.LEDataInputStream;
|
||||||
import com.keepassdroid.stream.LEDataOutputStream;
|
import com.keepassdroid.stream.LEDataOutputStream;
|
||||||
@@ -180,4 +180,15 @@ public class TypesTest extends TestCase {
|
|||||||
assertEquals("Minute mismatch: ", 4, actual.get(Calendar.MINUTE));
|
assertEquals("Minute mismatch: ", 4, actual.get(Calendar.MINUTE));
|
||||||
assertEquals("Second mismatch: ", 5, actual.get(Calendar.SECOND));
|
assertEquals("Second mismatch: ", 5, actual.get(Calendar.SECOND));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testUUID() {
|
||||||
|
Random rnd = new Random();
|
||||||
|
byte[] bUUID = new byte[16];
|
||||||
|
rnd.nextBytes(bUUID);
|
||||||
|
|
||||||
|
UUID uuid = Types.bytestoUUID(bUUID);
|
||||||
|
byte[] eUUID = Types.UUIDtoBytes(uuid);
|
||||||
|
|
||||||
|
assertArrayEquals("UUID match failed", bUUID, eUUID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,7 @@ import android.content.Context;
|
|||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.Database;
|
||||||
|
import com.keepassdroid.database.PwDatabase;
|
||||||
import com.keepassdroid.database.PwDatabaseV3;
|
import com.keepassdroid.database.PwDatabaseV3;
|
||||||
import com.keepassdroid.database.PwEntry;
|
import com.keepassdroid.database.PwEntry;
|
||||||
import com.keepassdroid.database.PwGroup;
|
import com.keepassdroid.database.PwGroup;
|
||||||
@@ -100,7 +101,7 @@ public class DeleteEntry extends AndroidTestCase {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PwGroup getGroup(PwDatabaseV3 pm, String name) {
|
private PwGroup getGroup(PwDatabase pm, String name) {
|
||||||
Vector<PwGroup> groups = pm.getGroups();
|
Vector<PwGroup> groups = pm.getGroups();
|
||||||
for ( int i = 0; i < groups.size(); i++ ) {
|
for ( int i = 0; i < groups.size(); i++ ) {
|
||||||
PwGroup group = groups.get(i);
|
PwGroup group = groups.get(i);
|
||||||
|
|||||||
Reference in New Issue
Block a user