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.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID;
public class KdfFactory { 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<>(); public static List<KdfEngine> kdfList = new ArrayList<>();
static { static {
kdfList.add(new AesKdf()); kdfListV3.add(aesKdf);
kdfList.add(new Argon2Kdf());
kdfList.add(aesKdf);
kdfList.add(argon2Kdf);
} }
public static KdfParameters getDefaultParameters() { public static KdfEngine get(KdfParameters kdfParameters) {
return kdfList.get(0).getDefaultParameters();
}
public static KdfEngine get(UUID uuid) {
for (KdfEngine engine: kdfList) { for (KdfEngine engine: kdfList) {
if (engine.uuid.equals(uuid)) { if (engine.uuid.equals(kdfParameters.kdfUUID)) {
return engine; return engine;
} }
} }
return null; return null;
} }

View File

@@ -35,7 +35,7 @@ public class KdfParameters extends VariantDictionary {
private static final String ParamUUID = "$UUID"; private static final String ParamUUID = "$UUID";
public KdfParameters(UUID uuid) { KdfParameters(UUID uuid) {
kdfUUID = uuid; kdfUUID = uuid;
} }
@@ -45,20 +45,14 @@ public class KdfParameters extends VariantDictionary {
VariantDictionary d = VariantDictionary.deserialize(lis); VariantDictionary d = VariantDictionary.deserialize(lis);
if (d == null) { if (d == null) {
assert(false);
return null; return null;
} }
UUID uuid = Types.bytestoUUID(d.getByteArray(ParamUUID)); UUID uuid = Types.bytestoUUID(d.getByteArray(ParamUUID));
if (uuid == null) {
assert(false);
return null;
}
KdfParameters kdfP = new KdfParameters(uuid); KdfParameters kdfP = new KdfParameters(uuid);
kdfP.copyTo(d); kdfP.copyTo(d);
return kdfP; return kdfP;
} }
public static byte[] serialize(KdfParameters kdf) throws IOException { 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.R;
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; 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.ContentFileNotFoundException;
import com.kunzisoft.keepass.database.exception.InvalidDBException; import com.kunzisoft.keepass.database.exception.InvalidDBException;
import com.kunzisoft.keepass.database.exception.InvalidPasswordException; import com.kunzisoft.keepass.database.exception.InvalidPasswordException;
@@ -376,9 +377,9 @@ public class Database {
public List<KdfEngine> getAvailableKdfEngines() { public List<KdfEngine> getAvailableKdfEngines() {
switch (getPwDatabase().getVersion()) { switch (getPwDatabase().getVersion()) {
case V4: case V4:
return ((PwDatabaseV4) getPwDatabase()).getAvailableKdfEngines(); return KdfFactory.kdfList;
case V3: case V3:
return ((PwDatabaseV3) getPwDatabase()).getAvailableKdfEngines(); return KdfFactory.kdfListV3;
} }
return new ArrayList<>(); return new ArrayList<>();
} }
@@ -391,14 +392,18 @@ public class Database {
switch (getPwDatabase().getVersion()) { switch (getPwDatabase().getVersion()) {
case V4: case V4:
PwDatabaseV4 db = ((PwDatabaseV4) getPwDatabase()); PwDatabaseV4 db = ((PwDatabaseV4) getPwDatabase());
db.setKdfEngine(kdfEngine);
db.setKdfParameters(kdfEngine.getDefaultParameters()); db.setKdfParameters(kdfEngine.getDefaultParameters());
db.setNumberKeyEncryptionRounds(kdfEngine.getDefaultKeyRounds()); setNumberKeyEncryptionRounds(kdfEngine.getDefaultKeyRounds());
break;
} }
} }
public String getKeyDerivationName(Resources resources) { public String getKeyDerivationName(Resources resources) {
return getPwDatabase().getKeyDerivationName(resources); KdfEngine kdfEngine = getPwDatabase().getKdfEngine();
if (kdfEngine != null) {
return kdfEngine.getName(resources);
}
return "";
} }
public String getNumberKeyEncryptionRoundsAsString() { public String getNumberKeyEncryptionRoundsAsString() {

View File

@@ -19,8 +19,6 @@
*/ */
package com.kunzisoft.keepass.database; package com.kunzisoft.keepass.database;
import android.content.res.Resources;
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine;
import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; import com.kunzisoft.keepass.database.exception.InvalidKeyFileException;
import com.kunzisoft.keepass.database.exception.KeyFileEmptyException; 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<PwEncryptionAlgorithm> getAvailableEncryptionAlgorithms();
public abstract List<KdfEngine> getAvailableKdfEngines();
public abstract KdfEngine getKdfEngine(); public abstract KdfEngine getKdfEngine();
public abstract String getKeyDerivationName(Resources resources);
public abstract List<PwGroupDB> getGrpRoots(); public abstract List<PwGroupDB> getGrpRoots();
public abstract List<PwGroupDB> getGroups(); 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; package com.kunzisoft.keepass.database;
import android.content.res.Resources;
import com.kunzisoft.keepass.crypto.finalkey.FinalKey; import com.kunzisoft.keepass.crypto.finalkey.FinalKey;
import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory; 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.KdfEngine;
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory;
import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; import com.kunzisoft.keepass.database.exception.InvalidKeyFileException;
import com.kunzisoft.keepass.stream.NullOutputStream; import com.kunzisoft.keepass.stream.NullOutputStream;
@@ -71,7 +69,6 @@ import java.util.Random;
public class PwDatabaseV3 extends PwDatabase<PwGroupV3, PwEntryV3> { public class PwDatabaseV3 extends PwDatabase<PwGroupV3, PwEntryV3> {
private static final int DEFAULT_ENCRYPTION_ROUNDS = 300; private static final int DEFAULT_ENCRYPTION_ROUNDS = 300;
private KdfEngine kdfEngine = new AesKdf(); // Always the same
// all entries // all entries
private List<PwEntryV3> entries = new ArrayList<>(); private List<PwEntryV3> entries = new ArrayList<>();
@@ -107,19 +104,7 @@ public class PwDatabaseV3 extends PwDatabase<PwGroupV3, PwEntryV3> {
@Override @Override
public KdfEngine getKdfEngine() { public KdfEngine getKdfEngine() {
return kdfEngine; return KdfFactory.aesKdf;
}
@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);
} }
@Override @Override
@@ -346,7 +331,6 @@ public class PwDatabaseV3 extends PwDatabase<PwGroupV3, PwEntryV3> {
// Add tree to root groups // Add tree to root groups
groups.add(newGroup); groups.add(newGroup);
} }
@Override @Override

View File

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

View File

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

View File

@@ -27,24 +27,21 @@ import com.kunzisoft.keepass.database.exception.PwDbOutputException;
import java.io.IOException; import java.io.IOException;
public class SaveDBRunnable extends RunnableOnFinish { public class SaveDBRunnable extends RunnableOnFinish {
private Context mCtx;
private Database mDb; private Database mDb;
private boolean mDontSave; private boolean mDontSave;
private Context mCtx;
public SaveDBRunnable(Context ctx, Database db, OnFinishRunnable finish, boolean dontSave) { public SaveDBRunnable(Context ctx, Database db, OnFinishRunnable finish, boolean dontSave) {
super(finish); super(finish);
mDb = db; this.mDb = db;
mDontSave = dontSave; this.mDontSave = dontSave;
mCtx = ctx; this.mCtx = ctx;
} }
public SaveDBRunnable(Context ctx, Database db, OnFinishRunnable finish) { public SaveDBRunnable(Context ctx, Database db, OnFinishRunnable finish) {
super(finish); this(ctx, db, finish, false);
mDb = db;
mDontSave = false;
mCtx = ctx;
} }
@Override @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.InvalidKeyFileException;
import com.kunzisoft.keepass.database.exception.InvalidPasswordException; import com.kunzisoft.keepass.database.exception.InvalidPasswordException;
import com.kunzisoft.keepass.stream.LEDataInputStream; import com.kunzisoft.keepass.stream.LEDataInputStream;
import com.kunzisoft.keepass.stream.LEDataOutputStream;
import com.kunzisoft.keepass.stream.NullOutputStream; import com.kunzisoft.keepass.stream.NullOutputStream;
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; import com.kunzisoft.keepass.tasks.ProgressTaskUpdater;
import com.kunzisoft.keepass.utils.Types; import com.kunzisoft.keepass.utils.Types;
@@ -133,13 +132,13 @@ public class ImporterV3 extends Importer {
@Override @Override
public PwDatabaseV3 openDatabase(InputStream inStream, String password, InputStream kfIs, ProgressTaskUpdater progressTaskUpdater, long roundsFix) public PwDatabaseV3 openDatabase(InputStream inStream, String password, InputStream kfIs, ProgressTaskUpdater progressTaskUpdater, long roundsFix)
throws IOException, InvalidDBException { throws IOException, InvalidDBException {
PwDatabaseV3 newManager;
PwDatabaseV3 databaseToOpen;
// Load entire file, most of it's encrypted. // Load entire file, most of it's encrypted.
int fileSize = inStream.available(); 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 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(); inStream.close();
// Parse header (unencrypted) // Parse header (unencrypted)
@@ -158,34 +157,34 @@ public class ImporterV3 extends Importer {
if (progressTaskUpdater != null) if (progressTaskUpdater != null)
progressTaskUpdater.updateMessage(R.string.creating_db_key); progressTaskUpdater.updateMessage(R.string.creating_db_key);
newManager = createDB(); databaseToOpen = createDB();
newManager.retrieveMasterKey(password, kfIs); databaseToOpen.retrieveMasterKey(password, kfIs);
// Select algorithm // Select algorithm
if( (hdr.flags & PwDbHeaderV3.FLAG_RIJNDAEL) != 0 ) { 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 ) { } else if( (hdr.flags & PwDbHeaderV3.FLAG_TWOFISH) != 0 ) {
newManager.setEncryptionAlgorithm(PwEncryptionAlgorithm.Twofish); databaseToOpen.setEncryptionAlgorithm(PwEncryptionAlgorithm.Twofish);
} else { } else {
throw new InvalidAlgorithmException(); throw new InvalidAlgorithmException();
} }
// Copy for testing // Copy for testing
newManager.copyHeader(hdr); databaseToOpen.copyHeader(hdr);
newManager.setNumberKeyEncryptionRounds(hdr.numKeyEncRounds); databaseToOpen.setNumberKeyEncryptionRounds(hdr.numKeyEncRounds);
// Generate transformedMasterKey from masterKey // Generate transformedMasterKey from masterKey
newManager.makeFinalKey(hdr.masterSeed, hdr.transformSeed, newManager.getNumberKeyEncryptionRounds()); databaseToOpen.makeFinalKey(hdr.masterSeed, hdr.transformSeed, databaseToOpen.getNumberKeyEncryptionRounds());
if (progressTaskUpdater != null) if (progressTaskUpdater != null)
progressTaskUpdater.updateMessage(R.string.decrypting_db); progressTaskUpdater.updateMessage(R.string.decrypting_db);
// Initialize Rijndael algorithm // Initialize Rijndael algorithm
Cipher cipher; Cipher cipher;
try { try {
if ( newManager.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AES_Rijndael) { if ( databaseToOpen.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AES_Rijndael) {
cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding"); cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding");
} else if ( newManager.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish ) { } else if ( databaseToOpen.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish ) {
cipher = CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING"); cipher = CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING");
} else { } else {
throw new IOException( "Encryption algorithm is not supported" ); throw new IOException( "Encryption algorithm is not supported" );
@@ -198,7 +197,7 @@ public class ImporterV3 extends Importer {
} }
try { 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) { } catch (InvalidKeyException e1) {
throw new IOException("Invalid key"); throw new IOException("Invalid key");
} catch (InvalidAlgorithmParameterException e1) { } catch (InvalidAlgorithmParameterException e1) {
@@ -218,7 +217,7 @@ public class ImporterV3 extends Importer {
} }
// Copy decrypted data for testing // Copy decrypted data for testing
newManager.copyEncrypted(filebuf, PwDbHeaderV3.BUF_SIZE, encryptedPartSize); databaseToOpen.copyEncrypted(filebuf, PwDbHeaderV3.BUF_SIZE, encryptedPartSize);
MessageDigest md = null; MessageDigest md = null;
try { try {
@@ -251,13 +250,13 @@ public class ImporterV3 extends Importer {
if( fieldType == 0xFFFF ) { if( fieldType == 0xFFFF ) {
// End-Group record. Save group and count it. // End-Group record. Save group and count it.
newGrp.populateBlankFields(newManager); newGrp.populateBlankFields(databaseToOpen);
newManager.addGroup(newGrp); databaseToOpen.addGroup(newGrp);
newGrp = new PwGroupV3(); newGrp = new PwGroupV3();
i++; i++;
} }
else { else {
readGroupField(newManager, newGrp, fieldType, filebuf, pos); readGroupField(databaseToOpen, newGrp, fieldType, filebuf, pos);
} }
pos += fieldSize; pos += fieldSize;
} }
@@ -270,65 +269,22 @@ public class ImporterV3 extends Importer {
if( fieldType == 0xFFFF ) { if( fieldType == 0xFFFF ) {
// End-Group record. Save group and count it. // End-Group record. Save group and count it.
newEnt.populateBlankFields(newManager); newEnt.populateBlankFields(databaseToOpen);
newManager.addEntry(newEnt); databaseToOpen.addEntry(newEnt);
newEnt = new PwEntryV3(); newEnt = new PwEntryV3();
i++; i++;
} }
else { else {
readEntryField(newManager, newEnt, filebuf, pos); readEntryField(databaseToOpen, newEnt, filebuf, pos);
} }
pos += 2 + 4 + fieldSize; 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. * Parse and save one record from binary file.
* @param buf * @param buf
@@ -336,7 +292,7 @@ public class ImporterV3 extends Importer {
* @return If >0, * @return If >0,
* @throws UnsupportedEncodingException * @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 ) { switch( fieldType ) {
case 0x0000 : case 0x0000 :
// Ignore field // Ignore field
@@ -373,9 +329,7 @@ public class ImporterV3 extends Importer {
void readEntryField(PwDatabaseV3 db, PwEntryV3 ent, byte[] buf, int offset) private void readEntryField(PwDatabaseV3 db, PwEntryV3 ent, byte[] buf, int offset) throws UnsupportedEncodingException {
throws UnsupportedEncodingException
{
int fieldType = LEDataInputStream.readUShort(buf, offset); int fieldType = LEDataInputStream.readUShort(buf, offset);
offset += 2; offset += 2;
int fieldSize = LEDataInputStream.readInt(buf, offset); int fieldSize = LEDataInputStream.readInt(buf, offset);

View File

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

View File

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

View File

@@ -29,7 +29,7 @@ import java.io.OutputStream;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
public abstract class PwDbOutput { public abstract class PwDbOutput<Header extends PwDbHeader> {
protected OutputStream mOS; protected OutputStream mOS;
@@ -47,7 +47,7 @@ public abstract class PwDbOutput {
mOS = os; mOS = os;
} }
protected SecureRandom setIVs(PwDbHeader header) throws PwDbOutputException { protected SecureRandom setIVs(Header header) throws PwDbOutputException {
SecureRandom random; SecureRandom random;
try { try {
random = SecureRandom.getInstance("SHA1PRNG"); random = SecureRandom.getInstance("SHA1PRNG");
@@ -62,6 +62,6 @@ public abstract class PwDbOutput {
public abstract void output() throws PwDbOutputException; 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.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
public class PwDbV3Output extends PwDbOutput { public class PwDbV3Output extends PwDbOutput<PwDbHeaderV3> {
private PwDatabaseV3 mPM; private PwDatabaseV3 mPM;
private byte[] headerHashBlock; private byte[] headerHashBlock;
@@ -111,15 +111,13 @@ public class PwDbV3Output extends PwDbOutput {
} }
@Override @Override
protected SecureRandom setIVs(PwDbHeader header) throws PwDbOutputException { protected SecureRandom setIVs(PwDbHeaderV3 header) throws PwDbOutputException {
SecureRandom random = super.setIVs(header); SecureRandom random = super.setIVs(header);
random.nextBytes(header.transformSeed);
PwDbHeaderV3 h3 = (PwDbHeaderV3) header;
random.nextBytes(h3.transformSeed);
return random; return random;
} }
@Override
public PwDbHeaderV3 outputHeader(OutputStream os) throws PwDbOutputException { public PwDbHeaderV3 outputHeader(OutputStream os) throws PwDbOutputException {
// Build header // Build header
PwDbHeaderV3 header = new PwDbHeaderV3(); 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.PwDatabaseV3;
import com.kunzisoft.keepass.database.PwDatabaseV3Debug; import com.kunzisoft.keepass.database.PwDatabaseV3Debug;
import com.kunzisoft.keepass.database.PwDbHeader;
import com.kunzisoft.keepass.database.PwDbHeaderV3; import com.kunzisoft.keepass.database.PwDbHeaderV3;
import com.kunzisoft.keepass.database.exception.PwDbOutputException; import com.kunzisoft.keepass.database.exception.PwDbOutputException;
@@ -32,10 +31,6 @@ public class PwDbV3OutputDebug extends PwDbV3Output {
PwDatabaseV3Debug debugDb; PwDatabaseV3Debug debugDb;
private boolean noHeaderHash; private boolean noHeaderHash;
public PwDbV3OutputDebug(PwDatabaseV3 pm, OutputStream os) {
this(pm, os, false);
}
public PwDbV3OutputDebug(PwDatabaseV3 pm, OutputStream os, boolean noHeaderHash) { public PwDbV3OutputDebug(PwDatabaseV3 pm, OutputStream os, boolean noHeaderHash) {
super(pm, os); super(pm, os);
debugDb = (PwDatabaseV3Debug) pm; debugDb = (PwDatabaseV3Debug) pm;
@@ -43,10 +38,7 @@ public class PwDbV3OutputDebug extends PwDbV3Output {
} }
@Override @Override
protected SecureRandom setIVs(PwDbHeader h) throws PwDbOutputException { protected SecureRandom setIVs(PwDbHeaderV3 header) throws PwDbOutputException {
PwDbHeaderV3 header = (PwDbHeaderV3) h;
// Reuse random values to test equivalence in debug mode // Reuse random values to test equivalence in debug mode
PwDbHeaderV3 origHeader = debugDb.getDbHeader(); PwDbHeaderV3 origHeader = debugDb.getDbHeader();
System.arraycopy(origHeader.encryptionIV, 0, header.encryptionIV, 0, origHeader.encryptionIV.length); 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.PwCompressionAlgorithm;
import com.kunzisoft.keepass.database.PwDatabaseV4; import com.kunzisoft.keepass.database.PwDatabaseV4;
import com.kunzisoft.keepass.database.PwDatabaseV4XML; import com.kunzisoft.keepass.database.PwDatabaseV4XML;
import com.kunzisoft.keepass.database.PwDbHeader;
import com.kunzisoft.keepass.database.PwDbHeaderV4; import com.kunzisoft.keepass.database.PwDbHeaderV4;
import com.kunzisoft.keepass.database.PwDefsV4; import com.kunzisoft.keepass.database.PwDefsV4;
import com.kunzisoft.keepass.database.PwDeletedObject; import com.kunzisoft.keepass.database.PwDeletedObject;
@@ -74,9 +73,9 @@ import javax.crypto.CipherOutputStream;
import biz.source_code.base64Coder.Base64Coder; 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 StreamCipher randomStream;
private XmlSerializer xml; private XmlSerializer xml;
private PwDbHeaderV4 header; private PwDbHeaderV4 header;
@@ -86,8 +85,7 @@ public class PwDbV4Output extends PwDbOutput {
protected PwDbV4Output(PwDatabaseV4 pm, OutputStream os) { protected PwDbV4Output(PwDatabaseV4 pm, OutputStream os) {
super(os); super(os);
this.mPM = pm;
mPM = pm;
} }
@Override @Override
@@ -100,15 +98,14 @@ public class PwDbV4Output extends PwDbOutput {
throw new PwDbOutputException("No such cipher", e); throw new PwDbOutputException("No such cipher", e);
} }
header = (PwDbHeaderV4) outputHeader(mOS); header = outputHeader(mOS);
OutputStream osPlain; OutputStream osPlain;
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) { if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
CipherOutputStream cos = attachStreamEncryptor(header, mOS); CipherOutputStream cos = attachStreamEncryptor(header, mOS);
cos.write(header.streamStartBytes); cos.write(header.streamStartBytes);
HashedBlockOutputStream hashed = new HashedBlockOutputStream(cos); osPlain = new HashedBlockOutputStream(cos);
osPlain = hashed;
} else { } else {
mOS.write(hashOfHeader); mOS.write(hashOfHeader);
mOS.write(headerHmac); mOS.write(headerHmac);
@@ -286,45 +283,43 @@ public class PwDbV4Output extends PwDbOutput {
} }
@Override @Override
protected SecureRandom setIVs(PwDbHeader header) throws PwDbOutputException { protected SecureRandom setIVs(PwDbHeaderV4 header) throws PwDbOutputException {
SecureRandom random = super.setIVs(header); SecureRandom random = super.setIVs(header);
random.nextBytes(header.masterSeed);
PwDbHeaderV4 h = (PwDbHeaderV4) header;
random.nextBytes(h.masterSeed);
int ivLength = engine.ivLength(); int ivLength = engine.ivLength();
if (ivLength != h.encryptionIV.length) { if (ivLength != header.encryptionIV.length) {
h.encryptionIV = new byte[ivLength]; header.encryptionIV = new byte[ivLength];
} }
random.nextBytes(h.encryptionIV); random.nextBytes(header.encryptionIV);
UUID kdfUUID = mPM.getKdfParameters().kdfUUID; KdfEngine kdf = KdfFactory.get(mPM.getKdfParameters());
KdfEngine kdf = KdfFactory.get(kdfUUID);
kdf.randomize(mPM.getKdfParameters()); kdf.randomize(mPM.getKdfParameters());
if (h.version < PwDbHeaderV4.FILE_VERSION_32_4) { if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
h.innerRandomStream = CrsAlgorithm.Salsa20; header.innerRandomStream = CrsAlgorithm.Salsa20;
h.innerRandomStreamKey = new byte[32]; header.innerRandomStreamKey = new byte[32];
} else { } else {
h.innerRandomStream = CrsAlgorithm.ChaCha20; header.innerRandomStream = CrsAlgorithm.ChaCha20;
h.innerRandomStreamKey = new byte[64]; 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) { if (randomStream == null) {
throw new PwDbOutputException("Invalid random cipher"); throw new PwDbOutputException("Invalid random cipher");
} }
if ( h.version < PwDbHeaderV4.FILE_VERSION_32_4) { if ( header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
random.nextBytes(h.streamStartBytes); random.nextBytes(header.streamStartBytes);
} }
return random; return random;
} }
@Override @Override
public PwDbHeader outputHeader(OutputStream os) throws PwDbOutputException { public PwDbHeaderV4 outputHeader(OutputStream os) throws PwDbOutputException {
PwDbHeaderV4 header = new PwDbHeaderV4(mPM); PwDbHeaderV4 header = new PwDbHeaderV4(mPM);
setIVs(header); setIVs(header);

View File

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