Support for outputing the database header.

This commit is contained in:
Brian Pellin
2009-04-30 21:32:54 -05:00
parent 0287413643
commit 76b68ef2c4
12 changed files with 592 additions and 254 deletions

View File

@@ -0,0 +1,51 @@
/*
* 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.android.keepass.keepasslib;
import java.io.IOException;
import java.io.OutputStream;
import org.phoneid.keepassj2me.PwDbHeader;
import org.phoneid.keepassj2me.Types;
public class PwDbHeaderOutput {
private PwDbHeader mHeader;
private OutputStream mOS;
public PwDbHeaderOutput(PwDbHeader header, OutputStream os) {
mHeader = header;
mOS = os;
}
public void output() throws IOException {
mOS.write(Types.writeInt(mHeader.signature1));
mOS.write(Types.writeInt(mHeader.signature2));
mOS.write(Types.writeInt(mHeader.flags));
mOS.write(Types.writeInt(mHeader.version));
mOS.write(mHeader.masterSeed);
mOS.write(mHeader.encryptionIV);
mOS.write(Types.writeInt(mHeader.numGroups));
mOS.write(Types.writeInt(mHeader.numEntries));
mOS.write(mHeader.contentsHash);
mOS.write(mHeader.masterSeed2);
mOS.write(Types.writeInt(mHeader.numKeyEncRounds));
}
}

View File

@@ -26,95 +26,7 @@ import org.phoneid.keepassj2me.PwEntry;
import org.phoneid.keepassj2me.Types; import org.phoneid.keepassj2me.Types;
public class PwEntryOutput { public class PwEntryOutput {
private OutputStream mOS; // Constants
private PwEntry mPE;
/** Output the PwGroup to the stream
* @param pe
* @param os
*/
public PwEntryOutput(PwEntry pe, OutputStream os) {
mPE = pe;
mOS = os;
}
//NOTE: Need be to careful about using ints. The actual type written to file is a unsigned int
public void output() throws IOException {
// UUID
mOS.write(UUID_FIELD_TYPE);
mOS.write(UUID_FIELD_SIZE);
mOS.write(mPE.uuid);
// Group ID
mOS.write(GROUPID_FIELD_TYPE);
mOS.write(LONG_FOUR);
mOS.write(Types.writeInt(mPE.groupId));
// Image ID
mOS.write(IMAGEID_FIELD_TYPE);
mOS.write(LONG_FOUR);
mOS.write(Types.writeInt(mPE.imageId));
// Title
//byte[] title = mPE.title.getBytes("UTF-8");
mOS.write(TITLE_FIELD_TYPE);
Types.writeCString(mPE.title, mOS);
// URL
mOS.write(URL_FIELD_TYPE);
Types.writeCString(mPE.url, mOS);
// Username
mOS.write(USERNAME_FIELD_TYPE);
Types.writeCString(mPE.username, mOS);
// Password
byte[] password = mPE.getPassword();
mOS.write(PASSWORD_FIELD_TYPE);
mOS.write(Types.writeInt(password.length+1));
mOS.write(password);
mOS.write(0);
// Additional
mOS.write(ADDITIONAL_FIELD_TYPE);
Types.writeCString(mPE.additional, mOS);
// Create date
mOS.write(CREATE_FIELD_TYPE);
mOS.write(DATE_FIELD_SIZE);
mOS.write(Types.writeTime(mPE.tCreation));
// Modification date
mOS.write(MOD_FIELD_TYPE);
mOS.write(DATE_FIELD_SIZE);
mOS.write(Types.writeTime(mPE.tLastMod));
// Access date
mOS.write(ACCESS_FIELD_TYPE);
mOS.write(DATE_FIELD_SIZE);
mOS.write(Types.writeTime(mPE.tLastAccess));
// Expiration date
mOS.write(EXPIRE_FIELD_TYPE);
mOS.write(DATE_FIELD_SIZE);
mOS.write(Types.writeTime(mPE.tExpire));
// Binary desc
mOS.write(BINARY_DESC_FIELD_TYPE);
Types.writeCString(mPE.binaryDesc, mOS);
// Binary data
byte[] data = mPE.getBinaryData();
mOS.write(BINARY_DATA_FIELD_TYPE);
mOS.write(Types.writeInt(data.length));
mOS.write(data);
// End
mOS.write(END_FIELD_TYPE);
mOS.write(ZERO_FIELD_SIZE);
}
public static final byte[] UUID_FIELD_TYPE = Types.writeShort(1); public static final byte[] UUID_FIELD_TYPE = Types.writeShort(1);
public static final byte[] GROUPID_FIELD_TYPE = Types.writeShort(2); public static final byte[] GROUPID_FIELD_TYPE = Types.writeShort(2);
public static final byte[] IMAGEID_FIELD_TYPE = Types.writeShort(3); public static final byte[] IMAGEID_FIELD_TYPE = Types.writeShort(3);
@@ -139,5 +51,109 @@ public class PwEntryOutput {
public static final byte[] ZERO_FIELD_SIZE = Types.writeInt(0); public static final byte[] ZERO_FIELD_SIZE = Types.writeInt(0);
public static final byte[] TEST = {0x33, 0x33, 0x33, 0x33}; public static final byte[] TEST = {0x33, 0x33, 0x33, 0x33};
private OutputStream mOS;
private PwEntry mPE;
private long outputBytes = 0;
/** Output the PwGroup to the stream
* @param pe
* @param os
*/
public PwEntryOutput(PwEntry pe, OutputStream os) {
mPE = pe;
mOS = os;
}
//NOTE: Need be to careful about using ints. The actual type written to file is a unsigned int
public void output() throws IOException {
outputBytes += 134; // Length of fixed size fields
// UUID
mOS.write(UUID_FIELD_TYPE);
mOS.write(UUID_FIELD_SIZE);
mOS.write(mPE.uuid);
// Group ID
mOS.write(GROUPID_FIELD_TYPE);
mOS.write(LONG_FOUR);
mOS.write(Types.writeInt(mPE.groupId));
// Image ID
mOS.write(IMAGEID_FIELD_TYPE);
mOS.write(LONG_FOUR);
mOS.write(Types.writeInt(mPE.imageId));
// Title
//byte[] title = mPE.title.getBytes("UTF-8");
mOS.write(TITLE_FIELD_TYPE);
int titleLen = Types.writeCString(mPE.title, mOS);
outputBytes += titleLen;
// URL
mOS.write(URL_FIELD_TYPE);
int urlLen = Types.writeCString(mPE.url, mOS);
outputBytes += urlLen;
// Username
mOS.write(USERNAME_FIELD_TYPE);
int userLen = Types.writeCString(mPE.username, mOS);
outputBytes += userLen;
// Password
byte[] password = mPE.getPassword();
mOS.write(PASSWORD_FIELD_TYPE);
mOS.write(Types.writeInt(password.length+1));
mOS.write(password);
mOS.write(0);
outputBytes += password.length + 1;
// Additional
mOS.write(ADDITIONAL_FIELD_TYPE);
int addlLen = Types.writeCString(mPE.additional, mOS);
outputBytes += addlLen;
// Create date
mOS.write(CREATE_FIELD_TYPE);
mOS.write(DATE_FIELD_SIZE);
mOS.write(Types.writeTime(mPE.tCreation));
// Modification date
mOS.write(MOD_FIELD_TYPE);
mOS.write(DATE_FIELD_SIZE);
mOS.write(Types.writeTime(mPE.tLastMod));
// Access date
mOS.write(ACCESS_FIELD_TYPE);
mOS.write(DATE_FIELD_SIZE);
mOS.write(Types.writeTime(mPE.tLastAccess));
// Expiration date
mOS.write(EXPIRE_FIELD_TYPE);
mOS.write(DATE_FIELD_SIZE);
mOS.write(Types.writeTime(mPE.tExpire));
// Binary desc
mOS.write(BINARY_DESC_FIELD_TYPE);
int descLen = Types.writeCString(mPE.binaryDesc, mOS);
outputBytes += descLen;
// Binary data
byte[] data = mPE.getBinaryData();
mOS.write(BINARY_DATA_FIELD_TYPE);
mOS.write(Types.writeInt(data.length));
mOS.write(data);
outputBytes += data.length;
// End
mOS.write(END_FIELD_TYPE);
mOS.write(ZERO_FIELD_SIZE);
}
/** Returns the number of bytes written by the stream
* @return Number of bytes written
*/
public long getLength() {
return outputBytes;
}
} }

View File

@@ -26,8 +26,28 @@ import org.phoneid.keepassj2me.PwGroup;
import org.phoneid.keepassj2me.Types; import org.phoneid.keepassj2me.Types;
public class PwGroupOutput { public class PwGroupOutput {
// Constants
public static final byte[] GROUPID_FIELD_TYPE = Types.writeShort(1);
public static final byte[] NAME_FIELD_TYPE = Types.writeShort(2);
public static final byte[] CREATE_FIELD_TYPE = Types.writeShort(3);
public static final byte[] MOD_FIELD_TYPE = Types.writeShort(4);
public static final byte[] ACCESS_FIELD_TYPE = Types.writeShort(5);
public static final byte[] EXPIRE_FIELD_TYPE = Types.writeShort(6);
public static final byte[] IMAGEID_FIELD_TYPE = Types.writeShort(7);
public static final byte[] LEVEL_FIELD_TYPE = Types.writeShort(8);
public static final byte[] FLAGS_FIELD_TYPE = Types.writeShort(9);
public static final byte[] END_FIELD_TYPE = Types.writeShort(0xFFFF);
public static final byte[] LONG_FOUR = Types.writeInt(4);
public static final byte[] GROUPID_FIELD_SIZE = LONG_FOUR;
public static final byte[] DATE_FIELD_SIZE = Types.writeInt(5);
public static final byte[] IMAGEID_FIELD_SIZE = LONG_FOUR;
public static final byte[] LEVEL_FIELD_SIZE = Types.writeInt(2);
public static final byte[] FLAGS_FIELD_SIZE = LONG_FOUR;
public static final byte[] ZERO_FIELD_SIZE = Types.writeInt(0);
private OutputStream mOS; private OutputStream mOS;
private PwGroup mPG; private PwGroup mPG;
private long outputBytes = 0;
/** Output the PwGroup to the stream /** Output the PwGroup to the stream
* @param pg * @param pg
@@ -38,9 +58,11 @@ public class PwGroupOutput {
mOS = os; mOS = os;
} }
//NOTE: Need be to careful about using ints. The actual type written to file is a unsigned int
public void output() throws IOException { public void output() throws IOException {
//NOTE: Need be to careful about using ints. The actual type written to file is a unsigned int, but most values can't be greater than 2^31, so it probably doesn't matter.
outputBytes += 94; // Length of fixed size fields
// Group ID // Group ID
mOS.write(GROUPID_FIELD_TYPE); mOS.write(GROUPID_FIELD_TYPE);
mOS.write(GROUPID_FIELD_SIZE); mOS.write(GROUPID_FIELD_SIZE);
@@ -48,8 +70,9 @@ public class PwGroupOutput {
// Name // Name
mOS.write(NAME_FIELD_TYPE); mOS.write(NAME_FIELD_TYPE);
Types.writeCString(mPG.name, mOS); int nameLen = Types.writeCString(mPG.name, mOS);
outputBytes += nameLen;
// Create date // Create date
mOS.write(CREATE_FIELD_TYPE); mOS.write(CREATE_FIELD_TYPE);
mOS.write(DATE_FIELD_SIZE); mOS.write(DATE_FIELD_SIZE);
@@ -90,21 +113,11 @@ public class PwGroupOutput {
mOS.write(ZERO_FIELD_SIZE); mOS.write(ZERO_FIELD_SIZE);
} }
public static final byte[] GROUPID_FIELD_TYPE = Types.writeShort(1); /** Returns the number of bytes written by the stream
public static final byte[] NAME_FIELD_TYPE = Types.writeShort(2); * @return Number of bytes written
public static final byte[] CREATE_FIELD_TYPE = Types.writeShort(3); */
public static final byte[] MOD_FIELD_TYPE = Types.writeShort(4); public long getLength() {
public static final byte[] ACCESS_FIELD_TYPE = Types.writeShort(5); return outputBytes;
public static final byte[] EXPIRE_FIELD_TYPE = Types.writeShort(6); }
public static final byte[] IMAGEID_FIELD_TYPE = Types.writeShort(7);
public static final byte[] LEVEL_FIELD_TYPE = Types.writeShort(8);
public static final byte[] FLAGS_FIELD_TYPE = Types.writeShort(9);
public static final byte[] END_FIELD_TYPE = Types.writeShort(0xFFFF);
public static final byte[] LONG_FOUR = Types.writeInt(4);
public static final byte[] GROUPID_FIELD_SIZE = LONG_FOUR;
public static final byte[] DATE_FIELD_SIZE = Types.writeInt(5);
public static final byte[] IMAGEID_FIELD_SIZE = LONG_FOUR;
public static final byte[] LEVEL_FIELD_SIZE = Types.writeInt(2);
public static final byte[] FLAGS_FIELD_SIZE = LONG_FOUR;
public static final byte[] ZERO_FIELD_SIZE = Types.writeInt(0);
} }

View File

@@ -0,0 +1,179 @@
/*
* 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.android.keepass.keepasslib;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import org.phoneid.keepassj2me.PwDbHeader;
import org.phoneid.keepassj2me.PwEntry;
import org.phoneid.keepassj2me.PwGroup;
import org.phoneid.keepassj2me.PwManager;
public class PwManagerOutput {
private PwManager mPM;
private OutputStream mOS;
private final boolean mDebug;
public static final boolean DEBUG = true;
public PwManagerOutput(PwManager pm, OutputStream os) {
mPM = pm;
mOS = os;
mDebug = false;
}
public PwManagerOutput(PwManager pm, OutputStream os, boolean debug) {
mPM = pm;
mOS = os;
mDebug = debug;
}
public void output() throws IOException, PwManagerOutputException {
/*
int filePadding = (int)(16 - (fileSize % 16)); // Pad file to 16-byte boundary
if ( filePadding > 0 ) {
byte[] padding = new byte[filePadding];
fos.write(padding);
}
*/
}
public void outputHeader(OutputStream os) throws PwManagerOutputException {
// Build header
PwDbHeader header = new PwDbHeader();
header.signature1 = PwDbHeader.PWM_DBSIG_1;
header.signature2 = PwDbHeader.PWM_DBSIG_2;
header.flags = PwDbHeader.PWM_FLAG_SHA2;
if ( mPM.getAlgorithm() == PwDbHeader.ALGO_AES ) {
header.flags |= PwDbHeader.PWM_FLAG_RIJNDAEL;
} else if ( mPM.getAlgorithm() == PwDbHeader.ALGO_TWOFISH ) {
header.flags |= PwDbHeader.PWM_FLAG_TWOFISH;
throw new PwManagerOutputException("Unsupported algorithm.");
} else {
throw new PwManagerOutputException("Unsupported algorithm.");
}
header.version = PwDbHeader.PWM_DBVER_DW;
header.numGroups = mPM.groups.size();
header.numEntries = mPM.entries.size();
header.numKeyEncRounds = mPM.getNumKeyEncRecords();
// Reuse random values to test equivalence in debug mode
if ( mDebug ) {
System.arraycopy(mPM.dbHeader.encryptionIV, 0, header.encryptionIV, 0, header.encryptionIV.length);
System.arraycopy(mPM.dbHeader.masterSeed, 0, header.masterSeed, 0, header.masterSeed.length);
System.arraycopy(mPM.dbHeader.masterSeed2, 0, header.masterSeed2, 0, header.masterSeed2.length);
} else {
SecureRandom random;
try {
random = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
throw new PwManagerOutputException("Does not support secure random number generation.");
}
random.nextBytes(header.encryptionIV);
random.nextBytes(header.masterSeed);
random.nextBytes(header.masterSeed2);
}
// Write checksum Checksum
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
assert true;
throw new PwManagerOutputException("SHA-256 not implemented here.");
}
FileOutputStream fos;
try {
fos = new FileOutputStream("/dev/null");
} catch (FileNotFoundException e1) {
throw new PwManagerOutputException("Could not open /dev/null");
}
DigestOutputStream dos = new DigestOutputStream(fos, md);
try {
outputPlanGroupAndEntries(dos);
dos.close();
} catch (IOException e) {
throw new PwManagerOutputException("Failed to generate checksum.");
}
header.contentsHash = md.digest();
// Output header
PwDbHeaderOutput pho = new PwDbHeaderOutput(header, os);
try {
pho.output();
} catch (IOException e) {
throw new PwManagerOutputException("Failed to output the header.");
}
}
public void outputPlanGroupAndEntries(OutputStream os) throws IOException {
//long size = 0;
// Groups
for (int i = 0; i < mPM.groups.size(); i++ ) {
PwGroup pg = mPM.groups.get(i);
PwGroupOutput pgo = new PwGroupOutput(pg, os);
pgo.output();
//size += pgo.getLength();
}
// Entries
for (int i = 0; i < mPM.entries.size(); i++ ) {
PwEntry pe = mPM.entries.get(i);
PwEntryOutput peo = new PwEntryOutput(pe, os);
peo.output();
//size += peo.getLength();
}
//return size;
}
class PwManagerOutputException extends Exception {
public PwManagerOutputException(String string) {
super(string);
}
/**
*
*/
private static final long serialVersionUID = 3321212743159473368L;
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.android.keepass.keepasslib;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
public class RandomFileOutputStream extends OutputStream {
RandomAccessFile mFile;
RandomFileOutputStream(RandomAccessFile file) {
mFile = file;
}
@Override
public void close() throws IOException {
super.close();
mFile.close();
}
@Override
public void write(byte[] buffer, int offset, int count) throws IOException {
super.write(buffer, offset, count);
mFile.write(buffer, offset, count);
}
@Override
public void write(byte[] buffer) throws IOException {
super.write(buffer);
mFile.write(buffer);
}
@Override
public void write(int oneByte) throws IOException {
mFile.write(oneByte);
}
public void seek(long pos) throws IOException {
mFile.seek(pos);
}
}

View File

@@ -58,8 +58,16 @@ import com.android.keepass.keepasslib.InvalidKeyFileException;
*/ */
public class ImporterV3 { public class ImporterV3 {
public static final boolean DEBUG = true;
private final boolean mDebug;
public ImporterV3() { public ImporterV3() {
//super(); mDebug = false;
}
public ImporterV3(boolean debug) {
mDebug = debug;
} }
@@ -102,12 +110,12 @@ public class ImporterV3 {
throw new IOException( "File too short for header" ); throw new IOException( "File too short for header" );
PwDbHeader hdr = new PwDbHeader( filebuf, 0 ); PwDbHeader hdr = new PwDbHeader( filebuf, 0 );
if( (hdr.signature1 != PwManager.PWM_DBSIG_1) || (hdr.signature2 != PwManager.PWM_DBSIG_2) ) { if( (hdr.signature1 != PwDbHeader.PWM_DBSIG_1) || (hdr.signature2 != PwDbHeader.PWM_DBSIG_2) ) {
//KeePassMIDlet.logS ( "Bad database file signature" ); //KeePassMIDlet.logS ( "Bad database file signature" );
throw new IOException( "Bad database file signature" ); throw new IOException( "Bad database file signature" );
} }
if( hdr.version != PwManager.PWM_DBVER_DW ) { if( hdr.version != PwDbHeader.PWM_DBVER_DW ) {
//KeePassMIDlet.logS ( "Bad database file version"); //KeePassMIDlet.logS ( "Bad database file version");
//throw new IOException( "Bad database file version" ); //throw new IOException( "Bad database file version" );
} }
@@ -116,20 +124,22 @@ public class ImporterV3 {
newManager.setMasterKey( password, keyfile ); newManager.setMasterKey( password, keyfile );
// Select algorithm // Select algorithm
if( (hdr.flags & PwManager.PWM_FLAG_RIJNDAEL) != 0 ) { if( (hdr.flags & PwDbHeader.PWM_FLAG_RIJNDAEL) != 0 ) {
//KeePassMIDlet.logS ( "Algorithm AES"); //KeePassMIDlet.logS ( "Algorithm AES");
newManager.algorithm = PwManager.ALGO_AES; newManager.algorithm = PwDbHeader.ALGO_AES;
} else if( (hdr.flags & PwManager.PWM_FLAG_TWOFISH) != 0 ) { } else if( (hdr.flags & PwDbHeader.PWM_FLAG_TWOFISH) != 0 ) {
//KeePassMIDlet.logS ( "Algorithm TWOFISH"); //KeePassMIDlet.logS ( "Algorithm TWOFISH");
newManager.algorithm = PwManager.ALGO_TWOFISH; newManager.algorithm = PwDbHeader.ALGO_TWOFISH;
} else { } else {
throw new IOException( "Unknown algorithm." ); throw new IOException( "Unknown algorithm." );
} }
if( newManager.algorithm == PwManager.ALGO_TWOFISH ) if( newManager.algorithm == PwDbHeader.ALGO_TWOFISH )
throw new IOException( "TwoFish algorithm is not supported" ); throw new IOException( "TwoFish algorithm is not supported" );
newManager.dbHeader = hdr; if ( mDebug ) {
newManager.dbHeader = hdr;
}
newManager.numKeyEncRounds = hdr.numKeyEncRounds; newManager.numKeyEncRounds = hdr.numKeyEncRounds;
@@ -180,10 +190,10 @@ public class ImporterV3 {
System.arraycopy(filebuf, PwDbHeader.BUF_SIZE, plainContent, 0, encryptedPartSize); System.arraycopy(filebuf, PwDbHeader.BUF_SIZE, plainContent, 0, encryptedPartSize);
*/ */
// TODO: Delete Me, temp for debugging if ( mDebug ) {
newManager.postHeader = new byte[encryptedPartSize]; newManager.postHeader = new byte[encryptedPartSize];
System.arraycopy(filebuf, PwDbHeader.BUF_SIZE, newManager.postHeader, 0, encryptedPartSize); System.arraycopy(filebuf, PwDbHeader.BUF_SIZE, newManager.postHeader, 0, encryptedPartSize);
}
//if( pRepair == null ) { //if( pRepair == null ) {
md = new SHA256Digest(); md = new SHA256Digest();

View File

@@ -25,6 +25,22 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
package org.phoneid.keepassj2me; package org.phoneid.keepassj2me;
public class PwDbHeader { public class PwDbHeader {
// DB sig from KeePass 1.03
public static final int PWM_DBSIG_1 = 0x9AA2D903;
// DB sig from KeePass 1.03
public static final int PWM_DBSIG_2 = 0xB54BFB65;
// DB sig from KeePass 1.03
public static final int PWM_DBVER_DW = 0x00030002;
public static final int PWM_FLAG_SHA2 = 1;
public static final int PWM_FLAG_RIJNDAEL = 2;
public static final int PWM_FLAG_ARCFOUR = 4;
public static final int PWM_FLAG_TWOFISH = 8;
public static final int ALGO_AES = 0;
public static final int ALGO_TWOFISH = 1;
/** /**
* Parse given buf, as read from file. * Parse given buf, as read from file.
* @param buf * @param buf
@@ -46,11 +62,9 @@ public class PwDbHeader {
System.arraycopy( buf, offset + 88, masterSeed2, 0, 32 ); System.arraycopy( buf, offset + 88, masterSeed2, 0, 32 );
numKeyEncRounds = Types.readInt( buf, offset + 120 ); numKeyEncRounds = Types.readInt( buf, offset + 120 );
} }
public PwDbHeader() {
public void toBuf( byte[] buf, int offset ) {
throw new RuntimeException("Method 'toBuf' not implemented yet");
} }

View File

@@ -47,20 +47,6 @@ public class PwManager {
// Constants // Constants
// private static final int PWM_SESSION_KEY_SIZE = 12; // private static final int PWM_SESSION_KEY_SIZE = 12;
// DB sig from KeePass 1.03
static final int PWM_DBSIG_1 = 0x9AA2D903;
// DB sig from KeePass 1.03
static final int PWM_DBSIG_2 = 0xB54BFB65;
// DB sig from KeePass 1.03
static final int PWM_DBVER_DW = 0x00030002;
static final int PWM_FLAG_SHA2 = 1;
static final int PWM_FLAG_RIJNDAEL = 2;
static final int PWM_FLAG_ARCFOUR = 4;
static final int PWM_FLAG_TWOFISH = 8;
static final int ALGO_AES = 0;
static final int ALGO_TWOFISH = 1;
// Descriptive name for database, used in GUI. // Descriptive name for database, used in GUI.
public String name = "KeePass database"; public String name = "KeePass database";
@@ -88,7 +74,15 @@ public class PwManager {
// root group // root group
PwGroup rootGroup; PwGroup rootGroup;
public int getAlgorithm() {
return algorithm;
}
public int getNumKeyEncRecords() {
return numKeyEncRounds;
}
public void setMasterKey( String key, String keyFileName ) throws InvalidKeyFileException, IOException { public void setMasterKey( String key, String keyFileName ) throws InvalidKeyFileException, IOException {
assert( key != null && keyFileName != null ); assert( key != null && keyFileName != null );

View File

@@ -26,7 +26,6 @@ package org.phoneid.keepassj2me;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
@@ -219,16 +218,6 @@ public class Types {
//return null; //return null;
} }
public static void writeCString(String str, OutputStream os) throws IOException {
byte[] initial = str.getBytes("UTF-8");
int length = initial.length;
os.write(writeInt(length+1));
os.write(initial);
os.write(0x00);
}
public static byte[] writeTime(Date date) { public static byte[] writeTime(Date date) {
byte[] buf = new byte[5]; byte[] buf = new byte[5];
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
@@ -250,4 +239,16 @@ public class Types {
return buf; return buf;
} }
public static int writeCString(String str, OutputStream os) throws IOException {
byte[] initial = str.getBytes("UTF-8");
int length = initial.length+1;
os.write(writeInt(length));
os.write(initial);
os.write(0x00);
return length;
}
} }

View File

@@ -1,100 +0,0 @@
/*
* 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 3 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.android.keepass.tests;
import static org.junit.Assert.assertArrayEquals;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import junit.framework.TestCase;
import org.phoneid.keepassj2me.PwEntry;
import org.phoneid.keepassj2me.PwGroup;
import org.phoneid.keepassj2me.PwManager;
import org.phoneid.keepassj2me.Types;
import com.android.keepass.keepasslib.PwEntryOutput;
import com.android.keepass.keepasslib.PwGroupOutput;
public class PwGroupOutputTest extends TestCase {
PwManager mPM;
@Override
protected void setUp() throws Exception {
super.setUp();
mPM = TestData.GetTest1();
}
public void testPlainContent() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// Groups
for (int i = 0; i < mPM.groups.size(); i++ ) {
PwGroup pg = mPM.groups.get(i);
PwGroupOutput pgo = new PwGroupOutput(pg, bos);
pgo.output();
}
// Entries
for (int i = 0; i < mPM.entries.size(); i++ ) {
boolean debug;
PwEntry pe = mPM.entries.get(i);
PwEntryOutput peo = new PwEntryOutput(pe, bos);
debug = (i == 1);
peo.output();
}
byte[] buf = bos.toByteArray();
assertArrayEquals(mPM.postHeader, bos.toByteArray());
}
public void testChecksum() throws NoSuchAlgorithmException, IOException {
FileOutputStream fos = new FileOutputStream("/dev/null");
MessageDigest md = MessageDigest.getInstance("SHA-256");
DigestOutputStream dos = new DigestOutputStream(fos, md);
// Groups
for (int i = 0; i < mPM.groups.size(); i++ ) {
PwGroup pg = mPM.groups.get(i);
PwGroupOutput pgo = new PwGroupOutput(pg, dos);
pgo.output();
}
// Entries
for (int i = 0; i < mPM.entries.size(); i++ ) {
PwEntry pe = mPM.entries.get(i);
PwEntryOutput peo = new PwEntryOutput(pe, dos);
peo.output();
}
assertArrayEquals("Hash of groups and entries failed.", md.digest(), mPM.dbHeader.contentsHash);
}
}

View File

@@ -0,0 +1,96 @@
/*
* 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 3 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.android.keepass.tests;
import static org.junit.Assert.assertArrayEquals;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import junit.framework.TestCase;
import org.phoneid.keepassj2me.PwManager;
import com.android.keepass.keepasslib.PwDbHeaderOutput;
import com.android.keepass.keepasslib.PwManagerOutput;
public class PwManagerOutputTest extends TestCase {
PwManager mPM;
@Override
protected void setUp() throws Exception {
super.setUp();
mPM = TestData.GetTest1();
}
public void testPlainContent() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PwManagerOutput pos = new PwManagerOutput(mPM, bos, PwManagerOutput.DEBUG);
pos.outputPlanGroupAndEntries(bos);
assertArrayEquals("Group and entry output doesn't match.", mPM.postHeader, bos.toByteArray());
}
public void testChecksum() throws NoSuchAlgorithmException, IOException {
FileOutputStream fos = new FileOutputStream("/dev/null");
MessageDigest md = MessageDigest.getInstance("SHA-256");
DigestOutputStream dos = new DigestOutputStream(fos, md);
PwManagerOutput pos = new PwManagerOutput(mPM, dos, PwManagerOutput.DEBUG);
pos.outputPlanGroupAndEntries(dos);
assertArrayEquals("Hash of groups and entries failed.", md.digest(), mPM.dbHeader.contentsHash);
}
public void testHeader() throws Exception {
ByteArrayOutputStream bActual = new ByteArrayOutputStream();
PwManagerOutput pActual = new PwManagerOutput(mPM, bActual, PwManagerOutput.DEBUG);
pActual.outputHeader(bActual);
ByteArrayOutputStream bExpected = new ByteArrayOutputStream();
PwDbHeaderOutput outExpected = new PwDbHeaderOutput(mPM.dbHeader, bExpected);
outExpected.output();
assertArrayEquals("Header does not match.", bExpected.toByteArray(), bActual.toByteArray());
}
/*
public void testEncryptedPart() throws Exception {
File file = new File("/sdcard/test1.kdb");
long length = file.length();
FileInputStream fis = new FileInputStream(file);
byte[] expected = new byte[(int)(length-PwDbHeader.BUF_SIZE)];
fis.skip(PwDbHeader.BUF_SIZE);
fis.read(expected);
}
*/
}

View File

@@ -36,7 +36,7 @@ public class TestData {
if ( test1 == null ) { if ( test1 == null ) {
FileInputStream fis = new FileInputStream("/sdcard/test1.kdb"); FileInputStream fis = new FileInputStream("/sdcard/test1.kdb");
ImporterV3 importer = new ImporterV3(); ImporterV3 importer = new ImporterV3(ImporterV3.DEBUG);
test1 = importer.openDatabase(fis, "12345", ""); test1 = importer.openDatabase(fis, "12345", "");
if (test1 != null) { if (test1 != null) {
test1.constructTree(null); test1.constructTree(null);