Dynamic creation of KdfEngine, reorganise code

This commit is contained in:
J-Jamet
2018-05-09 15:17:55 +02:00
parent c72aa0e97d
commit fea7af6910
16 changed files with 148 additions and 270 deletions

View File

@@ -21,28 +21,28 @@ package com.kunzisoft.keepass.crypto.keyDerivation;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class KdfFactory {
public static AesKdf aesKdf = new AesKdf();
public static Argon2Kdf argon2Kdf = new Argon2Kdf();
public static List<KdfEngine> kdfListV3 = new ArrayList<>();
public static List<KdfEngine> kdfList = new ArrayList<>();
static {
kdfList.add(new AesKdf());
kdfList.add(new Argon2Kdf());
kdfListV3.add(aesKdf);
kdfList.add(aesKdf);
kdfList.add(argon2Kdf);
}
public static KdfParameters getDefaultParameters() {
return kdfList.get(0).getDefaultParameters();
}
public static KdfEngine get(UUID uuid) {
public static KdfEngine get(KdfParameters kdfParameters) {
for (KdfEngine engine: kdfList) {
if (engine.uuid.equals(uuid)) {
if (engine.uuid.equals(kdfParameters.kdfUUID)) {
return engine;
}
}
return null;
}

View File

@@ -35,7 +35,7 @@ public class KdfParameters extends VariantDictionary {
private static final String ParamUUID = "$UUID";
public KdfParameters(UUID uuid) {
KdfParameters(UUID uuid) {
kdfUUID = uuid;
}
@@ -45,20 +45,14 @@ public class KdfParameters extends VariantDictionary {
VariantDictionary d = VariantDictionary.deserialize(lis);
if (d == null) {
assert(false);
return null;
}
UUID uuid = Types.bytestoUUID(d.getByteArray(ParamUUID));
if (uuid == null) {
assert(false);
return null;
}
KdfParameters kdfP = new KdfParameters(uuid);
kdfP.copyTo(d);
return kdfP;
}
public static byte[] serialize(KdfParameters kdf) throws IOException {

View File

@@ -28,6 +28,7 @@ import android.util.Log;
import com.kunzisoft.keepass.R;
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine;
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory;
import com.kunzisoft.keepass.database.exception.ContentFileNotFoundException;
import com.kunzisoft.keepass.database.exception.InvalidDBException;
import com.kunzisoft.keepass.database.exception.InvalidPasswordException;
@@ -376,9 +377,9 @@ public class Database {
public List<KdfEngine> getAvailableKdfEngines() {
switch (getPwDatabase().getVersion()) {
case V4:
return ((PwDatabaseV4) getPwDatabase()).getAvailableKdfEngines();
return KdfFactory.kdfList;
case V3:
return ((PwDatabaseV3) getPwDatabase()).getAvailableKdfEngines();
return KdfFactory.kdfListV3;
}
return new ArrayList<>();
}
@@ -391,14 +392,18 @@ public class Database {
switch (getPwDatabase().getVersion()) {
case V4:
PwDatabaseV4 db = ((PwDatabaseV4) getPwDatabase());
db.setKdfEngine(kdfEngine);
db.setKdfParameters(kdfEngine.getDefaultParameters());
db.setNumberKeyEncryptionRounds(kdfEngine.getDefaultKeyRounds());
setNumberKeyEncryptionRounds(kdfEngine.getDefaultKeyRounds());
break;
}
}
public String getKeyDerivationName(Resources resources) {
return getPwDatabase().getKeyDerivationName(resources);
KdfEngine kdfEngine = getPwDatabase().getKdfEngine();
if (kdfEngine != null) {
return kdfEngine.getName(resources);
}
return "";
}
public String getNumberKeyEncryptionRoundsAsString() {

View File

@@ -19,8 +19,6 @@
*/
package com.kunzisoft.keepass.database;
import android.content.res.Resources;
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine;
import com.kunzisoft.keepass.database.exception.InvalidKeyFileException;
import com.kunzisoft.keepass.database.exception.KeyFileEmptyException;
@@ -251,12 +249,8 @@ public abstract class PwDatabase<PwGroupDB extends PwGroup<PwGroupDB, PwGroupDB,
public abstract List<PwEncryptionAlgorithm> getAvailableEncryptionAlgorithms();
public abstract List<KdfEngine> getAvailableKdfEngines();
public abstract KdfEngine getKdfEngine();
public abstract String getKeyDerivationName(Resources resources);
public abstract List<PwGroupDB> getGrpRoots();
public abstract List<PwGroupDB> getGroups();

View File

@@ -45,12 +45,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
package com.kunzisoft.keepass.database;
import android.content.res.Resources;
import com.kunzisoft.keepass.crypto.finalkey.FinalKey;
import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory;
import com.kunzisoft.keepass.crypto.keyDerivation.AesKdf;
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine;
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory;
import com.kunzisoft.keepass.database.exception.InvalidKeyFileException;
import com.kunzisoft.keepass.stream.NullOutputStream;
@@ -71,7 +69,6 @@ import java.util.Random;
public class PwDatabaseV3 extends PwDatabase<PwGroupV3, PwEntryV3> {
private static final int DEFAULT_ENCRYPTION_ROUNDS = 300;
private KdfEngine kdfEngine = new AesKdf(); // Always the same
// all entries
private List<PwEntryV3> entries = new ArrayList<>();
@@ -107,19 +104,7 @@ public class PwDatabaseV3 extends PwDatabase<PwGroupV3, PwEntryV3> {
@Override
public KdfEngine getKdfEngine() {
return kdfEngine;
}
@Override
public List<KdfEngine> getAvailableKdfEngines() {
List<KdfEngine> list = new ArrayList<>();
list.add(kdfEngine);
return list;
}
@Override
public String getKeyDerivationName(Resources resources) {
return kdfEngine.getName(resources);
return KdfFactory.aesKdf;
}
@Override
@@ -346,7 +331,6 @@ public class PwDatabaseV3 extends PwDatabase<PwGroupV3, PwEntryV3> {
// Add tree to root groups
groups.add(newGroup);
}
@Override

View File

@@ -19,7 +19,6 @@
*/
package com.kunzisoft.keepass.database;
import android.content.res.Resources;
import android.webkit.URLUtil;
import com.kunzisoft.keepass.collections.VariantDictionary;
@@ -67,9 +66,10 @@ public class PwDatabaseV4 extends PwDatabase<PwGroupV4, PwEntryV4> {
private UUID dataCipher = AesEngine.CIPHER_UUID;
private CipherEngine dataEngine = new AesEngine();
private PwCompressionAlgorithm compressionAlgorithm = PwCompressionAlgorithm.Gzip;
private KdfEngine kdfEngine;
private KdfParameters kdfParameters;
private long numKeyEncRounds;
private VariantDictionary publicCustomData = new VariantDictionary();
private long numKeyEncRounds = AesKdf.DEFAULT_ROUNDS; // By default take the AES rounds
protected String name = "KeePass DX database";
private PwDate nameChanged = new PwDate();
private PwDate settingsChanged = new PwDate();
@@ -99,8 +99,6 @@ public class PwDatabaseV4 extends PwDatabase<PwGroupV4, PwEntryV4> {
private List<PwIconCustom> customIcons = new ArrayList<>();
private Map<String, String> customData = new HashMap<>();
private KdfParameters kdfParameters = KdfFactory.getDefaultParameters();
private VariantDictionary publicCustomData = new VariantDictionary();
private BinaryPool binPool = new BinaryPool();
public String localizedAppName = "KeePassDX"; // TODO resource
@@ -153,24 +151,7 @@ public class PwDatabaseV4 extends PwDatabase<PwGroupV4, PwEntryV4> {
@Override
public KdfEngine getKdfEngine() {
return kdfEngine;
}
public void setKdfEngine(KdfEngine kdfEngine) {
this.kdfEngine = kdfEngine;
}
@Override
public String getKeyDerivationName(Resources resources) {
if (kdfEngine!=null)
return kdfEngine.getName(resources);
else
return "";
}
@Override
public List<KdfEngine> getAvailableKdfEngines() {
return KdfFactory.kdfList;
return KdfFactory.get(getKdfParameters());
}
public KdfParameters getKdfParameters() {
@@ -389,25 +370,25 @@ public class PwDatabaseV4 extends PwDatabase<PwGroupV4, PwEntryV4> {
return md.digest(fKey);
}
public void makeFinalKey(byte[] masterSeed, KdfParameters kdfP) throws IOException {
makeFinalKey(masterSeed, kdfP, 0);
public void makeFinalKey(byte[] masterSeed) throws IOException {
makeFinalKey(masterSeed, 0);
}
public void makeFinalKey(byte[] masterSeed, KdfParameters kdfP, long roundsFix)
public void makeFinalKey(byte[] masterSeed, long roundsFix)
throws IOException {
kdfEngine = KdfFactory.get(kdfP.kdfUUID);
KdfEngine kdfEngine = KdfFactory.get(kdfParameters);
if (kdfEngine == null) {
throw new IOException("Unknown key derivation function");
}
// Set to 6000 rounds to open corrupted database
if (roundsFix > 0 && kdfP.kdfUUID.equals(AesKdf.CIPHER_UUID)) {
kdfP.setUInt32(AesKdf.ParamRounds, roundsFix);
if (roundsFix > 0 && kdfParameters.kdfUUID.equals(AesKdf.CIPHER_UUID)) {
kdfParameters.setUInt32(AesKdf.ParamRounds, roundsFix);
numKeyEncRounds = roundsFix;
}
byte[] transformedMasterKey = kdfEngine.transform(masterKey, kdfP);
byte[] transformedMasterKey = kdfEngine.transform(masterKey, kdfParameters);
if (transformedMasterKey.length != 32) {
transformedMasterKey = CryptoUtil.hashSha256(transformedMasterKey);
}
@@ -795,7 +776,7 @@ public class PwDatabaseV4 extends PwDatabase<PwGroupV4, PwEntryV4> {
}
public int getMinKdbxVersion() {
if (!AesKdf.CIPHER_UUID.equals(kdfParameters.kdfUUID)) {
if (kdfParameters != null && !AesKdf.CIPHER_UUID.equals(kdfParameters.kdfUUID)) {
return PwDbHeaderV4.FILE_VERSION_32;
}

View File

@@ -20,6 +20,7 @@
package com.kunzisoft.keepass.database;
import com.kunzisoft.keepass.crypto.keyDerivation.AesKdf;
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory;
import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters;
import com.kunzisoft.keepass.database.exception.InvalidDBVersionException;
import com.kunzisoft.keepass.stream.CopyInputStream;
@@ -92,9 +93,9 @@ public class PwDbHeaderV4 extends PwDbHeader {
public long version;
public PwDbHeaderV4(PwDatabaseV4 d) {
db = d;
version = d.getMinKdbxVersion();
masterSeed = new byte[32];
this.db = d;
this.version = d.getMinKdbxVersion();
this.masterSeed = new byte[32];
}
/** Assumes the input stream is at the beginning of the .kdbx file
@@ -173,24 +174,13 @@ public class PwDbHeaderV4 extends PwDbHeader {
break;
case PwDbHeaderV4Fields.TransformSeed:
assert(version < PwDbHeaderV4.FILE_VERSION_32_4); // TODO file > FILEVERSION
AesKdf kdfS = new AesKdf();
if (!db.getKdfParameters().kdfUUID.equals(kdfS.uuid)) {
db.setKdfParameters(kdfS.getDefaultParameters());
}
db.getKdfParameters().setByteArray(AesKdf.ParamSeed, fieldData);
if(version < PwDbHeaderV4.FILE_VERSION_32_4)
setTransformSeed(fieldData);
break;
case PwDbHeaderV4Fields.TransformRounds:
assert(version < PwDbHeaderV4.FILE_VERSION_32_4);
AesKdf kdfR = new AesKdf();
if (!db.getKdfParameters().kdfUUID.equals(kdfR.uuid)) {
db.setKdfParameters(kdfR.getDefaultParameters());
}
long rounds = LEDataInputStream.readLong(fieldData, 0);
db.getKdfParameters().setUInt64(AesKdf.ParamRounds, rounds);
db.setNumberKeyEncryptionRounds(rounds);
if(version < PwDbHeaderV4.FILE_VERSION_32_4)
setTransformRound(fieldData);
break;
case PwDbHeaderV4Fields.EncryptionIV:
@@ -198,7 +188,7 @@ public class PwDbHeaderV4 extends PwDbHeader {
break;
case PwDbHeaderV4Fields.InnerRandomstreamKey:
assert(version < PwDbHeaderV4.FILE_VERSION_32_4);
if(version < PwDbHeaderV4.FILE_VERSION_32_4)
innerRandomStreamKey = fieldData;
break;
@@ -207,7 +197,7 @@ public class PwDbHeaderV4 extends PwDbHeader {
break;
case PwDbHeaderV4Fields.InnerRandomStreamID:
assert(version < PwDbHeaderV4.FILE_VERSION_32_4);
if(version < PwDbHeaderV4.FILE_VERSION_32_4)
setRandomStreamID(fieldData);
break;
@@ -225,6 +215,12 @@ public class PwDbHeaderV4 extends PwDbHeader {
return false;
}
private void assignAesKdfEngineIfNotExists() {
if (db.getKdfParameters() == null || !db.getKdfParameters().kdfUUID.equals(KdfFactory.aesKdf.uuid)) {
db.setKdfParameters(KdfFactory.aesKdf.getDefaultParameters());
}
}
private void setCipher(byte[] pbId) throws IOException {
if ( pbId == null || pbId.length != 16 ) {
throw new IOException("Invalid cipher ID.");
@@ -233,6 +229,18 @@ public class PwDbHeaderV4 extends PwDbHeader {
db.setDataCipher(Types.bytestoUUID(pbId));
}
private void setTransformSeed(byte[] seed) {
assignAesKdfEngineIfNotExists();
db.getKdfParameters().setByteArray(AesKdf.ParamSeed, seed);
}
private void setTransformRound(byte[] roundsByte) {
assignAesKdfEngineIfNotExists();
long rounds = LEDataInputStream.readLong(roundsByte, 0);
db.getKdfParameters().setUInt64(AesKdf.ParamRounds, rounds);
db.setNumberKeyEncryptionRounds(rounds);
}
private void setCompressionFlags(byte[] pbFlags) throws IOException {
if ( pbFlags == null || pbFlags.length != 4 ) {
throw new IOException("Invalid compression flags.");
@@ -244,23 +252,6 @@ public class PwDbHeaderV4 extends PwDbHeader {
}
db.setCompressionAlgorithm(PwCompressionAlgorithm.fromId(flag));
}
private void setTransformRounds(byte[] rounds) throws IOException {
if ( rounds == null || rounds.length != 8 ) {
throw new IOException("Invalid rounds.");
}
long rnd = LEDataInputStream.readLong(rounds, 0);
if ( rnd < 0 || rnd > Integer.MAX_VALUE ) {
//TODO: Actually support really large numbers
throw new IOException("Rounds higher than " + Integer.MAX_VALUE + " are not currently supported.");
}
db.setNumberKeyEncryptionRounds(rnd);
}
public void setRandomStreamID(byte[] streamID) throws IOException {
@@ -276,25 +267,23 @@ public class PwDbHeaderV4 extends PwDbHeader {
innerRandomStream = CrsAlgorithm.fromId(id);
}
/** Determines if this is a supported version.
/**
* Determines if this is a supported version.
*
* A long is needed here to represent the unsigned int since we perform
* arithmetic on it.
* @param version
* @return
* A long is needed here to represent the unsigned int since we perform arithmetic on it.
* @param version Database version
* @return true if it's a supported version
*/
private boolean validVersion(long version) {
return ! ((version & FILE_VERSION_CRITICAL_MASK) > (FILE_VERSION_32 & FILE_VERSION_CRITICAL_MASK));
}
public static boolean matchesHeader(int sig1, int sig2) {
return (sig1 == PWM_DBSIG_1) && ( (sig2 == DBSIG_2) || (sig2 == DBSIG_2) );
return (sig1 == PWM_DBSIG_1) && ( (sig2 == DBSIG_2) );
//return (sig1 == PWM_DBSIG_1) && ( (sig2 == DBSIG_PRE2) || (sig2 == DBSIG_2) ); // TODO verify add DBSIG_PRE2
}
public static byte[] computeHeaderHmac(byte[] header, byte[] key) throws IOException{
byte[] headerHmac;
byte[] blockKey = HmacBlockStream.GetHmacKey64(key, Types.ULONG_MAX_VALUE);
Mac hmac;
@@ -312,8 +301,7 @@ public class PwDbHeaderV4 extends PwDbHeader {
}
public byte[] getTransformSeed() {
assert(version < FILE_VERSION_32_4);
// version < FILE_VERSION_32_4)
return db.getKdfParameters().getByteArray(AesKdf.ParamSeed);
}
}

View File

@@ -27,24 +27,21 @@ import com.kunzisoft.keepass.database.exception.PwDbOutputException;
import java.io.IOException;
public class SaveDBRunnable extends RunnableOnFinish {
private Context mCtx;
private Database mDb;
private boolean mDontSave;
private Context mCtx;
public SaveDBRunnable(Context ctx, Database db, OnFinishRunnable finish, boolean dontSave) {
super(finish);
mDb = db;
mDontSave = dontSave;
mCtx = ctx;
this.mDb = db;
this.mDontSave = dontSave;
this.mCtx = ctx;
}
public SaveDBRunnable(Context ctx, Database db, OnFinishRunnable finish) {
super(finish);
mDb = db;
mDontSave = false;
mCtx = ctx;
this(ctx, db, finish, false);
}
@Override

View File

@@ -63,7 +63,6 @@ import com.kunzisoft.keepass.database.exception.InvalidDBVersionException;
import com.kunzisoft.keepass.database.exception.InvalidKeyFileException;
import com.kunzisoft.keepass.database.exception.InvalidPasswordException;
import com.kunzisoft.keepass.stream.LEDataInputStream;
import com.kunzisoft.keepass.stream.LEDataOutputStream;
import com.kunzisoft.keepass.stream.NullOutputStream;
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater;
import com.kunzisoft.keepass.utils.Types;
@@ -133,13 +132,13 @@ public class ImporterV3 extends Importer {
@Override
public PwDatabaseV3 openDatabase(InputStream inStream, String password, InputStream kfIs, ProgressTaskUpdater progressTaskUpdater, long roundsFix)
throws IOException, InvalidDBException {
PwDatabaseV3 newManager;
PwDatabaseV3 databaseToOpen;
// Load entire file, most of it's encrypted.
int fileSize = inStream.available();
byte[] filebuf = new byte[fileSize + 16]; // Pad with a blocksize (Twofish uses 128 bits), since Android 4.3 tries to write more to the buffer
inStream.read(filebuf, 0, fileSize);
inStream.read(filebuf, 0, fileSize); // TODO remove
inStream.close();
// Parse header (unencrypted)
@@ -158,34 +157,34 @@ public class ImporterV3 extends Importer {
if (progressTaskUpdater != null)
progressTaskUpdater.updateMessage(R.string.creating_db_key);
newManager = createDB();
newManager.retrieveMasterKey(password, kfIs);
databaseToOpen = createDB();
databaseToOpen.retrieveMasterKey(password, kfIs);
// Select algorithm
if( (hdr.flags & PwDbHeaderV3.FLAG_RIJNDAEL) != 0 ) {
newManager.setEncryptionAlgorithm(PwEncryptionAlgorithm.AES_Rijndael);
databaseToOpen.setEncryptionAlgorithm(PwEncryptionAlgorithm.AES_Rijndael);
} else if( (hdr.flags & PwDbHeaderV3.FLAG_TWOFISH) != 0 ) {
newManager.setEncryptionAlgorithm(PwEncryptionAlgorithm.Twofish);
databaseToOpen.setEncryptionAlgorithm(PwEncryptionAlgorithm.Twofish);
} else {
throw new InvalidAlgorithmException();
}
// Copy for testing
newManager.copyHeader(hdr);
databaseToOpen.copyHeader(hdr);
newManager.setNumberKeyEncryptionRounds(hdr.numKeyEncRounds);
databaseToOpen.setNumberKeyEncryptionRounds(hdr.numKeyEncRounds);
// Generate transformedMasterKey from masterKey
newManager.makeFinalKey(hdr.masterSeed, hdr.transformSeed, newManager.getNumberKeyEncryptionRounds());
databaseToOpen.makeFinalKey(hdr.masterSeed, hdr.transformSeed, databaseToOpen.getNumberKeyEncryptionRounds());
if (progressTaskUpdater != null)
progressTaskUpdater.updateMessage(R.string.decrypting_db);
// Initialize Rijndael algorithm
Cipher cipher;
try {
if ( newManager.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AES_Rijndael) {
if ( databaseToOpen.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AES_Rijndael) {
cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding");
} else if ( newManager.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish ) {
} else if ( databaseToOpen.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish ) {
cipher = CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING");
} else {
throw new IOException( "Encryption algorithm is not supported" );
@@ -198,7 +197,7 @@ public class ImporterV3 extends Importer {
}
try {
cipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec( newManager.getFinalKey(), "AES" ), new IvParameterSpec( hdr.encryptionIV ) );
cipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec( databaseToOpen.getFinalKey(), "AES" ), new IvParameterSpec( hdr.encryptionIV ) );
} catch (InvalidKeyException e1) {
throw new IOException("Invalid key");
} catch (InvalidAlgorithmParameterException e1) {
@@ -218,7 +217,7 @@ public class ImporterV3 extends Importer {
}
// Copy decrypted data for testing
newManager.copyEncrypted(filebuf, PwDbHeaderV3.BUF_SIZE, encryptedPartSize);
databaseToOpen.copyEncrypted(filebuf, PwDbHeaderV3.BUF_SIZE, encryptedPartSize);
MessageDigest md = null;
try {
@@ -251,13 +250,13 @@ public class ImporterV3 extends Importer {
if( fieldType == 0xFFFF ) {
// End-Group record. Save group and count it.
newGrp.populateBlankFields(newManager);
newManager.addGroup(newGrp);
newGrp.populateBlankFields(databaseToOpen);
databaseToOpen.addGroup(newGrp);
newGrp = new PwGroupV3();
i++;
}
else {
readGroupField(newManager, newGrp, fieldType, filebuf, pos);
readGroupField(databaseToOpen, newGrp, fieldType, filebuf, pos);
}
pos += fieldSize;
}
@@ -270,65 +269,22 @@ public class ImporterV3 extends Importer {
if( fieldType == 0xFFFF ) {
// End-Group record. Save group and count it.
newEnt.populateBlankFields(newManager);
newManager.addEntry(newEnt);
newEnt.populateBlankFields(databaseToOpen);
databaseToOpen.addEntry(newEnt);
newEnt = new PwEntryV3();
i++;
}
else {
readEntryField(newManager, newEnt, filebuf, pos);
readEntryField(databaseToOpen, newEnt, filebuf, pos);
}
pos += 2 + 4 + fieldSize;
}
newManager.constructTree(null);
databaseToOpen.constructTree(null);
return newManager;
return databaseToOpen;
}
/**
* KeePass's custom pad style.
*
* @param data buffer to pad.
* @return addtional bytes to append to data[] to make
* a properly padded array.
*/
public static byte[] makePad( byte[] data ) {
//custom pad method
// append 0x80 plus zeros to a multiple of 4 bytes
int thisblk = 32 - data.length % 32; // bytes needed to finish blk
int nextblk = 0; // 32 if we need another block
// need 9 bytes; add new block if no room
if( thisblk < 9 ) {
nextblk = 32;
}
// all bytes are zeroed for free
byte[] pad = new byte[ thisblk + nextblk ];
pad[0] = (byte)0x80;
// write length*8 to end of final block
int ix = thisblk + nextblk - 8;
LEDataOutputStream.writeInt( data.length>>29, pad, ix );
bsw32( pad, ix );
ix += 4;
LEDataOutputStream.writeInt( data.length<<3, pad, ix );
bsw32( pad, ix );
return pad;
}
public static void bsw32( byte[] ary, int offset ) {
byte t = ary[offset];
ary[offset] = ary[offset+3];
ary[offset+3] = t;
t = ary[offset+1];
ary[offset+1] = ary[offset+2];
ary[offset+2] = t;
}
/**
* Parse and save one record from binary file.
* @param buf
@@ -336,7 +292,7 @@ public class ImporterV3 extends Importer {
* @return If >0,
* @throws UnsupportedEncodingException
*/
void readGroupField(PwDatabaseV3 db, PwGroupV3 grp, int fieldType, byte[] buf, int offset) throws UnsupportedEncodingException {
private void readGroupField(PwDatabaseV3 db, PwGroupV3 grp, int fieldType, byte[] buf, int offset) throws UnsupportedEncodingException {
switch( fieldType ) {
case 0x0000 :
// Ignore field
@@ -373,9 +329,7 @@ public class ImporterV3 extends Importer {
void readEntryField(PwDatabaseV3 db, PwEntryV3 ent, byte[] buf, int offset)
throws UnsupportedEncodingException
{
private void readEntryField(PwDatabaseV3 db, PwEntryV3 ent, byte[] buf, int offset) throws UnsupportedEncodingException {
int fieldType = LEDataInputStream.readUShort(buf, offset);
offset += 2;
int fieldSize = LEDataInputStream.readInt(buf, offset);

View File

@@ -82,18 +82,12 @@ public class ImporterV4 extends Importer {
private byte[] hashOfHeader = null;
private byte[] pbHeader = null;
private long version;
private int binNum = 0;
Calendar utcCal;
public ImporterV4() {
utcCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
}
protected PwDatabaseV4 createDB() {
return new PwDatabaseV4();
}
@Override
public PwDatabaseV4 openDatabase(InputStream inStream, String password,
InputStream keyInputStream) throws IOException, InvalidDBException {
@@ -108,7 +102,7 @@ public class ImporterV4 extends Importer {
if (progressTaskUpdater != null)
progressTaskUpdater.updateMessage(R.string.creating_db_key);
db = createDB();
db = new PwDatabaseV4();
PwDbHeaderV4 header = new PwDbHeaderV4(db);
db.getBinPool().clear();
@@ -120,7 +114,7 @@ public class ImporterV4 extends Importer {
pbHeader = hh.header;
db.retrieveMasterKey(password, keyInputStream);
db.makeFinalKey(header.masterSeed, db.getKdfParameters(), roundsFix);
db.makeFinalKey(header.masterSeed, roundsFix);
if (progressTaskUpdater != null)
progressTaskUpdater.updateMessage(R.string.decrypting_db);
@@ -192,7 +186,7 @@ public class ImporterV4 extends Importer {
isXml = isPlain;
}
if (version >= header.FILE_VERSION_32_4) {
if (version >= PwDbHeaderV4.FILE_VERSION_32_4) {
LoadInnerHeader(isXml, header);
}

View File

@@ -55,7 +55,7 @@ public class PwDbHeaderOutputV4 extends PwDbHeaderOutput {
db = d;
header = h;
MessageDigest md = null;
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
@@ -63,7 +63,7 @@ public class PwDbHeaderOutputV4 extends PwDbHeaderOutput {
}
try {
d.makeFinalKey(header.masterSeed, d.getKdfParameters());
d.makeFinalKey(header.masterSeed);
} catch (IOException e) {
throw new PwDbOutputException(e);
}

View File

@@ -29,7 +29,7 @@ import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public abstract class PwDbOutput {
public abstract class PwDbOutput<Header extends PwDbHeader> {
protected OutputStream mOS;
@@ -47,7 +47,7 @@ public abstract class PwDbOutput {
mOS = os;
}
protected SecureRandom setIVs(PwDbHeader header) throws PwDbOutputException {
protected SecureRandom setIVs(Header header) throws PwDbOutputException {
SecureRandom random;
try {
random = SecureRandom.getInstance("SHA1PRNG");
@@ -62,6 +62,6 @@ public abstract class PwDbOutput {
public abstract void output() throws PwDbOutputException;
public abstract PwDbHeader outputHeader(OutputStream os) throws PwDbOutputException;
public abstract Header outputHeader(OutputStream os) throws PwDbOutputException;
}

View File

@@ -48,7 +48,7 @@ import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class PwDbV3Output extends PwDbOutput {
public class PwDbV3Output extends PwDbOutput<PwDbHeaderV3> {
private PwDatabaseV3 mPM;
private byte[] headerHashBlock;
@@ -111,15 +111,13 @@ public class PwDbV3Output extends PwDbOutput {
}
@Override
protected SecureRandom setIVs(PwDbHeader header) throws PwDbOutputException {
protected SecureRandom setIVs(PwDbHeaderV3 header) throws PwDbOutputException {
SecureRandom random = super.setIVs(header);
PwDbHeaderV3 h3 = (PwDbHeaderV3) header;
random.nextBytes(h3.transformSeed);
random.nextBytes(header.transformSeed);
return random;
}
@Override
public PwDbHeaderV3 outputHeader(OutputStream os) throws PwDbOutputException {
// Build header
PwDbHeaderV3 header = new PwDbHeaderV3();

View File

@@ -21,7 +21,6 @@ package com.kunzisoft.keepass.database.save;
import com.kunzisoft.keepass.database.PwDatabaseV3;
import com.kunzisoft.keepass.database.PwDatabaseV3Debug;
import com.kunzisoft.keepass.database.PwDbHeader;
import com.kunzisoft.keepass.database.PwDbHeaderV3;
import com.kunzisoft.keepass.database.exception.PwDbOutputException;
@@ -32,10 +31,6 @@ public class PwDbV3OutputDebug extends PwDbV3Output {
PwDatabaseV3Debug debugDb;
private boolean noHeaderHash;
public PwDbV3OutputDebug(PwDatabaseV3 pm, OutputStream os) {
this(pm, os, false);
}
public PwDbV3OutputDebug(PwDatabaseV3 pm, OutputStream os, boolean noHeaderHash) {
super(pm, os);
debugDb = (PwDatabaseV3Debug) pm;
@@ -43,10 +38,7 @@ public class PwDbV3OutputDebug extends PwDbV3Output {
}
@Override
protected SecureRandom setIVs(PwDbHeader h) throws PwDbOutputException {
PwDbHeaderV3 header = (PwDbHeaderV3) h;
protected SecureRandom setIVs(PwDbHeaderV3 header) throws PwDbOutputException {
// Reuse random values to test equivalence in debug mode
PwDbHeaderV3 origHeader = debugDb.getDbHeader();
System.arraycopy(origHeader.encryptionIV, 0, header.encryptionIV, 0, origHeader.encryptionIV.length);

View File

@@ -35,7 +35,6 @@ import com.kunzisoft.keepass.database.MemoryProtectionConfig;
import com.kunzisoft.keepass.database.PwCompressionAlgorithm;
import com.kunzisoft.keepass.database.PwDatabaseV4;
import com.kunzisoft.keepass.database.PwDatabaseV4XML;
import com.kunzisoft.keepass.database.PwDbHeader;
import com.kunzisoft.keepass.database.PwDbHeaderV4;
import com.kunzisoft.keepass.database.PwDefsV4;
import com.kunzisoft.keepass.database.PwDeletedObject;
@@ -74,9 +73,9 @@ import javax.crypto.CipherOutputStream;
import biz.source_code.base64Coder.Base64Coder;
public class PwDbV4Output extends PwDbOutput {
public class PwDbV4Output extends PwDbOutput<PwDbHeaderV4> {
PwDatabaseV4 mPM;
private PwDatabaseV4 mPM;
private StreamCipher randomStream;
private XmlSerializer xml;
private PwDbHeaderV4 header;
@@ -86,8 +85,7 @@ public class PwDbV4Output extends PwDbOutput {
protected PwDbV4Output(PwDatabaseV4 pm, OutputStream os) {
super(os);
mPM = pm;
this.mPM = pm;
}
@Override
@@ -100,15 +98,14 @@ public class PwDbV4Output extends PwDbOutput {
throw new PwDbOutputException("No such cipher", e);
}
header = (PwDbHeaderV4) outputHeader(mOS);
header = outputHeader(mOS);
OutputStream osPlain;
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
CipherOutputStream cos = attachStreamEncryptor(header, mOS);
cos.write(header.streamStartBytes);
HashedBlockOutputStream hashed = new HashedBlockOutputStream(cos);
osPlain = hashed;
osPlain = new HashedBlockOutputStream(cos);
} else {
mOS.write(hashOfHeader);
mOS.write(headerHmac);
@@ -286,45 +283,43 @@ public class PwDbV4Output extends PwDbOutput {
}
@Override
protected SecureRandom setIVs(PwDbHeader header) throws PwDbOutputException {
protected SecureRandom setIVs(PwDbHeaderV4 header) throws PwDbOutputException {
SecureRandom random = super.setIVs(header);
PwDbHeaderV4 h = (PwDbHeaderV4) header;
random.nextBytes(h.masterSeed);
random.nextBytes(header.masterSeed);
int ivLength = engine.ivLength();
if (ivLength != h.encryptionIV.length) {
h.encryptionIV = new byte[ivLength];
if (ivLength != header.encryptionIV.length) {
header.encryptionIV = new byte[ivLength];
}
random.nextBytes(h.encryptionIV);
random.nextBytes(header.encryptionIV);
UUID kdfUUID = mPM.getKdfParameters().kdfUUID;
KdfEngine kdf = KdfFactory.get(kdfUUID);
KdfEngine kdf = KdfFactory.get(mPM.getKdfParameters());
kdf.randomize(mPM.getKdfParameters());
if (h.version < PwDbHeaderV4.FILE_VERSION_32_4) {
h.innerRandomStream = CrsAlgorithm.Salsa20;
h.innerRandomStreamKey = new byte[32];
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
header.innerRandomStream = CrsAlgorithm.Salsa20;
header.innerRandomStreamKey = new byte[32];
} else {
h.innerRandomStream = CrsAlgorithm.ChaCha20;
h.innerRandomStreamKey = new byte[64];
header.innerRandomStream = CrsAlgorithm.ChaCha20;
header.innerRandomStreamKey = new byte[64];
}
random.nextBytes(h.innerRandomStreamKey);
random.nextBytes(header.innerRandomStreamKey);
randomStream = PwStreamCipherFactory.getInstance(h.innerRandomStream, h.innerRandomStreamKey);
randomStream = PwStreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey);
if (randomStream == null) {
throw new PwDbOutputException("Invalid random cipher");
}
if ( h.version < PwDbHeaderV4.FILE_VERSION_32_4) {
random.nextBytes(h.streamStartBytes);
if ( header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
random.nextBytes(header.streamStartBytes);
}
return random;
}
@Override
public PwDbHeader outputHeader(OutputStream os) throws PwDbOutputException {
public PwDbHeaderV4 outputHeader(OutputStream os) throws PwDbOutputException {
PwDbHeaderV4 header = new PwDbHeaderV4(mPM);
setIVs(header);

View File

@@ -38,7 +38,7 @@ public abstract class DatabaseSavePreferenceDialogFragmentCompat extends InputP
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
database = App.getDB();
this.database = App.getDB();
}
@Override
@@ -47,7 +47,9 @@ public abstract class DatabaseSavePreferenceDialogFragmentCompat extends InputP
assert getActivity() != null;
if (database != null && afterSaveDatabase != null) {
SaveDBRunnable saveDBRunnable = new SaveDBRunnable(getContext(), database, afterSaveDatabase);
SaveDBRunnable saveDBRunnable = new SaveDBRunnable(getContext(),
database,
afterSaveDatabase);
saveDBRunnable.setUpdateProgressTaskStatus(
new UpdateProgressTaskStatus(getContext(),
SaveDatabaseProgressTaskDialogFragment.start(