package de.tu.dresden.dud.dc.KeyGenerators;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import org.apache.log4j.Logger;
import de.tu.dresden.dud.dc.ParticipantMgmntInfo;
import de.tu.dresden.dud.dc.Util;
import de.tu.dresden.dud.dc.WorkCycle.WorkCycle;
import de.tu.dresden.dud.dc.WorkCycle.WorkCycleManager;
import de.tu.dresden.dud.dc.WorkCycle.WorkCycleSending;
/**
* K_{ij} = a_{ij}^t + b_{ij}^t * K_{ij}^{t-1} + e * I_{i}^{t-1}
* @author klobs
*
*/
public class KeyGeneratorProbabFailStop extends KeyGeneratorNormalDC {
// Logging
private static Logger log = Logger.getLogger(KeyGeneratorFailStopWorkCycle.class);
private HashMap<ParticipantMgmntInfo, byte[]> lastKeys = new HashMap<ParticipantMgmntInfo, byte[]>();
public KeyGeneratorProbabFailStop(WorkCycleManager wcm) {
super(wcm);
if (wcm.getMessageLengthMode() == WorkCycleManager.MESSAGE_LENGTHS_VARIABLE){
log.warn("Server uses probabilistic fail stop together with variable message lengths. This is highly discurraged, as it might have influences on security of the participants.");
}
actualKeyGeneratingMethod = KeyGenerator.KGMETHOD_PROBAB_FAIL_STOP;
}
/**
* This method generates "random" keys by using AES in ECB mode and using
* the exchanged DH-Key as IV.
*
* Before adding, it checks whether there are enough different participant
* keys to be added.
*
* @param length length in bytes. Must be multiple of 4 (bytes).
* @return
*/
@Override
protected synchronized byte[] calcKeysMain(int length) {
if ((length % 4) != 0) {
log.error("Required key length is no multiple of 4");
}
byte[] b = new byte[length];
Arrays.fill(b, (byte) 0);
ParticipantMgmntInfo pmi = null;
// apl = active participants list
LinkedList<ParticipantMgmntInfo> apl = assocWorkCycleManag
.getAssocParticipantManager().getActivePartExtKeysMgmtInfo();
Iterator<ParticipantMgmntInfo> i = apl.iterator();
ArrayList<byte[]> bl = new ArrayList<byte[]>(apl.size());
if (apl.size() < WorkCycle.WC_MIN_ACTIVE_KEYS) {
log.error("There are not enough active keys.");
// TODO what is the resulting action we should perform?
}
while (i.hasNext()) {
pmi = i.next();
byte[] cr = null;
cr = calcKeysAESPRNGCaller(workcycleNumber, currentRound, pmi,
length);
if(lastKeys.get(pmi) != null){
cr = Util.mergeDCwise(
cr,
calcKeysBPartCaller(workcycleNumber, currentRound, pmi,
length), WorkCycleSending.MODULUS);
cr = Util.mergeDCwise(cr, calcKeysEPart(pmi, length),
WorkCycleSending.MODULUS);
}
lastKeys.put(pmi, cr);
// Finally calculate the inverse, when needed.
if (pmi.getKey().getInverse()) {
cr = inverseKey(cr);
}
bl.add(cr);
}
for (byte[] w : bl) {
b = Util.mergeDCwise(b, w, WorkCycleSending.MODULUS);
}
return b;
}
private byte[] calcKeysBPartCaller(long workcycleNumber, int currentRound,
ParticipantMgmntInfo pmi, int length) {
BigInteger b_ij = null;
byte[] cr = new byte[0];
int prn = length / 16;
int rrn = length % 16;
if (rrn > 0)
prn++;
for (int i = 0; i<prn; i++) {
cr = Util.concatenate(cr, calcKeysAESPRNG(workcycleNumber, currentRound, Util.getBytesByOffset(pmi.getKey()
.getCalculatedSecret().toByteArray(), 0, 32), Util.getBytesByOffset(pmi.getKey()
.getCalculatedSecret().toByteArray(), 36, 4), i));
}
b_ij = new BigInteger(cr);
b_ij = b_ij.multiply(new BigInteger(lastKeys.get(pmi)));
cr = b_ij.toByteArray();
if (cr.length > length)
cr = Util.getLastBytes(cr, length);
if (cr.length < length)
cr = Util.fillAndMergeSending(cr, new byte[length]);
return cr;
}
private byte[] calcKeysEPart(ParticipantMgmntInfo pmi, int length){
byte[] cr = new byte[length];
byte[] oldMessages = getCurrentWorkCycle().getMessageBin();
BigInteger e = new BigInteger(Util.getBytesByOffset(pmi.getKey()
.getCalculatedSecret().toByteArray(), 40, 40));
if (oldMessages.length < lastKeys.get(pmi).length)
log.debug("Not enough old message material - ommitting e part!");
else {
BigInteger i_tminus1 = new BigInteger(Util.getLastBytes(
oldMessages, lastKeys.get(pmi).length));
e.multiply(i_tminus1);
cr = e.toByteArray();
if (cr.length > length)
cr = Util.getLastBytes(cr, length);
if (cr.length < length)
cr = Util.fillAndMergeSending(cr, new byte[length]);
}
return cr;
}
}