Commit 75eec6f5 by Enrico Pozzobon

fixes for tests, .s to .S, other stuff

parent 67bcb93e
...@@ -172,10 +172,17 @@ def main(argv): ...@@ -172,10 +172,17 @@ def main(argv):
if n not in include_list: if n not in include_list:
continue continue
# Find date of last modification in directory
st_mtime = 0
for root, dirs, filess in os.walk(d):
for name in filess:
path = os.path.join(root, name)
st_mtime = max(st_mtime, os.stat(path).st_mtime)
# Put all in a tuple and count # Put all in a tuple and count
files.append((t, d, n)) files.append((t, d, n, st_mtime))
# For testing, we only do the first 1 # Uncomment next line for testing, if we only want to do 1
# files = files[:1] # files = files[:1]
print("%d algorithms will be compiled" % len(files)) print("%d algorithms will be compiled" % len(files))
...@@ -184,41 +191,29 @@ def main(argv): ...@@ -184,41 +191,29 @@ def main(argv):
print() print()
# Write a script that executes all the tests one after the other # Build all found algorithms
test_script_path = os.path.join(build_root_dir, "test_all.sh") for i, (t, d, name, st_mtime) in enumerate(files):
with open(test_script_path, 'w') as test_script: print()
test_script.write("#!/bin/sh\n") print(d)
test_script.write("mkdir -p logs\n") try:
test_script.write("mkdir -p measurements\n") build_dir = os.path.join(build_root_dir, name)
for i, (t, d, name) in enumerate(files):
print() b = build(d, template_dir, build_dir)
print(d)
try: if b is None:
build_dir = os.path.join(build_root_dir, name) continue
b = build(d, template_dir, build_dir)
if b is None: shutil.copyfile(t, os.path.join(b, 'LWC_AEAD_KAT.txt'))
continue
test_script.write( mdate_path = os.path.join(build_dir, 'cipher_mtime.txt')
"\n\necho \"TEST NUMBER %03d: TESTING %s\"\n" % (i, d)) with open(mdate_path, 'wt') as mdate_file:
test_script.write( print(int(st_mtime), file=mdate_file)
"python3 -u %s %s %s 2> %s | tee %s\n" % (
os.path.join(template_dir, 'test'), print("COMPILATION SUCCESS FOR %s" % d)
os.path.join(b, 'LWC_AEAD_KAT.txt'), except Exception as ex:
b, print("COMPILATION FAILED FOR %s" % d)
os.path.join(b, 'test_stderr.log'), print(ex)
os.path.join(b, 'test_stdout.log'))
)
shutil.copyfile(t, os.path.join(b, 'LWC_AEAD_KAT.txt'))
print("COMPILATION SUCCESS FOR %s" % d)
except Exception as ex:
print("COMPILATION FAILED FOR %s" % d)
print(ex)
st = os.stat(test_script_path)
os.chmod(test_script_path, st.st_mode | stat.S_IEXEC)
print()
print() print()
......
...@@ -18,6 +18,10 @@ td { ...@@ -18,6 +18,10 @@ td {
margin: 0px; margin: 0px;
} }
td.schedule-path, th.schedule-path {
width: 20em;
}
.iconButton { .iconButton {
height: 2.2em; height: 2.2em;
width: 2.2em; width: 2.2em;
...@@ -86,7 +90,7 @@ let menuView = null; ...@@ -86,7 +90,7 @@ let menuView = null;
function init() { function init() {
scheduleTable.style.display = 'none'; scheduleTable.style.display = 'none';
document.body.appendChild(scheduleTable); document.body.appendChild(scheduleTable);
scheduleTable.appendChild(makeRow(['ID', 'Created At', 'Path', 'Template', 'State', 'Actions'], 'th')); scheduleTable.appendChild(makeScheduleRow(['ID', 'Created At', 'Path', 'Template', 'State', 'Actions'], 'th'));
document.addEventListener("keydown", function(event) { document.addEventListener("keydown", function(event) {
if (event.which == 27) { if (event.which == 27) {
...@@ -132,7 +136,7 @@ function onStatusGet(status) { ...@@ -132,7 +136,7 @@ function onStatusGet(status) {
// Add rows for new incoming tasks // Add rows for new incoming tasks
for (const i of newTasksIds) { for (const i of newTasksIds) {
const row = makeRow([i, '', '', '', '', '']); const row = makeScheduleRow([i, '', '', '', '', '']);
const s = { id: i, row }; const s = { id: i, row };
schedule[s.id] = s; schedule[s.id] = s;
...@@ -168,7 +172,8 @@ function onStatusGet(status) { ...@@ -168,7 +172,8 @@ function onStatusGet(status) {
} }
const row = schedule[s.id].row; const row = schedule[s.id].row;
row.cells[1].innerText = s.added; row.cells[1].innerText = s.added.substr(0, 16)
+ '\n' + s.added.substr(17);
row.cells[2].innerText = s.path; row.cells[2].innerText = s.path;
row.cells[3].innerText = s.template; row.cells[3].innerText = s.template;
row.cells[4].innerText = s.state; row.cells[4].innerText = s.state;
...@@ -214,7 +219,7 @@ function showJobMenu(event, jobId) { ...@@ -214,7 +219,7 @@ function showJobMenu(event, jobId) {
menuView.appendChild(makeEntry('🗎 View Logs', () => viewJobLogs(jobId))); menuView.appendChild(makeEntry('🗎 View Logs', () => viewJobLogs(jobId)));
if (st == 'SUCCESSFUL') { if (st == 'SUCCESSFUL') {
menuView.appendChild(makeEntry('↓ Download results zip', () => getResultZip(jobId))); menuView.appendChild(makeEntry('↓ Download results zip', () => getResultZip(jobId)));
menuView.appendChild(makeEntry('↓ Download results sql', () => getResultSql(jobId))); menuView.appendChild(makeEntry('↓ Download results JSON', () => getResultJSON(jobId)));
} }
function onLayout() { function onLayout() {
...@@ -222,15 +227,17 @@ function showJobMenu(event, jobId) { ...@@ -222,15 +227,17 @@ function showJobMenu(event, jobId) {
// we have the size of the menu view, now we have to place it close to // we have the size of the menu view, now we have to place it close to
// the bounding box of the button that was clicked, without putting it // the bounding box of the button that was clicked, without putting it
// ouside the screen: // ouside the screen:
const a = document.body.getBoundingClientRect();
const b = menuView.getBoundingClientRect(); const b = menuView.getBoundingClientRect();
const r = event.target.getBoundingClientRect(); const r = event.target.getBoundingClientRect();
const vh = window.innerHeight || document.documentElement.clientHeight; const vh = window.innerHeight || document.documentElement.clientHeight;
const vw = window.innerWidth || document.documentElement.clientWidth; const vw = window.innerWidth || document.documentElement.clientWidth;
menuView.style.width = b.width + 'px'; menuView.style.width = b.width + 'px';
menuView.style.height = b.height + 'px'; menuView.style.height = b.height + 'px';
menuView.style.top = Math.min(r.y, vh - b.height - 20) + 'px'; menuView.style.top = (-a.top + Math.min(r.y, vh - b.height - 20)) + 'px';
menuView.style.left = Math.min(r.x, vw - b.width - 20) + 'px'; menuView.style.left = (-a.left + Math.min(r.x, vw - b.width - 20)) + 'px';
}; };
setTimeout(() => { setTimeout(() => {
...@@ -274,6 +281,11 @@ function getResultZip(jobId) { ...@@ -274,6 +281,11 @@ function getResultZip(jobId) {
} }
function getResultJSON(jobId) {
window.open('/results/' + jobId + '/results.json');
}
function viewJobLogs(jobId) { function viewJobLogs(jobId) {
const logIds = [0,1,2,3]; const logIds = [0,1,2,3];
const logNames = ['make stdout', 'make stderr', 'test stdout', 'test stderr']; const logNames = ['make stdout', 'make stderr', 'test stdout', 'test stderr'];
...@@ -365,16 +377,16 @@ function requestStatus() { ...@@ -365,16 +377,16 @@ function requestStatus() {
} }
function makeCell(text, tagName='td') { function makeScheduleRow(list, cellTagName='td') {
const cell = document.createElement(tagName);
cell.appendChild(document.createTextNode(text));
return cell;
};
function makeRow(list, cellTagName='td') {
const headerRow = document.createElement('tr'); const headerRow = document.createElement('tr');
(list.map((n) => makeCell(n, cellTagName))).forEach((n) => headerRow.appendChild(n)); const classes = ['schedule-id', 'schedule-date', 'schedule-path',
'schedule-template', 'schedule-state', 'schedule-actions'];
(list.map((text, idx) => {
const cell = document.createElement(cellTagName);
cell.className = classes[idx];
cell.appendChild(document.createTextNode(text));
return cell;
})).forEach((n) => headerRow.appendChild(n));
return headerRow; return headerRow;
}; };
......
#!/usr/bin/python3
import os
import sys
import statistics
import matplotlib.pyplot as plt
def parse_capture(filename):
f = open('measurements/' + filename)
# Skip the first two false positves (risky)
counter = -1
lets_use_a_flag = False
six_ts = ''
two_ts = ''
enc_deltas = {}
dec_deltas = {}
for l in f.readlines():
l_array = l.split(" ")
bit_field = l_array[-1][:-1]
# l_array? That's the best you came up with?
if bit_field == '2':
two_ts = l_array[0][:-1]
lets_use_a_flag = True
continue
if lets_use_a_flag:
if bit_field == '6':
if counter <= 0:
counter = counter + 1
lets_use_a_flag = False
continue
six_ts = l_array[0][:-1]
delta = float(six_ts) - float(two_ts)
if counter % 2 == 1:
enc_deltas[(counter+1)/2] = delta
else:
dec_deltas[counter/2] = delta
counter = counter + 1
else:
lets_use_a_flag = False
f.close()
return (enc_deltas, dec_deltas)
def read_log(d):
# Soo readlines, much efficient
f = open(d + '/test_stdout.log', 'r')
content = f.readlines()
are_we_happy = content[-1].split(' ')[-1]
if are_we_happy != 'SUCCESSFUL\n':
print ("Test unsuccesful or log file structure corrupted")
return
# I like to split it, split it
path = content[0].split(' ')[-1].split('/')
if path[-2] == 'ref':
algorithm = path[-3]
else:
algorithm = path[-2]
# Path to logic data is in the second to last line
logic_file = content[-2].split('/')[-1][:-2]
f. close()
print("Evaluating results for %s" % (algorithm))
dicts = parse_capture(logic_file)
return (algorithm, dicts)
def gen_graph_chunks(bp_data, bp_labels, size):
l = len(bp_data)
for i in range(0, (l//size) * size, size):
chunk = bp_data[i:i+size]
labels = bp_labels[i:i+size]
plt.boxplot(chunk, labels=labels)
plt.xticks(rotation=90)
plt.show()
# Let's also plot the leftover
rest = l % size
if rest != 0:
plt.boxplot(bp_data[(rest-2*rest):])
plt.show()
def main():
print('THE LWC BENCHMARK SPLITTER')
print('powered by Deutsche Bahn')
build_dir = 'build/new/'
bp_data = []
bp_labels = []
for d in os.listdir(build_dir):
#dicts[0] --> algo
#dicts[1][0] --> enc
#dicts[1][1] --> dec
dicts = read_log(os.path.join(build_dir + d))
enc_values = dicts[1][0].values()
dec_values = dicts[1][1].values()
bp_data.append(list(enc_values))
bp_labels.append(dicts[0])
print("Average enc time[s] = %f" % (statistics.mean(enc_values)))
print("Median enc time[s] = %f" % (statistics.median(enc_values)))
print("Average dec time[s] = %f" % (statistics.mean(dec_values)))
print("Median dec time[s] = %f" % (statistics.median(dec_values)))
print()
enc_len = len(dicts[1][0])
dec_len = len(dicts[1][1])
if dec_len != enc_len:
raise Exception("#Encryptions (%d) does not match #decryptions (%d)" % (enc_len, dec_len))
if dec_len != 1089 or enc_len != 1089:
raise Exception("#Number of encrypted test vectors (%d)/ decrypted test vectors (%d) does not match guidelines (1089)" % (enc_len, dec_len))
gen_graph_chunks(bp_data, bp_labels, 5)
#plt.boxplot(bp_data, labels=bp_labels)
#plt.xticks(rotation=90)
#plt.show()
if __name__ == "__main__":
main()
...@@ -30,17 +30,33 @@ function run() { ...@@ -30,17 +30,33 @@ function run() {
mkdir -p $DESTDIR mkdir -p $DESTDIR
echo "Compiling for template '$TEMPLATE' in directory '$TMPDIR'" echo "Compiling for template '$TEMPLATE' in directory '$TMPDIR'"
./compile_all.py -s $SUBMISSION -t "templates/$TEMPLATE" -b "$TMPDIR"
TEMPLATE_PATH="templates/$TEMPLATE"
TEMPLATE_COMMIT=$(git rev-list -1 HEAD -- "$TEMPLATE_PATH")
if [ -z "$TEMPLATE_COMMIT" ]; then
echo "Could not retrieve the git commit of the template"
exit 1
fi
TEMPLATE_TIMESTAMP=$(git show -s --format=%ct "$TEMPLATE_COMMIT")
if [ -z "$TEMPLATE_TIMESTAMP" ]; then
echo "Could not retrieve the git commit date of the template"
exit 1
fi
./compile_all.py -s $SUBMISSION -t $TEMPLATE_PATH -b "$TMPDIR"
for cipher in $TMPDIR/*; do for cipher in $TMPDIR/*; do
if [[ ! -d $cipher ]]; then continue; fi if [[ ! -d $cipher ]]; then continue; fi
mkdir -p "./queues"
QUEUE_PATH="./queues/$TEMPLATE"
CIPHER_SLUG=$(basename $cipher) CIPHER_SLUG=$(basename $cipher)
TEST_PATH="$DESTDIR/$CIPHER_SLUG" CIPHER_TIMESTAMP=$(cat "$cipher/cipher_mtime.txt")
TEST_PATH="$DESTDIR/$CIPHER_SLUG"
mkdir -p "$TEST_PATH" || exit 1 mkdir -p "$TEST_PATH" || exit 1
TEST_PATH=$(realpath $TEST_PATH)
mv $cipher/*.log "$TEST_PATH" mv $cipher/*.log "$TEST_PATH"
mv "$cipher/LWC_AEAD_KAT.txt" "$TEST_PATH" mv "$cipher/LWC_AEAD_KAT.txt" "$TEST_PATH"
...@@ -61,15 +77,24 @@ function run() { ...@@ -61,15 +77,24 @@ function run() {
;; ;;
esac esac
curl \ curl \
--request 'POST' \ --request 'POST' \
--header "Content-Type: application/json" \ --header "Content-Type: application/json" \
--data "{\"path\":\"$(realpath $TEST_PATH)\",\"template\":\"$TEMPLATE\"}" \ --data "\
{\
\"build_dir\":\"$TEST_PATH\",\
\"cipher\":\"$CIPHER_SLUG\",\
\"cipher_timestamp\":\"$CIPHER_TIMESTAMP\",\
\"template\":\"$TEMPLATE\",\
\"template_commit\":\"$TEMPLATE_COMMIT\",\
\"template_timestamp\":\"$TEMPLATE_TIMESTAMP\"\
}" \
"http://127.0.0.1:5002/schedule_test" "http://127.0.0.1:5002/schedule_test"
done done
rm -rf "$TMPDIR" #rm -rf "$TMPDIR"
} }
if [[ $1 == "run" ]]; then if [[ $1 == "run" ]]; then
...@@ -84,6 +109,7 @@ else ...@@ -84,6 +109,7 @@ else
MAINDIR=email-submissions/$(date +%Y-%m-%d-%H:%M) MAINDIR=email-submissions/$(date +%Y-%m-%d-%H:%M)
mkdir -p $MAINDIR mkdir -p $MAINDIR
TMPDIR=$(mktemp -d -t submission-XXXXXXXXXX) TMPDIR=$(mktemp -d -t submission-XXXXXXXXXX)
echo "Extracting in $TMPDIR"
unzip $ZIP_PATH -d $TMPDIR unzip $ZIP_PATH -d $TMPDIR
for i in templates/*; do for i in templates/*; do
TEMPLATE="${i##*/}" TEMPLATE="${i##*/}"
......
#!/bin/bash #!/bin/bash
mv -n *.c *.s *.S src/
# Rename all *.s to *.S
for f in *.s; do
mv -- "$f" "${f%.s}.S"
done
mv -n *.c *.S src/
mv -n *.dat *.inc *.h include/ mv -n *.dat *.inc *.h include/
sed -i src/encrypt.c -e "s/\(\s\)init(/\1encrypt_init(/g" sed -i src/encrypt.c -e "s/\(\s\)init(/\1encrypt_init(/g"
exit 0 exit 0
...@@ -38,6 +38,8 @@ class BluePill(DeviceUnderTestAeadUARTP): ...@@ -38,6 +38,8 @@ class BluePill(DeviceUnderTestAeadUARTP):
self.firmware_path = os.path.join( self.firmware_path = os.path.join(
build_dir, 'firmware.elf') build_dir, 'firmware.elf')
self.firmware_bin_path = os.path.join(
build_dir, 'firmware.bin')
self.ram_pattern_path = os.path.join( self.ram_pattern_path = os.path.join(
self.template_path, 'empty_ram.bin') self.template_path, 'empty_ram.bin')
self.ram_dump_path = os.path.join( self.ram_dump_path = os.path.join(
...@@ -47,6 +49,9 @@ class BluePill(DeviceUnderTestAeadUARTP): ...@@ -47,6 +49,9 @@ class BluePill(DeviceUnderTestAeadUARTP):
self.ocd = OpenOcd(self.openocd_cfg_path) self.ocd = OpenOcd(self.openocd_cfg_path)
def firmware_size(self):
return os.stat(self.firmware_bin_path).st_size
def flash(self): def flash(self):
ocd_cmd = 'program %s verify reset' % self.firmware_path ocd_cmd = 'program %s verify reset' % self.firmware_path
res = self.ocd.send(ocd_cmd) res = self.ocd.send(ocd_cmd)
......
#!/bin/bash #!/bin/bash
mv -n *.c *.s *.S src/
# Rename all *.s to *.S
for f in *.s; do
mv -- "$f" "${f%.s}.S"
done
mv -n *.c *.S src/
mv -n *.dat *.inc *.h include/ mv -n *.dat *.inc *.h include/
sed -i src/encrypt.c -e "s/\(\s\)init(/\1_init(/g" sed -i src/encrypt.c -e "s/\(\s\)init(/\1_init(/g"
exit 0 exit 0
...@@ -35,23 +35,33 @@ class ESP32(DeviceUnderTestAeadUARTP): ...@@ -35,23 +35,33 @@ class ESP32(DeviceUnderTestAeadUARTP):
self.build_dir = build_dir self.build_dir = build_dir
self.template_path = os.path.dirname(sys.argv[0]) self.template_path = os.path.dirname(sys.argv[0])
self.firmware_path = os.path.join( self.pio_packages_path = "/home/tester/.platformio/packages/"
build_dir, 'firmware.elf') self.esptoolpy_path = os.path.join(
self.pio_packages_path, "tool-esptoolpy/esptool.py")
self.partitionspath = os.path.join(build_dir, 'partitions.bin')
self.firmware_path = os.path.join(build_dir, 'firmware.bin')
# Convert elf to bin
cmd = ['python3', self.esptoolpy_path]
cmd += ['--chip', 'esp32']
cmd += ['elf2image', os.path.join(build_dir, 'firmware.elf')]
subprocess.check_call(cmd)
def firmware_size(self):
return os.stat(self.firmware_path).st_size
def reset(self): def reset(self):
self.ser.setDTR(False) # IO0=HIGH self.ser.setDTR(False) # IO0=HIGH
self.ser.setRTS(True) # EN=LOW, chip in reset self.ser.setRTS(True) # EN=LOW, chip in reset1
time.sleep(0.1) time.sleep(0.1)
self.ser.setDTR(False) # IO0=HIGH self.ser.setDTR(False) # IO0=HIGH
self.ser.setRTS(False) # EN=HIGH, chip out of reset self.ser.setRTS(False) # EN=HIGH, chip out of reset
time.sleep(1) time.sleep(1)
def flash(self): def flash(self):
pio_packages_path = "/home/tester/.platformio/packages/"
esptoolpy_path = os.path.join(
pio_packages_path, "tool-esptoolpy/esptool.py")
arduinoespressif32_path = os.path.join( arduinoespressif32_path = os.path.join(
pio_packages_path, "framework-arduinoespressif32/") self.pio_packages_path, "framework-arduinoespressif32/")
boot_app0_path = os.path.join( boot_app0_path = os.path.join(
arduinoespressif32_path, arduinoespressif32_path,
"tools/partitions/boot_app0.bin") "tools/partitions/boot_app0.bin")
...@@ -59,23 +69,14 @@ class ESP32(DeviceUnderTestAeadUARTP): ...@@ -59,23 +69,14 @@ class ESP32(DeviceUnderTestAeadUARTP):
arduinoespressif32_path, arduinoespressif32_path,
"tools/sdk/bin/bootloader_dio_80m.bin") "tools/sdk/bin/bootloader_dio_80m.bin")
elfpath = os.path.abspath(self.firmware_path)
binpath = os.path.splitext(elfpath)[0] + '.bin'
partpath = os.path.join(*os.path.split(elfpath)[:-1], 'partitions.bin')
partitions = [ partitions = [
(0xe000, boot_app0_path), (0xe000, boot_app0_path),
(0x1000, bootloader_path), (0x1000, bootloader_path),
(0x10000, binpath), (0x10000, self.firmware_path),
(0x8000, partpath) (0x8000, self.partitionspath)
] ]
cmd = ['python3', esptoolpy_path] cmd = ['python3', self.esptoolpy_path]
cmd += ['--chip', 'esp32']
cmd += ['elf2image', elfpath]
subprocess.check_call(cmd)
cmd = ['python3', esptoolpy_path]
cmd += ['--chip', 'esp32'] cmd += ['--chip', 'esp32']
cmd += ['--before', 'default_reset', '--after', 'hard_reset'] cmd += ['--before', 'default_reset', '--after', 'hard_reset']
cmd += ['--port', self.uart_device] cmd += ['--port', self.uart_device]
......
#!/bin/bash #!/bin/bash
# Rename all *.s to *.S
for f in *.s; do
mv -- "$f" "${f%.s}.S"
done
mv -n *.dat *.inc *.h Inc/ mv -n *.dat *.inc *.h Inc/
sed -i src/encrypt.c -e "s/\(\s\)init(/\1_init(/g" sed -i src/encrypt.c -e "s/\(\s\)init(/\1_init(/g"
mkdir -p /tmp/f7/Drivers mkdir -p /tmp/f7/Drivers
......
...@@ -47,6 +47,9 @@ class F7(DeviceUnderTestAeadUARTP): ...@@ -47,6 +47,9 @@ class F7(DeviceUnderTestAeadUARTP):
self.ram_pattern_path = os.path.join( self.ram_pattern_path = os.path.join(
self.template_path, 'ram_pattern.bin') self.template_path, 'ram_pattern.bin')
def firmware_size(self):
return os.stat(self.firmware_path).st_size
def flash(self): def flash(self):
jlink = self.jlink jlink = self.jlink
jlink.connect('STM32F746ZG') jlink.connect('STM32F746ZG')
......
#!/bin/bash #!/bin/bash
mv -n *.c *.s *.S src/
# Rename all *.s to *.S
for f in *.s; do
mv -- "$f" "${f%.s}.S"
done
mv -n *.c *.S src/
mv -n *.dat *.inc *.h include/ mv -n *.dat *.inc *.h include/
sed -i src/encrypt.c -e "s/\(\s\)init(/\1_init(/g" sed -i src/encrypt.c -e "s/\(\s\)init(/\1_init(/g"
exit 0 exit 0
...@@ -39,6 +39,9 @@ class Maixduino(DeviceUnderTestAeadUARTP): ...@@ -39,6 +39,9 @@ class Maixduino(DeviceUnderTestAeadUARTP):
self.firmware_path = os.path.join( self.firmware_path = os.path.join(
build_dir, 'firmware.bin') build_dir, 'firmware.bin')
def firmware_size(self):
return os.stat(self.firmware_path).st_size
def reset(self): def reset(self):
if self.ser is not None: if self.ser is not None:
self.ser.close() self.ser.close()
......
#!/bin/bash #!/bin/bash
mv -n *.c *.s *.S src/
# Rename all *.s to *.S
for f in *.s; do
mv -- "$f" "${f%.s}.S"
done
mv -n *.c *.S src/
mv -n *.dat *.inc *.h include/ mv -n *.dat *.inc *.h include/
sed -i src/encrypt.c -e "s/\(\s\)init(/\1_init(/g" sed -i src/encrypt.c -e "s/\(\s\)init(/\1_init(/g"
exit 0 exit 0
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
import os import os
import sys import sys
import time import time
import intelhex
import subprocess import subprocess
import serial.tools.list_ports import serial.tools.list_ports
from test_common import ( from test_common import (
...@@ -38,6 +39,17 @@ class Uno(DeviceUnderTestAeadUARTP): ...@@ -38,6 +39,17 @@ class Uno(DeviceUnderTestAeadUARTP):
self.firmware_path = os.path.join( self.firmware_path = os.path.join(
build_dir, 'firmware.hex') build_dir, 'firmware.hex')
ih = intelhex.IntelHex()
ih.loadhex(self.firmware_path)
total_size = 0
for start, stop in ih.segments():
print(start, stop, stop-start)
total_size += stop-start
self._firmware_size = total_size
def firmware_size(self):
return self._firmware_size
def reset(self): def reset(self):
self.ser.setDTR(True) self.ser.setDTR(True)
time.sleep(0.01) time.sleep(0.01)
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
import os import os
import sys import sys
import time
import signal import signal
import datetime import datetime
import threading import threading
...@@ -10,6 +11,7 @@ import subprocess ...@@ -10,6 +11,7 @@ import subprocess
from flask import Flask, request, Response from flask import Flask, request, Response
from flask_restful import Resource, Api from flask_restful import Resource, Api
from flask_jsonpify import jsonify from flask_jsonpify import jsonify
from test_common import pack_results
app = Flask(__name__, static_folder='.') app = Flask(__name__, static_folder='.')
...@@ -21,29 +23,51 @@ runners = [] ...@@ -21,29 +23,51 @@ runners = []
class ScheduledTest: class ScheduledTest:
def __init__(self, template, path): __slots__ = [
self.template = template 'id', 'state', 'added', 'lock', 'time_started',
self.path = path 'path', 'template', 'template_commit', 'template_timestamp',
'cipher', 'cipher_timestamp'
]
_unserialized_slots = [
'lock',
]
_next_id = 1
def __init__(self, **kwargs):
self.path = kwargs['build_dir']
self.cipher = kwargs['cipher']
self.cipher_timestamp = kwargs['cipher_timestamp']
self.template_timestamp = kwargs['template_timestamp']
self.template = kwargs['template']
self.template_commit = kwargs['template_commit']
self.id = str(ScheduledTest._next_id)
ScheduledTest._next_id += 1
self.state = 'SCHEDULED' self.state = 'SCHEDULED'
self.added = datetime.datetime.now() self.added = datetime.datetime.now()
self.time_started = None
self.lock = threading.Lock() self.lock = threading.Lock()
def to_dict(self): def to_dict(self):
return { res = {}
'id': str(id(self)), for k in ScheduledTest.__slots__:
'template': self.template, if k not in ScheduledTest._unserialized_slots:
'state': self.state, res[k] = getattr(self, k)
'path': self.path, return res
'added': self.added,
}
class Runner(threading.Thread): class Runner(threading.Thread):
def __init__(self, template, program=None): _next_id = 1
def __init__(self, template, platform, program=None):
if program is None: if program is None:
program = ['python3', './templates/%s/test' % template] program = ['python3', './templates/%s/test' % template]
self.id = str(Runner._next_id)
Runner._next_id += 1
self.template = template self.template = template
self.platform = platform
self.program = program self.program = program
self.process = None self.process = None
self.job = None self.job = None
...@@ -55,71 +79,87 @@ class Runner(threading.Thread): ...@@ -55,71 +79,87 @@ class Runner(threading.Thread):
def to_dict(self): def to_dict(self):
return { return {
'id': str(id(self)), 'id': self.id,
'template': self.template, 'template': self.template,
'program': ' '.join(self.program), 'program': ' '.join(self.program),
'job': str(id(self.job)) if self.job is not None else None 'job': self.job.id if self.job is not None else None
} }
def stop(self): def stop(self):
self.stop_event.set() self.stop_event.set()
def lock_new_job(self):
my_queue = [
s for s in schedule
if s.state == 'SCHEDULED'
and s.template == self.template
]
my_queue.sort(key=lambda s: s.added)
if len(my_queue) == 0:
# print("Runner %s has no jobs in queue" % self.template)
return None
job = my_queue[0]
with job.lock:
# Check if we were the first thread to choose this job
if job.state == 'SCHEDULED':
job.state = 'RUNNING'
self.job = job
return job
else:
# Some other thread is running this test
# print("Runner %s could not lock a job" % self.template)
return None
def do_job(self):
self.job.time_started = int(time.time())
cmd = []
cmd += self.program
cmd += [self.job.path]
print("Executing ``%s´´" % ' '.join(cmd))
out_path = os.path.join(self.job.path, 'test.stdout.log')
err_path = os.path.join(self.job.path, 'test.stderr.log')
with open(out_path, 'w') as out_fd, \
open(err_path, 'w') as err_fd:
self.process = subprocess.Popen(
cmd,
stdout=out_fd,
stderr=err_fd
)
while self.process.poll() is None:
if self.stop_event.wait(timeout=1):
self.process.send_signal(signal.SIGINT)
try:
self.process.wait(timeout=1)
except subprocess.TimeoutExpired:
pass
if self.process.returncode == 0:
self.job.state = 'SUCCESSFUL'
else:
self.job.state = 'FAILED'
self.process = None
pack_results(
self.job,
self.platform)
def run(self): def run(self):
while not self.stop_event.is_set(): while not self.stop_event.is_set():
my_queue = [ self.lock_new_job()
s for s in schedule if self.job is None:
if s.state == 'SCHEDULED'
and s.template == self.template
]
my_queue.sort(key=lambda s: s.added)
if len(my_queue) == 0:
# No tasks for this thread, go to sleep # No tasks for this thread, go to sleep
self.stop_event.wait(timeout=5) self.stop_event.wait(timeout=5)
continue continue
job = my_queue[0] try:
self.do_job()
with job.lock: except Exception as ex:
# Check if we were the first thread to choose this job print(ex)
if job.state == 'SCHEDULED':
job.state = 'RUNNING'
self.job = job
else:
# Some other thread is running this test
continue
cmd = []
cmd += self.program
cmd += [self.job.path]
print("Executing ``%s´´" % ' '.join(cmd))
out_path = os.path.join(self.job.path, 'test.stdout.log')
err_path = os.path.join(self.job.path, 'test.stderr.log')
with open(out_path, 'w') as out_fd, \
open(err_path, 'w') as err_fd:
self.process = subprocess.Popen(
cmd,
stdout=out_fd,
stderr=err_fd
)
while self.process.poll() is None:
if self.stop_event.wait(timeout=1):
self.process.send_signal(signal.SIGINT)
try:
self.process.wait(timeout=1)
except subprocess.TimeoutExpired:
pass
if self.process.returncode == 0:
self.job.state = 'SUCCESSFUL'
else:
self.job.state = 'FAILED'
self.process = None
subprocess.check_call(
['zip', '-r', 'results.zip', '.'],
cwd=self.job.path)
print("Job %d has finished" % id(self.job)) print("Job %s has finished" % self.job.id)
self.job = None self.job = None
print("Thread %s has finished" % self.name) print("Thread %s has finished" % self.name)
...@@ -135,7 +175,7 @@ class Status(Resource): ...@@ -135,7 +175,7 @@ class Status(Resource):
class RestartJob(Resource): class RestartJob(Resource):
def get(self, job_id): def get(self, job_id):
job = [job for job in schedule if str(id(job)) == job_id] job = [job for job in schedule if job.id == job_id]
job = job[0] if len(job) > 0 else None job = job[0] if len(job) > 0 else None
if job is None: if job is None:
return 'Job not found', 404 return 'Job not found', 404
...@@ -154,12 +194,15 @@ class ScheduleJob(Resource): ...@@ -154,12 +194,15 @@ class ScheduleJob(Resource):
return 'Please send me JSON', 400 return 'Please send me JSON', 400
data = request.get_json() data = request.get_json()
print(data) print(data)
if 'path' not in data: mandatory_fields = [
return 'path expected', 400 'build_dir', 'template', 'template_commit', 'template_timestamp',
if 'template' not in data: 'cipher', 'cipher_timestamp'
return 'template expected', 400 ]
for k in mandatory_fields:
if k not in data:
return 'field "%s" expected' % k, 400
schedule.append(ScheduledTest(data['template'], data['path'])) schedule.append(ScheduledTest(**data))
result = {'success': True} result = {'success': True}
return jsonify(result) return jsonify(result)
...@@ -177,7 +220,7 @@ def root(): ...@@ -177,7 +220,7 @@ def root():
@app.route('/view_log/<string:job_id>/<int:log_id>') @app.route('/view_log/<string:job_id>/<int:log_id>')
def view_log(job_id, log_id): def view_log(job_id, log_id):
job = [job for job in schedule if str(id(job)) == job_id] job = [job for job in schedule if job.id == job_id]
job = job[0] if len(job) > 0 else None job = job[0] if len(job) > 0 else None
if job is None: if job is None:
return 'Job not found', 404 return 'Job not found', 404
...@@ -195,20 +238,34 @@ def view_log(job_id, log_id): ...@@ -195,20 +238,34 @@ def view_log(job_id, log_id):
@app.route('/results/<string:job_id>/results.zip') @app.route('/results/<string:job_id>/results.zip')
def get_results_zip(job_id): def get_results_zip(job_id):
job = next(filter(lambda job: str(id(job)) == job_id, schedule), None) job = next(filter(lambda job: job.id == job_id, schedule), None)
if job is None: if job is None:
return 'Job not found', 404 return 'Job not found', 404
zip_path = os.path.join(job.path, 'results.zip') zip_path = os.path.join(job.path, 'results.zip')
if not os.path.isfile(zip_path):
return 'File not found', 404
with open(zip_path, 'rb') as zip: with open(zip_path, 'rb') as zip:
return Response(zip.read(), mimetype='application/zip') return Response(zip.read(), mimetype='application/zip')
@app.route('/results/<string:job_id>/results.json')
def get_results_json(job_id):
job = next(filter(lambda job: job.id == job_id, schedule), None)
if job is None:
return 'Job not found', 404
path = os.path.join(job.path, 'results.json')
if not os.path.isfile(path):
return 'File not found', 404
with open(path, 'rb') as zip:
return Response(zip.read(), mimetype='application/json')
if __name__ == '__main__': if __name__ == '__main__':
runners.append(Runner('maixduino')) runners.append(Runner('maixduino', 'Maixduino'))
runners.append(Runner('f7')) runners.append(Runner('f7', 'NUCLEO-F746ZG'))
runners.append(Runner('uno')) runners.append(Runner('uno', 'Arduino Uno'))
runners.append(Runner('esp32')) runners.append(Runner('esp32', 'ESP32'))
runners.append(Runner('bluepill')) runners.append(Runner('bluepill', 'BluePill'))
def signal_handler(signal, frame): def signal_handler(signal, frame):
print("Process interrupted!", file=sys.stderr) print("Process interrupted!", file=sys.stderr)
......
...@@ -3,11 +3,14 @@ ...@@ -3,11 +3,14 @@
import os import os
import re import re
import sys import sys
import glob
import json
import time import time
import fcntl import fcntl
import struct import struct
import socket import socket
import subprocess import subprocess
import numpy as np
def eprint(*args, **kargs): def eprint(*args, **kargs):
...@@ -186,33 +189,6 @@ def run_nist_aead_test_line(dut, i, m, ad, k, npub, c): ...@@ -186,33 +189,6 @@ def run_nist_aead_test_line(dut, i, m, ad, k, npub, c):
"expected ciphertext") "expected ciphertext")
def compare_dumps(dump_a, dump_b):
"""
Gets the length of the longes streaks of equal bytes in two RAM dumps
"""
streaks = []
streak_beg = 0
streak_end = 0
for i in range(len(dump_a)):
if dump_a[i] == dump_b[i]:
streak_end = i
else:
if streak_end != streak_beg:
streaks.append((streak_beg, streak_end))
streak_beg = i
streak_end = i
for b, e in streaks:
eprint("equal bytes from 0x%x to 0x%x (length: %d)" %
(b, e, e-b))
b, e = max(streaks, key=lambda a: a[1]-a[0])
eprint(
"longest equal bytes streak from 0x%x to 0x%x (length: %d)" %
(b, e, e-b))
return e-b
def parse_nist_aead_test_vectors(test_file_path): def parse_nist_aead_test_vectors(test_file_path):
with open(test_file_path, 'r') as test_file: with open(test_file_path, 'r') as test_file:
lineprog = re.compile( lineprog = re.compile(
...@@ -229,6 +205,7 @@ def parse_nist_aead_test_vectors(test_file_path): ...@@ -229,6 +205,7 @@ def parse_nist_aead_test_vectors(test_file_path):
res = lineprog.match(line) res = lineprog.match(line)
if line == "": if line == "":
yield i, m, ad, k, npub, c yield i, m, ad, k, npub, c
i = -1
m = b"" m = b""
ad = b"" ad = b""
k = b"" k = b""
...@@ -255,6 +232,9 @@ def parse_nist_aead_test_vectors(test_file_path): ...@@ -255,6 +232,9 @@ def parse_nist_aead_test_vectors(test_file_path):
raise Exception( raise Exception(
"ERROR: unparsed line in test vectors file: '%s'" % line) "ERROR: unparsed line in test vectors file: '%s'" % line)
if i >= 0:
yield i, m, ad, k, npub, c
class TimeMeasurementTool: class TimeMeasurementTool:
def begin_measurement(self): def begin_measurement(self):
...@@ -447,10 +427,194 @@ class OpenOcd: ...@@ -447,10 +427,194 @@ class OpenOcd:
return data.decode('ascii') return data.decode('ascii')
def pack_results(job, platform):
build_dir = job.path
subprocess.call(
['rm', 'results.zip', 'results.json'],
cwd=build_dir)
logic_path = os.path.join(build_dir, 'logic_trace.csv')
logic_trace = []
with open(logic_path, 'rt') as f:
f.readline() # skip header
for line in f.readlines():
parts = line.split(',')
t = float(parts[0].strip())
v = int(parts[1].strip(), 0)
logic_trace.append((t, v))
dips = find_dips(logic_trace)
dips_durations = [rais-fall for fall, rais in dips]
ram_dumps = {}
for dump_path in glob.glob(os.path.join(build_dir, "ram_dump.*.bin")):
dump_name = os.path.basename(dump_path)
m = re.match(r"ram_dump.(\d+).bin", dump_name)
if not m:
raise Exception("RAM dump has an unexpected name %s" % dump_name)
idx = int(m[1], 0)
with open(dump_path, 'rb') as f:
ram_dumps[idx] = f.read()
print(list(ram_dumps.keys()))
if 0 in ram_dumps and 1 in ram_dumps:
total_memory = len(ram_dumps[0])
untouched_memory = compare_dumps(ram_dumps[0], ram_dumps[1])
print(" longest chunk of untouched memory = %d" % untouched_memory)
memory_utilization = total_memory - untouched_memory
else:
memory_utilization = None
with open(os.path.join(build_dir, 'firmware_size.txt'), 'rt') as f:
firmware_size = int(f.readline(), 0)
cipher_family, cipher_variant, cipher_impl = tuple(
job.cipher.split('.', 2)
)
test_vectors_path = os.path.join(build_dir, 'LWC_AEAD_KAT.txt')
test_vector = identify_test_vector(test_vectors_path)
results = {
'format_version': '1.0',
'test_timestamp': job.time_started,
'avg_enc_time': np.mean(dips_durations[0::2]),
'avg_dec_time': np.mean(dips_durations[1::2]),
'firmware_size': firmware_size,
'memory_utilization': memory_utilization,
'cipher': {
'family': cipher_family,
'variant': cipher_variant,
'implementation': cipher_impl,
'timestamp': job.cipher_timestamp,
},
'template': {
'name': job.template,
'timestamp': job.template_timestamp,
'commit': job.template_commit,
},
'platform': {
'name': platform,
},
'test_vector': test_vector,
'dips': dips_durations,
}
json_path = os.path.join(build_dir, 'results.json')
with open(json_path, 'wt') as f:
json.dump(results, f)
subprocess.check_call(
['zip', '-r', 'results.zip', '.'],
cwd=build_dir)
def find_dips(logic_trace):
# There should be an even number of edges (2 edges for each dip)
assert 0 != len(logic_trace)
assert 0 == len(logic_trace) % 2
# First record should be a negative edge, last should be a positive one
assert 0 == logic_trace[0][1]
assert 0 != logic_trace[-1][1]
# Record the start and end times of every dip
dips = []
for i in range(0, len(logic_trace), 2):
assert 0 == logic_trace[i][1]
assert 0 != logic_trace[i+1][1]
dips.append((logic_trace[i][0], logic_trace[i+1][0]))
# Debounce dips by assuming that a data transfer
# between two dips takes at least 1 microsecond
THERESHOLD = 1e-6
debounced = []
i = 0
while i < len(dips)-1:
fall, rais = dips[i]
next_fall, next_rais = dips[i+1]
xfer_time = next_fall - rais
if xfer_time < THERESHOLD:
# Merge current dip with the next
dips[i] = (fall, next_rais)
dips.pop(i+1)
else:
# Save current dip
debounced.append((fall, rais))
i += 1
# Add the last dip
debounced.append((dips[i][0], dips[i][1]))
dips = debounced
# There should be an even number of dips (encryption and decryption)
assert 0 == len(dips) % 2
return dips
def compare_dumps(dump_a, dump_b):
"""
Gets the length of the longes streaks of equal bytes in two RAM dumps
"""
streaks = []
streak_beg = 0
streak_end = 0
for i in range(len(dump_a)):
if dump_a[i] == dump_b[i]:
streak_end = i
else:
if streak_end != streak_beg:
streaks.append((streak_beg, streak_end))
streak_beg = i
streak_end = i
for b, e in streaks:
print(
"equal bytes from 0x%x to 0x%x (length: %d)" % (b, e, e-b))
b, e = max(streaks, key=lambda a: a[1]-a[0])
print(
"longest equal bytes streak from 0x%x to 0x%x (length: %d)" %
(b, e, e-b))
return e-b
def identify_test_vector(kat_path):
# Check if a provided test vector is the official NIST LWC or not
kat = list(parse_nist_aead_test_vectors(kat_path))
def is_nist_aead_kat(kat):
if len(kat) != 1089:
return False
def genstr(length):
return bytes([b % 256 for b in range(length)])
expected_k = genstr(len(kat[0][3]))
expected_npub = genstr(len(kat[0][4]))
expected_i = 0
for i, m, ad, k, npub, c in kat:
expected_m = genstr((i-1) // 33)
expected_ad = genstr((i-1) % 33)
expected_i += 1
if not (expected_i == i and expected_m == m and
expected_k == k and expected_ad == ad and
expected_npub == npub):
return False
return True
if is_nist_aead_kat(kat):
return "NIST AEAD KAT"
return None
def run_nist_lws_aead_test(dut, vectors_file, build_dir, def run_nist_lws_aead_test(dut, vectors_file, build_dir,
logic_mask=0xffff): logic_mask=0xffff):
kat = list(parse_nist_aead_test_vectors(vectors_file)) kat = list(parse_nist_aead_test_vectors(vectors_file))
firmware_size = dut.firmware_size()
path = os.path.join(build_dir, 'firmware_size.txt')
with open(path, 'wt') as f:
print(firmware_size, file=f)
dut.flash() dut.flash()
dut.prepare() dut.prepare()
sys.stdout.write("Board prepared\n") sys.stdout.write("Board prepared\n")
...@@ -470,9 +634,6 @@ def run_nist_lws_aead_test(dut, vectors_file, build_dir, ...@@ -470,9 +634,6 @@ def run_nist_lws_aead_test(dut, vectors_file, build_dir,
if i == 1 and ram_dumps[0] is not None: if i == 1 and ram_dumps[0] is not None:
ram_dumps.append(dut.dump_ram()) ram_dumps.append(dut.dump_ram())
longest = compare_dumps(ram_dumps[0], ram_dumps[1])
print(" longest chunk of untouched memory = %d" % longest)
except Exception as ex: except Exception as ex:
print("TEST FAILED") print("TEST FAILED")
raise ex raise ex
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment