Register UUID of database version in KDF parameter

This commit is contained in:
J-Jamet
2018-05-11 19:19:16 +02:00
parent fea7af6910
commit 6afffb7245
11 changed files with 131 additions and 102 deletions

View File

@@ -58,6 +58,8 @@ public class AesKdf extends KdfEngine {
@Override @Override
public KdfParameters getDefaultParameters() { public KdfParameters getDefaultParameters() {
KdfParameters p = new KdfParameters(uuid); KdfParameters p = new KdfParameters(uuid);
p.setParamUUID();
p.setUInt32(ParamRounds, DEFAULT_ROUNDS); p.setUInt32(ParamRounds, DEFAULT_ROUNDS);
return p; return p;

View File

@@ -79,10 +79,11 @@ public class Argon2Kdf extends KdfEngine {
public KdfParameters getDefaultParameters() { public KdfParameters getDefaultParameters() {
KdfParameters p = new KdfParameters(uuid); KdfParameters p = new KdfParameters(uuid);
p.setUInt32(ParamVersion, MaxVersion); p.setParamUUID();
p.setUInt64(ParamMemory, DefaultMemory);
p.setUInt32(ParamParallelism, DefaultParallelism); p.setUInt32(ParamParallelism, DefaultParallelism);
p.setUInt64(ParamMemory, DefaultMemory);
p.setUInt64(ParamIterations, DefaultIterations); p.setUInt64(ParamIterations, DefaultIterations);
p.setUInt32(ParamVersion, MaxVersion);
return p; return p;
} }

View File

@@ -39,6 +39,10 @@ public class KdfParameters extends VariantDictionary {
kdfUUID = uuid; kdfUUID = uuid;
} }
protected void setParamUUID() {
setByteArray(ParamUUID, Types.UUIDtoBytes(kdfUUID));
}
public static KdfParameters deserialize(byte[] data) throws IOException { public static KdfParameters deserialize(byte[] data) throws IOException {
ByteArrayInputStream bis = new ByteArrayInputStream(data); ByteArrayInputStream bis = new ByteArrayInputStream(data);
LEDataInputStream lis = new LEDataInputStream(bis); LEDataInputStream lis = new LEDataInputStream(bis);

View File

@@ -152,11 +152,11 @@ public class Database {
loadData(ctx, is, password, kfIs, status, debug, roundsFix); loadData(ctx, is, password, kfIs, status, debug, roundsFix);
} }
public void loadData(Context ctx, InputStream is, String password, InputStream kfIs, boolean debug) throws IOException, InvalidDBException { public void loadData(Context ctx, InputStream is, String password, InputStream keyFileInputStream, boolean debug) throws IOException, InvalidDBException {
loadData(ctx, is, password, kfIs, null, debug, 0); 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); BufferedInputStream bis = new BufferedInputStream(is);
if ( ! bis.markSupported() ) { 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. // We'll end up reading 8 bytes to identify the header. Might as well use two extra.
bis.mark(10); bis.mark(10);
Importer imp = ImporterFactory.createImporter(bis, debug); Importer databaseImporter = ImporterFactory.createImporter(bis, debug);
bis.reset(); // Return to the start 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 ) { if ( pm != null ) {
try { try {
switch (pm.getVersion()) { switch (pm.getVersion()) {
@@ -392,6 +392,7 @@ public class Database {
switch (getPwDatabase().getVersion()) { switch (getPwDatabase().getVersion()) {
case V4: case V4:
PwDatabaseV4 db = ((PwDatabaseV4) getPwDatabase()); PwDatabaseV4 db = ((PwDatabaseV4) getPwDatabase());
if (!db.getKdfParameters().kdfUUID.equals(kdfEngine.getDefaultParameters().kdfUUID))
db.setKdfParameters(kdfEngine.getDefaultParameters()); db.setKdfParameters(kdfEngine.getDefaultParameters());
setNumberKeyEncryptionRounds(kdfEngine.getDefaultKeyRounds()); setNumberKeyEncryptionRounds(kdfEngine.getDefaultKeyRounds());
break; break;

View File

@@ -738,64 +738,4 @@ public class PwDatabaseV4 extends PwDatabase<PwGroupV4, PwEntryV4> {
return filename.substring(0, lastExtDot); 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;
}
} }

View File

@@ -92,12 +92,82 @@ public class PwDbHeaderV4 extends PwDbHeader {
public CrsAlgorithm innerRandomStream; public CrsAlgorithm innerRandomStream;
public long version; public long version;
public PwDbHeaderV4(PwDatabaseV4 d) { public PwDbHeaderV4(PwDatabaseV4 databaseV4) {
this.db = d; this.db = databaseV4;
this.version = d.getMinKdbxVersion(); this.version = getMinKdbxVersion(databaseV4); // TODO move Only for writing
this.masterSeed = new byte[32]; 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 /** Assumes the input stream is at the beginning of the .kdbx file
* @param is * @param is
* @throws IOException * @throws IOException
@@ -123,7 +193,7 @@ public class PwDbHeaderV4 extends PwDbHeader {
throw new InvalidDBVersionException(); throw new InvalidDBVersionException();
} }
version = lis.readUInt(); version = lis.readUInt(); // Erase previous value
if ( ! validVersion(version) ) { if ( ! validVersion(version) ) {
throw new InvalidDBVersionException(); throw new InvalidDBVersionException();
} }
@@ -279,8 +349,7 @@ public class PwDbHeaderV4 extends PwDbHeader {
} }
public static boolean matchesHeader(int sig1, int sig2) { 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{ public static byte[] computeHeaderHmac(byte[] header, byte[] key) throws IOException{

View File

@@ -23,6 +23,8 @@ import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.StringRes;
import android.util.Log;
import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.R;
import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.app.App;
@@ -41,6 +43,8 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
public class LoadDBRunnable extends RunnableOnFinish { public class LoadDBRunnable extends RunnableOnFinish {
private static final String TAG = LoadDBRunnable.class.getName();
private Uri mUri; private Uri mUri;
private String mPass; private String mPass;
private Uri mKey; private Uri mKey;
@@ -69,42 +73,46 @@ public class LoadDBRunnable extends RunnableOnFinish {
saveFileData(mUri, mKey); saveFileData(mUri, mKey);
} catch (ArcFourException e) { } catch (ArcFourException e) {
finish(false, mCtx.getString(R.string.error_arc4)); catchError(e, R.string.error_arc4);
return; return;
} catch (InvalidPasswordException e) { } catch (InvalidPasswordException e) {
finish(false, mCtx.getString(R.string.InvalidPassword)); catchError(e, R.string.InvalidPassword);
return; return;
} catch (ContentFileNotFoundException e) { } catch (ContentFileNotFoundException e) {
finish(false, mCtx.getString(R.string.file_not_found_content)); catchError(e, R.string.file_not_found_content);
return; return;
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
finish(false, mCtx.getString(R.string.file_not_found)); catchError(e, R.string.file_not_found);
return; return;
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Database can't be read", e);
finish(false, e.getMessage()); finish(false, e.getMessage());
return; return;
} catch (KeyFileEmptyException e) { } catch (KeyFileEmptyException e) {
finish(false, mCtx.getString(R.string.keyfile_is_empty)); catchError(e, R.string.keyfile_is_empty);
return; return;
} catch (InvalidAlgorithmException e) { } catch (InvalidAlgorithmException e) {
finish(false, mCtx.getString(R.string.invalid_algorithm)); catchError(e, R.string.invalid_algorithm);
return; return;
} catch (InvalidKeyFileException e) { } catch (InvalidKeyFileException e) {
finish(false, mCtx.getString(R.string.keyfile_does_not_exist)); catchError(e, R.string.keyfile_does_not_exist);
return; return;
} catch (InvalidDBSignatureException e) { } catch (InvalidDBSignatureException e) {
finish(false, mCtx.getString(R.string.invalid_db_sig)); catchError(e, R.string.invalid_db_sig);
return; return;
} catch (InvalidDBVersionException e) { } catch (InvalidDBVersionException e) {
finish(false, mCtx.getString(R.string.unsupported_db_version)); catchError(e, R.string.unsupported_db_version);
return; return;
} catch (InvalidDBException e) { } catch (InvalidDBException e) {
finish(false, mCtx.getString(R.string.error_invalid_db)); catchError(e, R.string.error_invalid_db);
return; return;
} catch (OutOfMemoryError e) { } 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; return;
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Database can't be load", e);
finish(false, e.getMessage()); finish(false, e.getMessage());
return; return;
} }
@@ -112,6 +120,12 @@ public class LoadDBRunnable extends RunnableOnFinish {
finish(true); 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) { private void saveFileData(Uri uri, Uri key) {
if ( ! mRememberKeyfile ) { if ( ! mRememberKeyfile ) {
key = null; key = null;

View File

@@ -28,13 +28,11 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
public class ImporterFactory { 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); 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 sig1 = LEDataInputStream.readInt(is);
int sig2 = LEDataInputStream.readInt(is); int sig2 = LEDataInputStream.readInt(is);
@@ -49,6 +47,5 @@ public class ImporterFactory {
} }
throw new InvalidDBSignatureException(); throw new InvalidDBSignatureException();
} }
} }

View File

@@ -108,7 +108,7 @@ public class ImporterV4 extends Importer {
db.getBinPool().clear(); db.getBinPool().clear();
PwDbHeaderV4.HeaderAndHash hh = header.loadFromFile(inStream); PwDbHeaderV4.HeaderAndHash hh = header.loadFromFile(inStream);
version = header.version; version = header.getVersion();
hashOfHeader = hh.hash; hashOfHeader = hh.hash;
pbHeader = hh.header; pbHeader = hh.header;

View File

@@ -88,25 +88,26 @@ public class PwDbHeaderOutputV4 extends PwDbHeaderOutput {
los.writeUInt(PwDbHeader.PWM_DBSIG_1); los.writeUInt(PwDbHeader.PWM_DBSIG_1);
los.writeUInt(PwDbHeaderV4.DBSIG_2); los.writeUInt(PwDbHeaderV4.DBSIG_2);
los.writeUInt(header.version); los.writeUInt(header.getVersion());
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.CipherID, Types.UUIDtoBytes(db.getDataCipher())); writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.CipherID, Types.UUIDtoBytes(db.getDataCipher()));
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.CompressionFlags, LEDataOutputStream.writeIntBuf(db.getCompressionAlgorithm().id)); writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.CompressionFlags, LEDataOutputStream.writeIntBuf(db.getCompressionAlgorithm().id));
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.MasterSeed, header.masterSeed); 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.TransformSeed, header.getTransformSeed());
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.TransformRounds, LEDataOutputStream.writeLongBuf(db.getNumberKeyEncryptionRounds())); writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.TransformRounds, LEDataOutputStream.writeLongBuf(db.getNumberKeyEncryptionRounds()));
} else { } else {
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.KdfParameters, KdfParameters.serialize(db.getKdfParameters())); writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.KdfParameters, KdfParameters.serialize(db.getKdfParameters()));
// TODO verify serialize in all cases
} }
if (header.encryptionIV.length > 0) { if (header.encryptionIV.length > 0) {
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.EncryptionIV, header.encryptionIV); 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.InnerRandomstreamKey, header.innerRandomStreamKey);
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.StreamStartBytes, header.streamStartBytes); writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.StreamStartBytes, header.streamStartBytes);
writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.InnerRandomStreamID, LEDataOutputStream.writeIntBuf(header.innerRandomStream.id)); 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 { 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); los.writeUShort(size);
} else { } else {
los.writeInt(size); los.writeInt(size);

View File

@@ -101,7 +101,7 @@ public class PwDbV4Output extends PwDbOutput<PwDbHeaderV4> {
header = outputHeader(mOS); header = outputHeader(mOS);
OutputStream osPlain; OutputStream osPlain;
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) { if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) {
CipherOutputStream cos = attachStreamEncryptor(header, mOS); CipherOutputStream cos = attachStreamEncryptor(header, mOS);
cos.write(header.streamStartBytes); cos.write(header.streamStartBytes);
@@ -122,7 +122,7 @@ public class PwDbV4Output extends PwDbOutput<PwDbHeaderV4> {
osXml = osPlain; 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); PwDbInnerHeaderOutputV4 ihOut = new PwDbInnerHeaderOutputV4(mPM, header, osXml);
ihOut.output(); ihOut.output();
} }
@@ -258,7 +258,7 @@ public class PwDbV4Output extends PwDbOutput<PwDbHeaderV4> {
writeObject(PwDatabaseV4XML.ElemLastSelectedGroup, mPM.getLastSelectedGroup()); writeObject(PwDatabaseV4XML.ElemLastSelectedGroup, mPM.getLastSelectedGroup());
writeObject(PwDatabaseV4XML.ElemLastTopVisibleGroup, mPM.getLastTopVisibleGroup()); writeObject(PwDatabaseV4XML.ElemLastTopVisibleGroup, mPM.getLastTopVisibleGroup());
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) { if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) {
writeBinPool(); writeBinPool();
} }
writeList(PwDatabaseV4XML.ElemCustomData, mPM.getCustomData()); writeList(PwDatabaseV4XML.ElemCustomData, mPM.getCustomData());
@@ -296,7 +296,7 @@ public class PwDbV4Output extends PwDbOutput<PwDbHeaderV4> {
KdfEngine kdf = KdfFactory.get(mPM.getKdfParameters()); KdfEngine kdf = KdfFactory.get(mPM.getKdfParameters());
kdf.randomize(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.innerRandomStream = CrsAlgorithm.Salsa20;
header.innerRandomStreamKey = new byte[32]; header.innerRandomStreamKey = new byte[32];
} else { } else {
@@ -310,7 +310,7 @@ public class PwDbV4Output extends PwDbOutput<PwDbHeaderV4> {
throw new PwDbOutputException("Invalid random cipher"); 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); 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 { 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)); writeObject(name, PwDatabaseV4XML.dateFormatter.get().format(value));
} else { } else {
DateTime dt = new DateTime(value); DateTime dt = new DateTime(value);