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.save.PwDbOutput;
|
||||
import com.keepassdroid.search.SearchDbHelper;
|
||||
import com.keepassdroid.utils.Types;
|
||||
|
||||
/**
|
||||
* @author bpellin
|
||||
@@ -206,7 +205,7 @@ public class Database {
|
||||
|
||||
for (int i = 0; i < childEntries.size(); 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++ ) {
|
||||
|
||||
@@ -77,7 +77,7 @@ public class EntryActivity extends LockCloseActivity {
|
||||
public static void Launch(Activity act, PwEntry pw, int pos) {
|
||||
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);
|
||||
|
||||
act.startActivityForResult(i,0);
|
||||
|
||||
@@ -70,7 +70,7 @@ public class EntryEditActivity extends LockCloseActivity {
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -155,7 +155,7 @@ public class EntryEditActivity extends LockCloseActivity {
|
||||
newEntry.parent = mEntry.parent;
|
||||
newEntry.tCreation = mEntry.tCreation;
|
||||
newEntry.tExpire = mEntry.tExpire;
|
||||
newEntry.uuid = mEntry.uuid;
|
||||
newEntry.setUUID(mEntry.getUUID());
|
||||
|
||||
Date now = Calendar.getInstance().getTime();
|
||||
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;
|
||||
|
||||
public class CrsAlgorithm {
|
||||
public static final int Null = 0;
|
||||
public static final int ArcFourVariant = 1;
|
||||
public static final int Salsa20 = 2;
|
||||
public enum CrsAlgorithm {
|
||||
|
||||
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[] finalKey;
|
||||
public String name = "KeePass database";
|
||||
|
||||
public void makeFinalKey(byte[] masterSeed, byte[] masterSeed2, int numRounds) throws IOException {
|
||||
|
||||
|
||||
@@ -48,9 +48,6 @@ public class PwDatabaseV3 extends PwDatabase {
|
||||
// Constants
|
||||
// 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
|
||||
public PwEntry metaInfo;
|
||||
|
||||
@@ -153,7 +150,7 @@ public class PwDatabaseV3 extends PwDatabase {
|
||||
groups.addElement(group);
|
||||
}
|
||||
|
||||
public void addEntry(PwEntryV3 entry)
|
||||
public void addEntry(PwEntry entry)
|
||||
{
|
||||
entries.addElement(entry);
|
||||
}
|
||||
|
||||
@@ -20,15 +20,17 @@
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
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.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.InvalidKeyFileException;
|
||||
|
||||
@@ -38,10 +40,48 @@ public class PwDatabaseV4 extends PwDatabase {
|
||||
public UUID dataCipher;
|
||||
public PwCompressionAlgorithm compressionAlgorithm;
|
||||
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 PwGroupV4 rootGroup;
|
||||
public PwGroupV4 rootGroup;
|
||||
|
||||
@Override
|
||||
public byte[] getMasterKey(String key, String keyFileName)
|
||||
@@ -83,19 +123,8 @@ public class PwDatabaseV4 extends PwDatabase {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void parseDB(Document d) throws InconsistentDBException {
|
||||
doc = d;
|
||||
|
||||
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);
|
||||
public void parseDB(InputStream in) throws InconsistentDBException {
|
||||
//TODO Implement Me
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -52,7 +52,7 @@ public class PwDbHeaderV4 extends PwDbHeader {
|
||||
private PwDatabaseV4 db;
|
||||
public byte[] protectedStreamKey;
|
||||
public byte[] streamStartBytes;
|
||||
public int innerRandomStream;
|
||||
public CrsAlgorithm innerRandomStream;
|
||||
|
||||
public PwDbHeaderV4(PwDatabaseV4 d) {
|
||||
db = d;
|
||||
@@ -190,11 +190,11 @@ public class PwDbHeaderV4 extends PwDbHeader {
|
||||
}
|
||||
|
||||
int id = LEDataInputStream.readInt(streamID, 0);
|
||||
if ( id < 0 || id >= CrsAlgorithm.Count ) {
|
||||
if ( id < 0 || id >= CrsAlgorithm.count ) {
|
||||
throw new IOException("Invalid stream id.");
|
||||
}
|
||||
|
||||
innerRandomStream = id;
|
||||
innerRandomStream = CrsAlgorithm.fromId(id);
|
||||
}
|
||||
|
||||
/** 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;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class PwEntry implements Cloneable {
|
||||
|
||||
public byte uuid[] = new byte[16];
|
||||
//public byte uuid[] = new byte[16];
|
||||
public String title;
|
||||
public String url;
|
||||
public String additional;
|
||||
public int imageId;
|
||||
|
||||
public PwEntry() {
|
||||
|
||||
@@ -42,7 +44,7 @@ public abstract class PwEntry implements Cloneable {
|
||||
throw new RuntimeException("Clone should be supported");
|
||||
}
|
||||
|
||||
System.arraycopy(uuid, 0, newEntry.uuid, 0, uuid.length);
|
||||
newEntry.setUUID(getUUID());
|
||||
newEntry.title = title;
|
||||
newEntry.url = url;
|
||||
newEntry.additional = additional;
|
||||
@@ -51,14 +53,16 @@ public abstract class PwEntry implements Cloneable {
|
||||
}
|
||||
|
||||
public void assign(PwEntry source) {
|
||||
System.arraycopy(source.uuid, 0, uuid, 0, source.uuid.length);
|
||||
setUUID(source.getUUID());
|
||||
title = source.title;
|
||||
url = source.url;
|
||||
additional = source.additional;
|
||||
}
|
||||
|
||||
public abstract void stampLastAccess();
|
||||
|
||||
|
||||
public abstract UUID getUUID();
|
||||
public abstract void setUUID(UUID u);
|
||||
public abstract String getUsername();
|
||||
public abstract String getPassword();
|
||||
public abstract Date getCreate();
|
||||
|
||||
@@ -29,11 +29,13 @@ import java.lang.ref.WeakReference;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.keepassdroid.Database;
|
||||
import com.keepassdroid.utils.Types;
|
||||
|
||||
|
||||
/**
|
||||
@@ -71,11 +73,9 @@ public class PwEntryV3 extends PwEntry {
|
||||
|
||||
|
||||
public int groupId;
|
||||
public int imageId;
|
||||
|
||||
public String username;
|
||||
|
||||
public String username;
|
||||
private byte[] password;
|
||||
private byte[] uuid;
|
||||
|
||||
public PwDate tCreation;
|
||||
public PwDate tLastMod;
|
||||
@@ -315,11 +315,6 @@ public class PwEntryV3 extends PwEntry {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getAccess() {
|
||||
return tLastAccess.getJDate();
|
||||
@@ -345,4 +340,19 @@ public class PwEntryV3 extends PwEntry {
|
||||
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;
|
||||
|
||||
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 org.w3c.dom.Node;
|
||||
|
||||
public class PwEntryV4 extends PwEntry {
|
||||
private Node node;
|
||||
public class PwEntryV4 extends PwEntry implements ITimeLogger {
|
||||
private static final String STR_USERNAME = "UserName";
|
||||
|
||||
public PwEntryV4(Node n) {
|
||||
node = n;
|
||||
public PwGroupV4 parent;
|
||||
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
|
||||
@@ -43,7 +78,7 @@ public class PwEntryV4 extends PwEntry {
|
||||
}
|
||||
|
||||
private void assign(PwEntryV4 source) {
|
||||
node = source.node;
|
||||
// TODO: Implement me
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,50 +95,131 @@ public class PwEntryV4 extends PwEntry {
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
// TODO Implement me
|
||||
return null;
|
||||
return getString(STR_USERNAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
// TODO Implement me
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getAccess() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getCreate() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getExpire() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getMod() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return parentGroupLastMod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayTitle() {
|
||||
// TOOD: Add special TAN handling for V4?
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroupV4 getParent() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return parent;
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Vector;
|
||||
|
||||
public abstract class PwGroup {
|
||||
@@ -30,5 +31,7 @@ public abstract class PwGroup {
|
||||
public abstract PwGroupId getId();
|
||||
|
||||
public abstract String getName();
|
||||
|
||||
public abstract Date getLastMod();
|
||||
|
||||
}
|
||||
|
||||
@@ -118,4 +118,9 @@ public class PwGroupV3 extends PwGroup {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastMod() {
|
||||
return tLastMod.getJDate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,45 +19,70 @@
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
public class PwGroupV4 extends PwGroup implements ITimeLogger {
|
||||
|
||||
import com.keepassdroid.database.exception.InconsistentDBException;
|
||||
|
||||
public class PwGroupV4 extends PwGroup {
|
||||
|
||||
private Node node;
|
||||
private PwGroup parent;
|
||||
|
||||
public PwGroupV4(Node n) throws InconsistentDBException {
|
||||
this(n, null);
|
||||
}
|
||||
|
||||
public PwGroupV4(Node n, PwGroup p) throws InconsistentDBException {
|
||||
node = n;
|
||||
parent = p;
|
||||
buildTree();
|
||||
}
|
||||
|
||||
private void buildTree() throws InconsistentDBException {
|
||||
NodeList children = node.getChildNodes();
|
||||
public PwGroupV4 parent = null;
|
||||
public UUID uuid;
|
||||
public String name;
|
||||
public String notes;
|
||||
public int iconId;
|
||||
public UUID customIconUuid;
|
||||
public boolean isExpanded;
|
||||
public String defaultAutoTypeSequence;
|
||||
public Boolean enableAutoType;
|
||||
public Boolean enableSearching;
|
||||
public UUID lastTopVisibleEntry;
|
||||
private Date parentGroupLastMod;
|
||||
private Date creation;
|
||||
private Date lastMod;
|
||||
private Date lastAccess;
|
||||
private Date expireDate;
|
||||
private boolean expires = false;
|
||||
private long usageCount = 0;
|
||||
|
||||
|
||||
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
|
||||
public PwGroup getParent() {
|
||||
return parent;
|
||||
@@ -87,18 +112,88 @@ public class PwGroupV4 extends PwGroup {
|
||||
|
||||
@Override
|
||||
public PwGroupId getId() {
|
||||
return new PwGroupIdV4(getUUID());
|
||||
}
|
||||
|
||||
public UUID getUUID() {
|
||||
// TODO: Get UUID from document
|
||||
return null;
|
||||
return new PwGroupIdV4(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return name;
|
||||
}
|
||||
|
||||
@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) {
|
||||
if ( entry instanceof PwEntryV3 ) {
|
||||
return new AddEntryV3(db, (PwEntryV3) entry, finish);
|
||||
return new AddEntryV3(db, (PwEntry) entry, finish);
|
||||
} else {
|
||||
// TODO: Implement me
|
||||
throw new RuntimeException("Not implemented yet.");
|
||||
@@ -76,7 +76,7 @@ public abstract class AddEntry extends RunnableOnFinish {
|
||||
mDb.dirty.put(parent, new WeakReference<PwGroup>(parent));
|
||||
|
||||
// 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 ) {
|
||||
// Add entry to search index
|
||||
|
||||
@@ -20,15 +20,15 @@
|
||||
package com.keepassdroid.database.edit;
|
||||
|
||||
import com.keepassdroid.Database;
|
||||
import com.keepassdroid.database.PwDatabaseV3;
|
||||
import com.keepassdroid.database.PwEntryV3;
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwGroupV3;
|
||||
|
||||
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);
|
||||
|
||||
mEntry = entry;
|
||||
@@ -36,13 +36,13 @@ public class AddEntryV3 extends AddEntry {
|
||||
|
||||
|
||||
public void addEntry() {
|
||||
PwGroupV3 parent = mEntry.getParent();
|
||||
PwGroupV3 parent = (PwGroupV3) mEntry.getParent();
|
||||
|
||||
// Add entry to group
|
||||
parent.childEntries.add(mEntry);
|
||||
|
||||
// Add entry to PwDatabaseV3
|
||||
PwDatabaseV3 pm = (PwDatabaseV3) mDb.pm;
|
||||
PwDatabase pm = (PwDatabase) mDb.pm;
|
||||
pm.getEntries().add(mEntry);
|
||||
|
||||
// Sort entries
|
||||
|
||||
@@ -107,7 +107,7 @@ public class ImporterV3 extends Importer {
|
||||
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
|
||||
{
|
||||
PwDatabaseV3 newManager;
|
||||
@@ -375,7 +375,7 @@ public class ImporterV3 extends Importer {
|
||||
// Ignore field
|
||||
break;
|
||||
case 0x0001 :
|
||||
System.arraycopy(buf, offset, ent.uuid, 0, 16);
|
||||
ent.setUUID(Types.bytestoUUID(buf, offset));
|
||||
break;
|
||||
case 0x0002 :
|
||||
ent.groupId = LEDataInputStream.readInt(buf, offset);
|
||||
|
||||
@@ -19,30 +19,41 @@
|
||||
*/
|
||||
package com.keepassdroid.database.load;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Stack;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
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.xml.sax.SAXException;
|
||||
import org.bouncycastle.crypto.StreamCipher;
|
||||
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.crypto.CipherFactory;
|
||||
import com.keepassdroid.crypto.PwStreamCipherFactory;
|
||||
import com.keepassdroid.database.ITimeLogger;
|
||||
import com.keepassdroid.database.PwCompressionAlgorithm;
|
||||
import com.keepassdroid.database.PwCustomIcon;
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
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.InvalidDBVersionException;
|
||||
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.HashedBlockInputStream;
|
||||
import com.keepassdroid.stream.LEDataInputStream;
|
||||
import com.keepassdroid.utils.Types;
|
||||
|
||||
public class ImporterV4 extends Importer {
|
||||
|
||||
private StreamCipher randomStream;
|
||||
private PwDatabaseV4 db;
|
||||
|
||||
@Override
|
||||
public PwDatabaseV4 openDatabase(InputStream inStream, String password,
|
||||
@@ -67,7 +82,7 @@ public class ImporterV4 extends Importer {
|
||||
InvalidKeyFileException, InvalidPasswordException,
|
||||
InvalidDBSignatureException, InvalidDBVersionException {
|
||||
|
||||
PwDatabaseV4 db = new PwDatabaseV4();
|
||||
db = new PwDatabaseV4();
|
||||
|
||||
PwDbHeaderV4 header = new PwDbHeaderV4(db);
|
||||
|
||||
@@ -112,6 +127,26 @@ public class ImporterV4 extends Importer {
|
||||
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
|
||||
BufferedInputStream bis2 = new BufferedInputStream(decompressed);
|
||||
|
||||
@@ -124,18 +159,15 @@ public class ImporterV4 extends Importer {
|
||||
throw new IOException("Couldn't create document builder.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
Document doc;
|
||||
try {
|
||||
doc = docB.parse(bis2);
|
||||
} catch (SAXException e) {
|
||||
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");
|
||||
@@ -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
|
||||
mOS.write(UUID_FIELD_TYPE);
|
||||
mOS.write(UUID_FIELD_SIZE);
|
||||
mOS.write(mPE.uuid);
|
||||
mOS.write(Types.UUIDtoBytes(mPE.getUUID()));
|
||||
|
||||
// Group ID
|
||||
mOS.write(GROUPID_FIELD_TYPE);
|
||||
|
||||
@@ -32,10 +32,8 @@ import android.util.Log;
|
||||
|
||||
import com.keepassdroid.Database;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwEntryV3;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.PwGroupV3;
|
||||
import com.keepassdroid.utils.Types;
|
||||
|
||||
public class SearchDbHelper {
|
||||
private static final String DATABASE_NAME = "search";
|
||||
@@ -100,7 +98,7 @@ public class SearchDbHelper {
|
||||
private ContentValues buildNewEntryContent(PwEntry entry) {
|
||||
|
||||
ContentValues cv = new ContentValues();
|
||||
UUID uuid = Types.bytestoUUID(entry.uuid);
|
||||
UUID uuid = entry.getUUID();
|
||||
String uuidStr = uuid.toString();
|
||||
|
||||
cv.put(KEY_UUID, uuidStr);
|
||||
@@ -137,7 +135,7 @@ public class SearchDbHelper {
|
||||
}
|
||||
|
||||
public void deleteEntry(PwEntry entry) {
|
||||
UUID uuid = Types.bytestoUUID(entry.uuid);
|
||||
UUID uuid = entry.getUUID();
|
||||
String uuidStr = uuid.toString();
|
||||
|
||||
mDb.delete(SEARCH_TABLE, KEY_UUID + " = ?", new String[] {uuidStr});
|
||||
@@ -157,7 +155,7 @@ public class SearchDbHelper {
|
||||
String sUUID = cursor.getString(0);
|
||||
UUID uuid = UUID.fromString(sUUID);
|
||||
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);
|
||||
|
||||
cursor.moveToNext();
|
||||
|
||||
@@ -178,19 +178,31 @@ public class Types {
|
||||
}
|
||||
|
||||
public static UUID bytestoUUID(byte[] buf) {
|
||||
|
||||
long msb = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
msb = (msb << 8) | (buf[i] & 0xff);
|
||||
}
|
||||
|
||||
return bytestoUUID(buf, 0);
|
||||
}
|
||||
|
||||
public static UUID bytestoUUID(byte[] buf, int offset) {
|
||||
long lsb = 0;
|
||||
for (int i = 8; i < 16; i++) {
|
||||
lsb = (lsb << 8) | (buf[i] & 0xff);
|
||||
for (int i = 15; i >= 8; i--) {
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
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.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
|
||||
import com.keepassdroid.database.PwDate;
|
||||
import com.keepassdroid.stream.LEDataInputStream;
|
||||
import com.keepassdroid.stream.LEDataOutputStream;
|
||||
@@ -180,4 +180,15 @@ public class TypesTest extends TestCase {
|
||||
assertEquals("Minute mismatch: ", 4, actual.get(Calendar.MINUTE));
|
||||
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 com.keepassdroid.Database;
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwDatabaseV3;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
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();
|
||||
for ( int i = 0; i < groups.size(); i++ ) {
|
||||
PwGroup group = groups.get(i);
|
||||
|
||||
Reference in New Issue
Block a user