mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Dynamic creation of KdfEngine, reorganise code
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user