mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Register UUID of database version in KDF parameter
This commit is contained in:
@@ -58,6 +58,8 @@ public class AesKdf extends KdfEngine {
|
||||
@Override
|
||||
public KdfParameters getDefaultParameters() {
|
||||
KdfParameters p = new KdfParameters(uuid);
|
||||
|
||||
p.setParamUUID();
|
||||
p.setUInt32(ParamRounds, DEFAULT_ROUNDS);
|
||||
|
||||
return p;
|
||||
|
||||
@@ -79,10 +79,11 @@ public class Argon2Kdf extends KdfEngine {
|
||||
public KdfParameters getDefaultParameters() {
|
||||
KdfParameters p = new KdfParameters(uuid);
|
||||
|
||||
p.setUInt32(ParamVersion, MaxVersion);
|
||||
p.setUInt64(ParamMemory, DefaultMemory);
|
||||
p.setParamUUID();
|
||||
p.setUInt32(ParamParallelism, DefaultParallelism);
|
||||
p.setUInt64(ParamMemory, DefaultMemory);
|
||||
p.setUInt64(ParamIterations, DefaultIterations);
|
||||
p.setUInt32(ParamVersion, MaxVersion);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,10 @@ public class KdfParameters extends VariantDictionary {
|
||||
kdfUUID = uuid;
|
||||
}
|
||||
|
||||
protected void setParamUUID() {
|
||||
setByteArray(ParamUUID, Types.UUIDtoBytes(kdfUUID));
|
||||
}
|
||||
|
||||
public static KdfParameters deserialize(byte[] data) throws IOException {
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(data);
|
||||
LEDataInputStream lis = new LEDataInputStream(bis);
|
||||
|
||||
@@ -152,11 +152,11 @@ public class Database {
|
||||
loadData(ctx, is, password, kfIs, status, debug, roundsFix);
|
||||
}
|
||||
|
||||
public void loadData(Context ctx, InputStream is, String password, InputStream kfIs, boolean debug) throws IOException, InvalidDBException {
|
||||
loadData(ctx, is, password, kfIs, null, debug, 0);
|
||||
public void loadData(Context ctx, InputStream is, String password, InputStream keyFileInputStream, boolean debug) throws IOException, InvalidDBException {
|
||||
loadData(ctx, is, password, keyFileInputStream, null, debug, 0);
|
||||
}
|
||||
|
||||
private void loadData(Context ctx, InputStream is, String password, InputStream kfIs, ProgressTaskUpdater progressTaskUpdater, boolean debug, long roundsFix) throws IOException, InvalidDBException {
|
||||
private void loadData(Context ctx, InputStream is, String password, InputStream keyFileInputStream, ProgressTaskUpdater progressTaskUpdater, boolean debug, long roundsFix) throws IOException, InvalidDBException {
|
||||
BufferedInputStream bis = new BufferedInputStream(is);
|
||||
|
||||
if ( ! bis.markSupported() ) {
|
||||
@@ -166,11 +166,11 @@ public class Database {
|
||||
// We'll end up reading 8 bytes to identify the header. Might as well use two extra.
|
||||
bis.mark(10);
|
||||
|
||||
Importer imp = ImporterFactory.createImporter(bis, debug);
|
||||
Importer databaseImporter = ImporterFactory.createImporter(bis, debug);
|
||||
|
||||
bis.reset(); // Return to the start
|
||||
|
||||
pm = imp.openDatabase(bis, password, kfIs, progressTaskUpdater, roundsFix);
|
||||
pm = databaseImporter.openDatabase(bis, password, keyFileInputStream, progressTaskUpdater, roundsFix);
|
||||
if ( pm != null ) {
|
||||
try {
|
||||
switch (pm.getVersion()) {
|
||||
@@ -392,7 +392,8 @@ public class Database {
|
||||
switch (getPwDatabase().getVersion()) {
|
||||
case V4:
|
||||
PwDatabaseV4 db = ((PwDatabaseV4) getPwDatabase());
|
||||
db.setKdfParameters(kdfEngine.getDefaultParameters());
|
||||
if (!db.getKdfParameters().kdfUUID.equals(kdfEngine.getDefaultParameters().kdfUUID))
|
||||
db.setKdfParameters(kdfEngine.getDefaultParameters());
|
||||
setNumberKeyEncryptionRounds(kdfEngine.getDefaultKeyRounds());
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -738,64 +738,4 @@ public class PwDatabaseV4 extends PwDatabase<PwGroupV4, PwEntryV4> {
|
||||
return filename.substring(0, lastExtDot);
|
||||
}
|
||||
|
||||
private class GroupHasCustomData extends GroupHandler<PwGroupV4> {
|
||||
|
||||
public boolean hasCustomData = false;
|
||||
|
||||
@Override
|
||||
public boolean operate(PwGroupV4 group) {
|
||||
if (group == null) {
|
||||
return true;
|
||||
}
|
||||
if (group.containsCustomData()) {
|
||||
hasCustomData = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class EntryHasCustomData extends EntryHandler<PwEntryV4> {
|
||||
|
||||
public boolean hasCustomData = false;
|
||||
|
||||
@Override
|
||||
public boolean operate(PwEntryV4 entry) {
|
||||
if (entry == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (entry.containsCustomData()) {
|
||||
hasCustomData = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMinKdbxVersion() {
|
||||
if (kdfParameters != null && !AesKdf.CIPHER_UUID.equals(kdfParameters.kdfUUID)) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32;
|
||||
}
|
||||
|
||||
if (publicCustomData.size() > 0) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32;
|
||||
}
|
||||
|
||||
EntryHasCustomData entryHandler = new EntryHasCustomData();
|
||||
GroupHasCustomData groupHandler = new GroupHasCustomData();
|
||||
|
||||
if (rootGroup == null ) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32_3;
|
||||
}
|
||||
rootGroup.preOrderTraverseTree(groupHandler, entryHandler);
|
||||
if (groupHandler.hasCustomData || entryHandler.hasCustomData) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32;
|
||||
}
|
||||
|
||||
return PwDbHeaderV4.FILE_VERSION_32_3;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,7 +42,7 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
public class PwDbHeaderV4 extends PwDbHeader {
|
||||
public static final int DBSIG_PRE2 = 0xB54BFB66;
|
||||
public static final int DBSIG_2 = 0xB54BFB67;
|
||||
|
||||
|
||||
private static final int FILE_VERSION_CRITICAL_MASK = 0xFFFF0000;
|
||||
public static final int FILE_VERSION_32_3 = 0x00030001;
|
||||
public static final int FILE_VERSION_32_4 = 0x00040000;
|
||||
@@ -92,12 +92,82 @@ public class PwDbHeaderV4 extends PwDbHeader {
|
||||
public CrsAlgorithm innerRandomStream;
|
||||
public long version;
|
||||
|
||||
public PwDbHeaderV4(PwDatabaseV4 d) {
|
||||
this.db = d;
|
||||
this.version = d.getMinKdbxVersion();
|
||||
public PwDbHeaderV4(PwDatabaseV4 databaseV4) {
|
||||
this.db = databaseV4;
|
||||
this.version = getMinKdbxVersion(databaseV4); // TODO move Only for writing
|
||||
this.masterSeed = new byte[32];
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
private class GroupHasCustomData extends GroupHandler<PwGroupV4> {
|
||||
|
||||
boolean hasCustomData = false;
|
||||
|
||||
@Override
|
||||
public boolean operate(PwGroupV4 group) {
|
||||
if (group == null) {
|
||||
return true;
|
||||
}
|
||||
if (group.containsCustomData()) {
|
||||
hasCustomData = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class EntryHasCustomData extends EntryHandler<PwEntryV4> {
|
||||
|
||||
boolean hasCustomData = false;
|
||||
|
||||
@Override
|
||||
public boolean operate(PwEntryV4 entry) {
|
||||
if (entry == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (entry.containsCustomData()) {
|
||||
hasCustomData = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private int getMinKdbxVersion(PwDatabaseV4 databaseV4) {
|
||||
// Return v4 if AES is not use
|
||||
if (databaseV4.getKdfParameters() != null) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32;
|
||||
}
|
||||
|
||||
// Return V4 if custom data are present
|
||||
if (databaseV4.containsPublicCustomData()) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32;
|
||||
}
|
||||
|
||||
EntryHasCustomData entryHandler = new EntryHasCustomData();
|
||||
GroupHasCustomData groupHandler = new GroupHasCustomData();
|
||||
|
||||
if (databaseV4.getRootGroup() == null ) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32_3;
|
||||
}
|
||||
databaseV4.getRootGroup().preOrderTraverseTree(groupHandler, entryHandler);
|
||||
if (groupHandler.hasCustomData || entryHandler.hasCustomData) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32;
|
||||
}
|
||||
|
||||
return PwDbHeaderV4.FILE_VERSION_32_3;
|
||||
}
|
||||
|
||||
/** Assumes the input stream is at the beginning of the .kdbx file
|
||||
* @param is
|
||||
* @throws IOException
|
||||
@@ -123,7 +193,7 @@ public class PwDbHeaderV4 extends PwDbHeader {
|
||||
throw new InvalidDBVersionException();
|
||||
}
|
||||
|
||||
version = lis.readUInt();
|
||||
version = lis.readUInt(); // Erase previous value
|
||||
if ( ! validVersion(version) ) {
|
||||
throw new InvalidDBVersionException();
|
||||
}
|
||||
@@ -279,8 +349,7 @@ public class PwDbHeaderV4 extends PwDbHeader {
|
||||
}
|
||||
|
||||
public static boolean matchesHeader(int sig1, int sig2) {
|
||||
return (sig1 == PWM_DBSIG_1) && ( (sig2 == DBSIG_2) );
|
||||
//return (sig1 == PWM_DBSIG_1) && ( (sig2 == DBSIG_PRE2) || (sig2 == DBSIG_2) ); // TODO verify add DBSIG_PRE2
|
||||
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{
|
||||
|
||||
@@ -23,6 +23,8 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.util.Log;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.kunzisoft.keepass.app.App;
|
||||
@@ -41,6 +43,8 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LoadDBRunnable extends RunnableOnFinish {
|
||||
private static final String TAG = LoadDBRunnable.class.getName();
|
||||
|
||||
private Uri mUri;
|
||||
private String mPass;
|
||||
private Uri mKey;
|
||||
@@ -69,42 +73,46 @@ public class LoadDBRunnable extends RunnableOnFinish {
|
||||
saveFileData(mUri, mKey);
|
||||
|
||||
} catch (ArcFourException e) {
|
||||
finish(false, mCtx.getString(R.string.error_arc4));
|
||||
catchError(e, R.string.error_arc4);
|
||||
return;
|
||||
} catch (InvalidPasswordException e) {
|
||||
finish(false, mCtx.getString(R.string.InvalidPassword));
|
||||
catchError(e, R.string.InvalidPassword);
|
||||
return;
|
||||
} catch (ContentFileNotFoundException e) {
|
||||
finish(false, mCtx.getString(R.string.file_not_found_content));
|
||||
catchError(e, R.string.file_not_found_content);
|
||||
return;
|
||||
} catch (FileNotFoundException e) {
|
||||
finish(false, mCtx.getString(R.string.file_not_found));
|
||||
catchError(e, R.string.file_not_found);
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Database can't be read", e);
|
||||
finish(false, e.getMessage());
|
||||
return;
|
||||
} catch (KeyFileEmptyException e) {
|
||||
finish(false, mCtx.getString(R.string.keyfile_is_empty));
|
||||
catchError(e, R.string.keyfile_is_empty);
|
||||
return;
|
||||
} catch (InvalidAlgorithmException e) {
|
||||
finish(false, mCtx.getString(R.string.invalid_algorithm));
|
||||
catchError(e, R.string.invalid_algorithm);
|
||||
return;
|
||||
} catch (InvalidKeyFileException e) {
|
||||
finish(false, mCtx.getString(R.string.keyfile_does_not_exist));
|
||||
catchError(e, R.string.keyfile_does_not_exist);
|
||||
return;
|
||||
} catch (InvalidDBSignatureException e) {
|
||||
finish(false, mCtx.getString(R.string.invalid_db_sig));
|
||||
catchError(e, R.string.invalid_db_sig);
|
||||
return;
|
||||
} catch (InvalidDBVersionException e) {
|
||||
finish(false, mCtx.getString(R.string.unsupported_db_version));
|
||||
catchError(e, R.string.unsupported_db_version);
|
||||
return;
|
||||
} catch (InvalidDBException e) {
|
||||
finish(false, mCtx.getString(R.string.error_invalid_db));
|
||||
catchError(e, R.string.error_invalid_db);
|
||||
return;
|
||||
} catch (OutOfMemoryError e) {
|
||||
finish(false, mCtx.getString(R.string.error_out_of_memory));
|
||||
String errorMessage = mCtx.getString(R.string.error_out_of_memory);
|
||||
Log.e(TAG, errorMessage, e);
|
||||
finish(false, errorMessage);
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Database can't be load", e);
|
||||
finish(false, e.getMessage());
|
||||
return;
|
||||
}
|
||||
@@ -112,6 +120,12 @@ public class LoadDBRunnable extends RunnableOnFinish {
|
||||
finish(true);
|
||||
}
|
||||
|
||||
private void catchError(Exception e, @StringRes int messageId) {
|
||||
String errorMessage = mCtx.getString(messageId);
|
||||
Log.e(TAG, errorMessage, e);
|
||||
finish(false, errorMessage);
|
||||
}
|
||||
|
||||
private void saveFileData(Uri uri, Uri key) {
|
||||
if ( ! mRememberKeyfile ) {
|
||||
key = null;
|
||||
|
||||
@@ -28,13 +28,11 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class ImporterFactory {
|
||||
public static Importer createImporter(InputStream is) throws InvalidDBSignatureException, IOException
|
||||
{
|
||||
public static Importer createImporter(InputStream is) throws InvalidDBSignatureException, IOException {
|
||||
return createImporter(is, false);
|
||||
}
|
||||
|
||||
public static Importer createImporter(InputStream is, boolean debug) throws InvalidDBSignatureException, IOException
|
||||
{
|
||||
public static Importer createImporter(InputStream is, boolean debug) throws InvalidDBSignatureException, IOException {
|
||||
int sig1 = LEDataInputStream.readInt(is);
|
||||
int sig2 = LEDataInputStream.readInt(is);
|
||||
|
||||
@@ -49,6 +47,5 @@ public class ImporterFactory {
|
||||
}
|
||||
|
||||
throw new InvalidDBSignatureException();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class ImporterV4 extends Importer {
|
||||
db.getBinPool().clear();
|
||||
|
||||
PwDbHeaderV4.HeaderAndHash hh = header.loadFromFile(inStream);
|
||||
version = header.version;
|
||||
version = header.getVersion();
|
||||
|
||||
hashOfHeader = hh.hash;
|
||||
pbHeader = hh.header;
|
||||
|
||||
@@ -88,25 +88,26 @@ public class PwDbHeaderOutputV4 extends PwDbHeaderOutput {
|
||||
|
||||
los.writeUInt(PwDbHeader.PWM_DBSIG_1);
|
||||
los.writeUInt(PwDbHeaderV4.DBSIG_2);
|
||||
los.writeUInt(header.version);
|
||||
los.writeUInt(header.getVersion());
|
||||
|
||||
|
||||
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.CipherID, Types.UUIDtoBytes(db.getDataCipher()));
|
||||
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.CompressionFlags, LEDataOutputStream.writeIntBuf(db.getCompressionAlgorithm().id));
|
||||
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.MasterSeed, header.masterSeed);
|
||||
|
||||
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.TransformSeed, header.getTransformSeed());
|
||||
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.TransformRounds, LEDataOutputStream.writeLongBuf(db.getNumberKeyEncryptionRounds()));
|
||||
} else {
|
||||
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.KdfParameters, KdfParameters.serialize(db.getKdfParameters()));
|
||||
// TODO verify serialize in all cases
|
||||
}
|
||||
|
||||
if (header.encryptionIV.length > 0) {
|
||||
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.EncryptionIV, header.encryptionIV);
|
||||
}
|
||||
|
||||
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.InnerRandomstreamKey, header.innerRandomStreamKey);
|
||||
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.StreamStartBytes, header.streamStartBytes);
|
||||
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.InnerRandomStreamID, LEDataOutputStream.writeIntBuf(header.innerRandomStream.id));
|
||||
@@ -139,7 +140,7 @@ public class PwDbHeaderOutputV4 extends PwDbHeaderOutput {
|
||||
}
|
||||
|
||||
private void writeHeaderFieldSize(int size) throws IOException {
|
||||
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
los.writeUShort(size);
|
||||
} else {
|
||||
los.writeInt(size);
|
||||
|
||||
@@ -101,7 +101,7 @@ public class PwDbV4Output extends PwDbOutput<PwDbHeaderV4> {
|
||||
header = outputHeader(mOS);
|
||||
|
||||
OutputStream osPlain;
|
||||
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
CipherOutputStream cos = attachStreamEncryptor(header, mOS);
|
||||
cos.write(header.streamStartBytes);
|
||||
|
||||
@@ -122,7 +122,7 @@ public class PwDbV4Output extends PwDbOutput<PwDbHeaderV4> {
|
||||
osXml = osPlain;
|
||||
}
|
||||
|
||||
if (header.version >= PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
if (header.getVersion() >= PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
PwDbInnerHeaderOutputV4 ihOut = new PwDbInnerHeaderOutputV4(mPM, header, osXml);
|
||||
ihOut.output();
|
||||
}
|
||||
@@ -258,7 +258,7 @@ public class PwDbV4Output extends PwDbOutput<PwDbHeaderV4> {
|
||||
writeObject(PwDatabaseV4XML.ElemLastSelectedGroup, mPM.getLastSelectedGroup());
|
||||
writeObject(PwDatabaseV4XML.ElemLastTopVisibleGroup, mPM.getLastTopVisibleGroup());
|
||||
|
||||
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
writeBinPool();
|
||||
}
|
||||
writeList(PwDatabaseV4XML.ElemCustomData, mPM.getCustomData());
|
||||
@@ -296,7 +296,7 @@ public class PwDbV4Output extends PwDbOutput<PwDbHeaderV4> {
|
||||
KdfEngine kdf = KdfFactory.get(mPM.getKdfParameters());
|
||||
kdf.randomize(mPM.getKdfParameters());
|
||||
|
||||
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
header.innerRandomStream = CrsAlgorithm.Salsa20;
|
||||
header.innerRandomStreamKey = new byte[32];
|
||||
} else {
|
||||
@@ -310,7 +310,7 @@ public class PwDbV4Output extends PwDbOutput<PwDbHeaderV4> {
|
||||
throw new PwDbOutputException("Invalid random cipher");
|
||||
}
|
||||
|
||||
if ( header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
if ( header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
random.nextBytes(header.streamStartBytes);
|
||||
}
|
||||
|
||||
@@ -463,7 +463,7 @@ public class PwDbV4Output extends PwDbOutput<PwDbHeaderV4> {
|
||||
}
|
||||
|
||||
private void writeObject(String name, Date value) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
writeObject(name, PwDatabaseV4XML.dateFormatter.get().format(value));
|
||||
} else {
|
||||
DateTime dt = new DateTime(value);
|
||||
|
||||
Reference in New Issue
Block a user