package cologne.eck.peafactory.crypto.kdf;
import cologne.eck.all_peas.data.Attachments;
import cologne.eck.all_peas.data.PeaProperties;
import cologne.eck.crypto.Blake2b_1;
import cologne.eck.crypto.Catena;
import cologne.eck.crypto.CatenaBRG;
import cologne.eck.crypto.CatenaDBG;
/*
* PeaFactory - Production of Password Encryption Archives
* Copyright (C) 2015 Axel von dem Bruch
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* See: http://www.gnu.org/licenses/gpl-2.0.html
* You should have received a copy of the GNU General Public License
* along with this library.
*/
/**
* Password Hashing Scheme Catena (v3) version v3.2 as key derivation function
*/
import cologne.eck.peafactory.crypto.CipherStuff;
import cologne.eck.tools.Comparator;
public class CatenaKDF extends KeyDerivation {
// Default setting:
private static String versionID = "Dragonfly-Full";
private static int lambdaDragonfly = 2;
private static int garlicDragonfly = 18;
private static int lambdaButterfly = 4;
private static int garlicButterfly = 14;
public CatenaKDF() {
if ((versionID.equals("Dragonfly-Full")) || (versionID.equals("Dragonfly"))){
settCost(lambdaDragonfly);
setmCost(garlicDragonfly);
setArg3(0);
setArg4(0);
setArg5(0);
setArg6(0);
setVersionString(versionID);
setKdf(this);
} else if ((versionID.equals("Butterfly-Full")) || (versionID.equals("Butterfly"))){
settCost(lambdaButterfly);
setmCost(garlicButterfly);
setArg3(0);
setArg4(0);
setArg5(0);
setArg6(0);
setVersionString(versionID);
setKdf(this);
} else {
System.err.println("Invalid Catena version ID");
}
setKdf(this);
}
@Override
public byte[] deriveKey(byte[] pswMaterial) {
Catena cat = null;
if (getVersionString().equals("Dragonfly-Full")){
cat = new CatenaBRG(false, true); // not fast, wipe password
cat.setFast(false);
} else if (getVersionString().equals("Butterfly-Full")){
cat = new CatenaDBG(false, true);
cat.setFast(false);
} else if (getVersionString().equals("Butterfly")){
cat = new CatenaDBG(true, true);
cat.setFast(true);
cat.setFastHash(new Blake2b_1());
} else if (getVersionString().equals("Dragonfly")){
cat = new CatenaBRG(true, true);
cat.setFast(true);
cat.setFastHash(new Blake2b_1());
} else {
throw new IllegalArgumentException("invalid version ID");
}
byte[] key = null;
// figure out if key file property:
byte[] testKeyFileProperty = Attachments.getProgramRandomBytes();
byte[] toTest = "PEAFACTORY".getBytes(PeaProperties.getCharset());
byte[] test = new byte[toTest.length];
System.arraycopy(testKeyFileProperty, 0, test, 0, test.length);
if (Comparator.compare(test, toTest) == true){ // no key file property
// use salt as salt and as public input:
// there is no need to keep a value secret (prevent cache-timing attacks)
//Help.printBytes("no key file", KeyDerivation.getSalt());
key = new byte[CipherStuff.getKeySize()];
cat.deriveKey(
pswMaterial,
KeyDerivation.getSalt(), // used as salt for initial hash
KeyDerivation.getSalt(), // used as public input, vulnerable to cache-timing attacks
getExtraValues(),
gettCost(), getmCost(), getmCost(), // lambda, minGarlic, garlic
0, key); // key id = 0
} else { // key file property
// prevent cache-timing attacks on salt (pepper)
//Help.printBytes("key file, attachedSalt", KeyDerivation.getAttachedSalt());
//Help.printBytes("key file, programRandomBytes", Attachments.getProgramRandomBytes());
key = new byte[CipherStuff.getKeySize()];
cat.deriveKey(
pswMaterial,
Attachments.getProgramRandomBytes(), // used as salt/pepper for initial hash, kept secret
KeyDerivation.getSalt(), // used as public input, vulnerable to cache-timing attacks
getExtraValues(),
gettCost(), getmCost(), getmCost(), // lambda, minGarlic, garlic
0, key); // key id = 0
}
// printInfos(true);
return key;
}
@Override
public String getName() {
return "Catena";
}
/**
* @return the versionID
*/
public static String getVersionID() {
return versionID;
}
/**
* @param versionID the versionID to set
*/
public static void setVersionID(String versionID) {
CatenaKDF.versionID = versionID;
}
/**
* @return the lambdaDragonfly
*/
public static int getDragonflyLambda() {
return lambdaDragonfly;
}
/**
* @param lambdaDragonfly the lambdaDragonfly to set
*/
public static void setLambdaDragonfly(int lambda) {
CatenaKDF.lambdaDragonfly = lambda;
}
/**
* @return the garlicDragonfly
*/
public static int getGarlicDragonfly() {
return garlicDragonfly;
}
/**
* @param garlicDragonfly the garlicDragonfly to set
*/
public static void setGarlicDragonfly(int garlic) {
CatenaKDF.garlicDragonfly = garlic;
}
/**
* @return the lambdaButterfly
*/
public static int getLambdaButterfly() {
return lambdaButterfly;
}
/**
* @param lambdaButterfly the lambdaButterfly to set
*/
public static void setLambdaButterfly(int lambdaButterfly) {
CatenaKDF.lambdaButterfly = lambdaButterfly;
}
/**
* @return the garlicButterfly
*/
public static int getGarlicButterfly() {
return garlicButterfly;
}
/**
* @param garlicButterfly the garlicButterfly to set
*/
public static void setGarlicButterfly(int garlicButterfly) {
CatenaKDF.garlicButterfly = garlicButterfly;
}
/**
* Get the required memory for the key derivation function.
* Returns the required memory in MiB.
*
* @return memory requirement in MiB
*/
@Override
public int getMemoryRequirement() {
if ((versionID.equals("Dragonfly-Full")) || (versionID.equals("Dragonfly"))){
return (1 << garlicDragonfly) * 64 / (1024*1024); // 64 is digest output size of Blake2b
} else if ((versionID.equals("Butterfly-Full")) || (versionID.equals("Butterfly"))){
return ( (1 << garlicButterfly) + (1 << (garlicButterfly -1) ) ) * 64 / (1024*1024); // 64 is digest output size of Blake2b
} else {
System.err.println("Unknown version of Catena");
return 0;
}
}
}