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
public KdfParameters getDefaultParameters() {
KdfParameters p = new KdfParameters(uuid);
p.setParamUUID();
p.setUInt32(ParamRounds, DEFAULT_ROUNDS);
return p;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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{

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);