Plots, fixes

This commit is contained in:
Pascal Engélibert 2025-11-14 11:49:22 +01:00
commit fb5adf26f1
4 changed files with 293 additions and 89 deletions

114
README.md
View file

@ -107,10 +107,6 @@ Most of the implementations can be used through RusTLS.
However RusTLS clients won't enable to force TLS1.2 if 1.3 is available. However RusTLS clients won't enable to force TLS1.2 if 1.3 is available.
### rpxy
Reverse-proxy utilisant RusTLS.
### WolfSSL ### WolfSSL
```bash ```bash
@ -122,31 +118,6 @@ make
sudo make install sudo make install
``` ```
### self-signed cert
```bash
openssl req -x509 -newkey rsa:2048 -keyout /tmp/foo.home.key -subj "/CN=foo.home/C=AT/ST=Lyon/L=Lyon/O=MyOrg" -out /tmp/foo.home.crt -nodes -sha256 -addext "subjectAltName=DNS:foo.home"
```
### Client
Automatize experiments using [Selenium](https://www.selenium.dev/documentation/webdriver/getting_started/)
#### Experiment management
* Manager tells P2 what shared libs and rpxy binary to load.
* Tell P1, P2, P3 what rpxy config to load.
* Start measures.
* Start Yocto (USB).
* Start
#### Ad-hoc proxy?
Features:
* Use RusTLS and any backend easily
* Listen to plain HTTP or TLS (1.2 or 1.3)
*
## State of the art ## State of the art
* https://pub.h-brs.de/frontdoor/deliver/index/docId/4771/file/2019-ESP32-TLS-Power.pdf * https://pub.h-brs.de/frontdoor/deliver/index/docId/4771/file/2019-ESP32-TLS-Power.pdf
@ -175,13 +146,68 @@ Features:
* https://github.com/MarcT0K/privacy-carbon-experiments * https://github.com/MarcT0K/privacy-carbon-experiments
* https://davidtnaylor.com/CostOfTheS.pdf * https://davidtnaylor.com/CostOfTheS.pdf
* 2014 * 2014
*
## Sources ## Sources
* [RFC8446 (TLS 1.3)](https://datatracker.ietf.org/doc/html/rfc8446) * [RFC8446 (TLS 1.3)](https://datatracker.ietf.org/doc/html/rfc8446)
## Notes ## Reproduce
### Record
Authorize rpxy and netreplay to bind to ports 80 and 443:
```bash
sudo setcap CAP_NET_BIND_SERVICE=+eip netreplay
```
Open Firefox with a dedicated profile: (create the profile using the GUI if it doesn't exist)
```bash
firefox -P tlsbench
```
In settings, disable DNS security.
In `about:config`, set `devtools.chrome.enabled` to `true`.
In the `about:config` tab, open the console, execute this script to override DNS for the selected names, and redirect them to localhost:
```js
const gOverride = Cc["@mozilla.org/network/native-dns-override;1"].getService(Ci.nsINativeDNSResolverOverride);
gOverride.clearOverrides();
var names = [
"apple.com", "www.apple.com",
"yt3.ggpht.com",
"accounts.google.com", "www.google.com",
"fonts.gstatic.com", "www.gstatic.com",
"mzstatic.com",
"wikimedia.org", "intake-analytics.wikimedia.org", "meta.wikimedia.org", "upload.wikimedia.org",
"wikipedia.org", "fr.wikipedia.org",
"youtube.com", "www.youtube.com",
"i.ytimg.com"
];
for(var i in names) {
gOverride.addIPOverride(names[i], "127.0.0.1");
}
```
Stop anything running on ports 80 or 443.
Start the record proxy:
```bash
./netreplay records/mynewrecord record
```
Just browse. Any traffic to and from the selected names will be recorded. Terminate netplayer with CTRL+C when finished.
### Measure
Add p2 the `/etc/hosts`:
```
192.168.3.14 p2
```
Install sa on p2: Install sa on p2:
@ -190,35 +216,11 @@ sudo apt install acct
sudo chmod +s /sbin/sa sudo chmod +s /sbin/sa
``` ```
Override DNS in browser:
```bash
firefox -P tlsbench
```
In `about:config`, set `devtools.chrome.enabled` to `true`.
Set default DNS in settings.
In console:
```js
const gOverride = Cc["@mozilla.org/network/native-dns-override;1"].getService(Ci.nsINativeDNSResolverOverride);
gOverride.clearOverrides();
var names = ["apple.com", "www.apple.com", "mzstatic.com", "youtube.com", "www.youtube.com", "i.ytimg.com", "fonts.gstatic.com", "www.google.com", "accounts.google.com", "yt3.ggpht.com", "www.gstatic.com"];
for(var i in names) {
gOverride.addIPOverride(names[i], "127.0.0.1");
}
```
Authorize rpxy to bind to ports 80 and 443:
```bash
sudo setcap CAP_NET_BIND_SERVICE=+eip netreplay
```
```bash ```bash
python exp.py make -c python exp.py make -c
python exp.py send python exp.py send
python exp.py update-certs # also do this command on p2 python exp.py update-certs # also do this command on p2
python exp.py run python exp.py run --idle
``` ```
On Debian, update-certs says 0 certs added even if it has actually updated some certs. This step is still needed. On Debian, update-certs says 0 certs added even if it has actually updated some certs. This step is still needed.

116
exp.py
View file

@ -6,6 +6,7 @@ P2_SSH = "exp@p2"
P2_PSW = "exp" P2_PSW = "exp"
P2_REPODIR = "/home/exp/exp" P2_REPODIR = "/home/exp/exp"
EXPDIR = "/dev/shm/exp" EXPDIR = "/dev/shm/exp"
LOG_BACKUP_DIR = "/home/tuxmain"
P2_ADDR = "192.168.3.14" P2_ADDR = "192.168.3.14"
DOMAINS_ = [ DOMAINS_ = [
# Apple # Apple
@ -33,11 +34,11 @@ DOMAINS_ = [
WATTMETER = True WATTMETER = True
RECORDS = [ RECORDS = [
#{ "filename": "youtube", "repeat": 1 }, #{ "filename": "youtube", "repeat": 1000 },
#{ "filename": "peertube", "repeat": 10 }, #{ "filename": "peertube", "repeat": 10 },
#{ "filename": "wikipedia", "repeat": 10 }, { "filename": "wikipedia", "repeat": 100 },
{ "filename": "apple", "repeat": 100 }, #{ "filename": "apple", "repeat": 1000 },
#{ "filename": "google", "repeat": 10 }, #{ "filename": "google", "repeat": 1000 },
] ]
CERT_SIGN_ALGS = [ CERT_SIGN_ALGS = [
"prime256v1", # widely used "prime256v1", # widely used
@ -46,8 +47,8 @@ CERT_SIGN_ALGS = [
"rsa2048", "rsa3072", "rsa4096", # widely used "rsa2048", "rsa3072", "rsa4096", # widely used
] ]
IMPLS = [ IMPLS = [
#"aws_lc_rs", # Amazon's Rust crypto widely used in Rust stuff "aws_lc_rs", # Amazon's Rust crypto widely used in Rust stuff
#"boring", # Google's fork of OpenSSL used in Chrome and Android "boring", # Google's fork of OpenSSL used in Chrome and Android
"openssl", # widely used "openssl", # widely used
#"ring", # used in most Rust stuff #"ring", # used in most Rust stuff
#"symcrypt", # Microsoft's crypto #"symcrypt", # Microsoft's crypto
@ -72,6 +73,7 @@ KEXES = [
"SECP256R1", "SECP256R1",
"SECP384R1", "SECP384R1",
] ]
IDLE = "idle - - - - - - 600.000081539154 0.0 896 4792 0.5399999999999991"
# Testing all combinations would be too much. Instead we isolate independent parts. # Testing all combinations would be too much. Instead we isolate independent parts.
EXPERIMENTS = { EXPERIMENTS = {
@ -83,9 +85,9 @@ EXPERIMENTS = {
"AES_128_GCM_SHA256", "AES_128_GCM_SHA256",
"AES_256_GCM_SHA384", "AES_256_GCM_SHA384",
"CHACHA20_POLY1305_SHA256", "CHACHA20_POLY1305_SHA256",
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256", #"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384", #"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", #"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
], ],
"kexes": ["X25519"], "kexes": ["X25519"],
"cert": ["prime256v1"], "cert": ["prime256v1"],
@ -96,14 +98,14 @@ EXPERIMENTS = {
"records": RECORDS, "records": RECORDS,
"ciphers": [ "ciphers": [
"AES_128_GCM_SHA256", "AES_128_GCM_SHA256",
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256", #"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256",
], ],
"kexes": ["X25519"], "kexes": ["X25519"],
"cert": [ "cert": [
"prime256v1", "prime256v1",
#"secp384r1", #"secp384r1",
"rsa2048", "rsa2048",
#"rsa3072", "rsa4096" "rsa3072", "rsa4096"
], ],
}, },
## Compare key exchange groups among implementations and TLS versions ## Compare key exchange groups among implementations and TLS versions
@ -112,7 +114,7 @@ EXPERIMENTS = {
"records": RECORDS, "records": RECORDS,
"ciphers": [ "ciphers": [
"AES_128_GCM_SHA256", "AES_128_GCM_SHA256",
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256", #"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256",
], ],
"kexes": ["X25519", "SECP256R1", "SECP384R1"], "kexes": ["X25519", "SECP256R1", "SECP384R1"],
"cert": ["prime256v1"], "cert": ["prime256v1"],
@ -122,8 +124,9 @@ EXPERIMENTS = {
# "records": RECORDS, # "records": RECORDS,
# "ciphers": [ # "ciphers": [
# "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256", # "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256",
# "ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
# ], # ],
# "kexes": ["SECP384R1"], # "kexes": ["X25519"],
# "cert": ["prime256v1"], # "cert": ["prime256v1"],
#}, #},
} }
@ -286,24 +289,28 @@ SETUPS = {
"netreplay_tls_mode": "none", "netreplay_tls_mode": "none",
"p2_port": 80, "p2_port": 80,
"listen_port": 80, "listen_port": 80,
"tls_invariant": True,
}, },
"client": { "client": {
"rpxy_config": "tls", "rpxy_config": "tls",
"netreplay_tls_mode": "server", "netreplay_tls_mode": "server",
"p2_port": 80, "p2_port": 80,
"listen_port": 443, "listen_port": 443,
"tls_invariant": False,
}, },
"server": { "server": {
"rpxy_config": "plain", "rpxy_config": "plain",
"netreplay_tls_mode": "client", "netreplay_tls_mode": "client",
"p2_port": 443, "p2_port": 443,
"listen_port": 80, "listen_port": 80,
"tls_invariant": False,
}, },
#"both": { #"both": {
# "rpxy_config": "tls", # "rpxy_config": "tls",
# "netreplay_tls_mode": "both", # "netreplay_tls_mode": "both",
# "p2_port": 443, # "p2_port": 443,
# "listen_port": 443, # "listen_port": 443,
# "tls_invariant": False,
#}, #},
} }
@ -357,22 +364,15 @@ def choose_impl(expdir, p, impl):
expdir += "/" expdir += "/"
os.symlink(os.getcwd()+"/rpxy_rustls_"+impl, expdir+str(p)+"_rpxy", False) os.symlink(os.getcwd()+"/rpxy_rustls_"+impl, expdir+str(p)+"_rpxy", False)
def run_rpxy(expdir, repodir, config_name, impl, ciphers=None, kexes=None): def run_netreplay(expdir, repodir, record, p2_addr, p2_port, listen_port, tls_mode, only_record=None, ciphers=None, kexes=None):
if expdir[-1] != "/": if expdir[-1] != "/":
expdir += "/" expdir += "/"
repodir = repodir.removesuffix("/") repodir = repodir.removesuffix("/")
env = {"RUST_LOG": "debug"} env = {"RUST_LOG": "debug"}
if ciphers: if ciphers:
env["CIPHERS"] = ",".join(ciphers) env["CIPHERS"] = ciphers
if kexes: if kexes:
env["KEXES"] = ",".join(KEXES) env["KEXES"] = kexes
return subprocess.Popen([repodir+"/rpxy_rustls_"+impl, "--config", expdir+"configs/"+config_name+".toml"], env=env)
def run_netreplay(expdir, repodir, record, p2_addr, p2_port, listen_port, tls_mode, only_record=None):
if expdir[-1] != "/":
expdir += "/"
repodir = repodir.removesuffix("/")
env = {"RUST_LOG": "debug"}
cmd = [repodir+"/netreplay", repodir+"/records/"+record["filename"], "play", p2_addr, str(p2_port), str(listen_port), expdir+"current_certs", tls_mode, "-r", str(record["repeat"])] cmd = [repodir+"/netreplay", repodir+"/records/"+record["filename"], "play", p2_addr, str(p2_port), str(listen_port), expdir+"current_certs", tls_mode, "-r", str(record["repeat"])]
if only_record != None: if only_record != None:
cmd += ["--record", only_record] cmd += ["--record", only_record]
@ -401,7 +401,7 @@ def get_net_stat(ssh):
bytes_out = int(items[8]) bytes_out = int(items[8])
return (bytes_in, bytes_out) return (bytes_in, bytes_out)
def run_exp(ssh, expdir, p2_path, exps, only_record=None): def run_exp(ssh, expdir, p2_path, exps, only_record=None, idle=False):
wattmeter = None wattmeter = None
if WATTMETER: if WATTMETER:
errmsg = YRefParam() errmsg = YRefParam()
@ -420,13 +420,58 @@ def run_exp(ssh, expdir, p2_path, exps, only_record=None):
ssh.run(f"killall rpxy_rustls_{impl}") ssh.run(f"killall rpxy_rustls_{impl}")
except invoke.exceptions.UnexpectedExit as e: except invoke.exceptions.UnexpectedExit as e:
pass pass
rpxy_cpu = get_cpu_stat(ssh) logfile_name = "log-"+str(int(time.time()))
logfile_name = expdir+"/log-"+str(int(time.time())) logfile_path = expdir+"/"+logfile_name
logfile = open(logfile_name, "w") logfile = open(logfile_path, "w")
logfile.write("exp impl alg kex cipher setup record time cpu bytes_in bytes_out Wh\n") logfile.write("exp impl alg kex cipher setup record time cpu bytes_in bytes_out Wh\n")
logfile.close() logfile.close()
if idle:
print("Measuring idle...")
rpxy_cpu = get_cpu_stat(ssh)
p2_bytes_in, p2_bytes_out = get_net_stat(ssh)
energy = 0
if WATTMETER:
energy = wattmeter.get_meter()
start = time.time()
time.sleep(600)
end = time.time()
new_energy = 0
if WATTMETER:
new_energy = wattmeter.get_meter()
new_p2_bytes_in, new_p2_bytes_out = get_net_stat(ssh)
new_rpxy_cpu = get_cpu_stat(ssh)
rpxy_cpu_diff = new_rpxy_cpu - rpxy_cpu
p2_bytes_in_diff = new_p2_bytes_in - p2_bytes_in
p2_bytes_out_diff = new_p2_bytes_out - p2_bytes_out
energy_diff = new_energy - energy
time_diff = end - start
while True:
try:
with open(logfile_path, "a") as logfile:
logfile.write(f"idle - - - - - - {time_diff} {rpxy_cpu_diff} {p2_bytes_in_diff} {p2_bytes_out_diff} {energy_diff}\n")
logfile.close()
break
except Exception as e:
print("Can't open log file:", e)
time.sleep(1)
else:
while True:
try:
with open(logfile_path, "a") as logfile:
logfile.write(IDLE+"\n")
logfile.close()
break
except Exception as e:
print("Can't open log file:", e)
time.sleep(1)
sh(f"cp {logfile_path} {LOG_BACKUP_DIR}/{logfile_name}")
for expname in exps: for expname in exps:
exp = exps[expname] exp = exps[expname]
first_set = True
for impl in exp["impls"]: for impl in exp["impls"]:
for alg in exp["cert"]: for alg in exp["cert"]:
for kex in exp["kexes"]: for kex in exp["kexes"]:
@ -434,6 +479,8 @@ def run_exp(ssh, expdir, p2_path, exps, only_record=None):
choose_cert_alg(expdir, alg) choose_cert_alg(expdir, alg)
ssh.run(f"python {p2_path}/exp.py cert {alg}") ssh.run(f"python {p2_path}/exp.py cert {alg}")
for setup in SETUPS: for setup in SETUPS:
if SETUPS[setup]["tls_invariant"] and not first_set:
continue
setupdir = expdir+"setups/"+setup setupdir = expdir+"setups/"+setup
for record in exp["records"]: for record in exp["records"]:
print(f"EXPERIMENT {expname}: {impl} {alg} {kex} {cipher} {setup}") print(f"EXPERIMENT {expname}: {impl} {alg} {kex} {cipher} {setup}")
@ -443,13 +490,14 @@ def run_exp(ssh, expdir, p2_path, exps, only_record=None):
runbg(ssh, f"{p2_path}/rpxy_rustls_{impl} --config {expdir}/configs/{p2_rpxy_config}.toml --log-dir /dev/shm", vars) runbg(ssh, f"{p2_path}/rpxy_rustls_{impl} --config {expdir}/configs/{p2_rpxy_config}.toml --log-dir /dev/shm", vars)
time.sleep(1) time.sleep(1)
rpxy_cpu = get_cpu_stat(ssh)
p2_bytes_in, p2_bytes_out = get_net_stat(ssh) p2_bytes_in, p2_bytes_out = get_net_stat(ssh)
energy = 0 energy = 0
if WATTMETER: if WATTMETER:
energy = wattmeter.get_meter() energy = wattmeter.get_meter()
start = time.time() start = time.time()
netreplay = run_netreplay(expdir, REPODIR, record, P2_ADDR, SETUPS[setup]["p2_port"], SETUPS[setup]["listen_port"], SETUPS[setup]["netreplay_tls_mode"], only_record=only_record) netreplay = run_netreplay(expdir, REPODIR, record, P2_ADDR, SETUPS[setup]["p2_port"], SETUPS[setup]["listen_port"], SETUPS[setup]["netreplay_tls_mode"], only_record=only_record, ciphers=cipher, kexes=kex)
# TODO detect when netreplay has finished # TODO detect when netreplay has finished
try: try:
@ -468,6 +516,10 @@ def run_exp(ssh, expdir, p2_path, exps, only_record=None):
#time.sleep(30) #time.sleep(30)
#sh("killall netreplay") #sh("killall netreplay")
try:
ssh.run(f"rm /dev/shm/access.log /dev/shm/rpxy.log")
except invoke.exceptions.UnexpectedExit as e:
pass
try: try:
ssh.run(f"killall rpxy_rustls_{impl}") ssh.run(f"killall rpxy_rustls_{impl}")
except invoke.exceptions.UnexpectedExit as e: except invoke.exceptions.UnexpectedExit as e:
@ -488,17 +540,18 @@ def run_exp(ssh, expdir, p2_path, exps, only_record=None):
p2_bytes_in_diff = new_p2_bytes_in - p2_bytes_in p2_bytes_in_diff = new_p2_bytes_in - p2_bytes_in
p2_bytes_out_diff = new_p2_bytes_out - p2_bytes_out p2_bytes_out_diff = new_p2_bytes_out - p2_bytes_out
energy_diff = new_energy - energy energy_diff = new_energy - energy
rpxy_cpu = new_rpxy_cpu
time_diff = end - start time_diff = end - start
while True: while True:
try: try:
with open(logfile_name, "a") as logfile: with open(logfile_path, "a") as logfile:
logfile.write(f"{expname} {impl} {alg} {kex} {cipher} {setup} {record_filename} {time_diff} {rpxy_cpu_diff} {p2_bytes_in_diff} {p2_bytes_out_diff} {energy_diff}\n") logfile.write(f"{expname} {impl} {alg} {kex} {cipher} {setup} {record_filename} {time_diff} {rpxy_cpu_diff} {p2_bytes_in_diff} {p2_bytes_out_diff} {energy_diff}\n")
logfile.close() logfile.close()
break break
except Exception as e: except Exception as e:
print("Can't open log file:", e) print("Can't open log file:", e)
time.sleep(1) time.sleep(1)
sh(f"cp {logfile_path} {LOG_BACKUP_DIR}/{logfile_name}")
first_set = False
if WATTMETER: if WATTMETER:
YAPI.FreeAPI() YAPI.FreeAPI()
@ -564,6 +617,7 @@ Run options:
--passphrase Prompt SSH key decryption passphrase (when using pubkey login) --passphrase Prompt SSH key decryption passphrase (when using pubkey login)
--count Do not run experiments but display number of experiments --count Do not run experiments but display number of experiments
--record <id> Only play this record --record <id> Only play this record
--idle Also measure when idle
""".format( """.format(
sig_algs=" ".join(CERT_SIGN_ALGS), sig_algs=" ".join(CERT_SIGN_ALGS),
impls=" ".join(IMPLS), impls=" ".join(IMPLS),
@ -607,7 +661,7 @@ Run options:
from yoctopuce.yocto_power import * from yoctopuce.yocto_power import *
ssh = connect_ssh() ssh = connect_ssh()
run_exp(ssh, EXPDIR, P2_REPODIR, EXPERIMENTS, only_record=getargv("--record", None)) run_exp(ssh, EXPDIR, P2_REPODIR, EXPERIMENTS, only_record=getargv("--record", None), idle="--idle" in sys.argv)
elif opt == "script": elif opt == "script":
print(SCRIPT_FIREFOX_HOSTS) print(SCRIPT_FIREFOX_HOSTS)
else: else:

148
plots.py Normal file
View file

@ -0,0 +1,148 @@
import os, sys
# Nice labels for algorithms of all kinds
ALG_LABEL = {
"AES_128_GCM_SHA256": "AES128",
"AES_256_GCM_SHA384": "AES256",
"CHACHA20_POLY1305_SHA256": "CHACHA20",
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256": "AES128",
"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384": "AES256",
"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": "CHACHA20",
"prime256v1": "prime256v1",
"rsa2048": "rsa2048",
"rsa3072": "rsa3072",
"rsa4096": "rsa4096",
"X25519": "X25519",
"SECP256R1": "SECP256R1",
"SECP384R1": "SECP384R1"
}
# Nice labels for TLS versions using ciphers
VER_LABEL = {
"AES_128_GCM_SHA256": "1.3",
"AES_256_GCM_SHA384": "1.3",
"CHACHA20_POLY1305_SHA256": "1.3",
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256": "1.2",
"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384": "1.2",
"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": "1.2"
}
# Titles for measured quantities
OBJ_TITLE = {
"cpu": "CPU time",
"energy": "energy consumption"
}
# Logfile column names
COL = {
"cpu": "cpu",
"energy": "Wh",
"cipher": "cipher",
"cert": "alg",
"kex": "kex"
}
# Physical units by object
UNIT = {
"cpu": "s",
"energy": "W"
}
# Titles for criteria
CRITERION_TITLE = {
"cipher": "cipher",
"cert": "signature algorithm",
"kex": "key exchange"
}
# Where gnuplot files, data files and images are output
PLOTS_DIR = "/dev/shm/plots"
def gnuplot_histogram(**kwargs):
cluster = ""
for i in range(kwargs["nb_impls"]-1):
cluster += """, "" using {}:xticlabels(1) title col""".format(i+4)
f = open("{plots_dir}/{object}_by_{criterion}_{side}.gnuplot".format(plots_dir=PLOTS_DIR, **kwargs), "w")
f.write("""\
set terminal pngcairo enhanced font "CMU Sans Serif,11" fontscale 1.0 size 800, 600
set output "{plots_dir}/{object}_by_{criterion}_{side}.png"
set boxwidth 0.9 absolute
set style fill solid 1.0 border lt -1
set style histogram clustered gap 1 title textcolor lt -1
set style data histograms
set title font "CMU Sans Serif,12" "{object_title} by {criterion_title} ({side} side) ({unit})"
set xtics border in scale 0,0 nomirror rotate by -45 autojustify
set key fixed right top vertical Right noreverse noenhanced autotitle nobox
set colorbox vertical origin screen 0.9, 0.2 size screen 0.05, 0.6 front noinvert bdefault
set xrange [ * : * ] noreverse writeback
plot \
newhistogram "", "{plots_dir}/{object}_by_{criterion}_{side}.dat" using 2:xticlabels(1) notitle col, \
newhistogram "", "{plots_dir}/{object}_by_{criterion}_{side}.dat" using 3:xticlabels(1) title col{cluster}
""".format(plots_dir=PLOTS_DIR, cluster=cluster, **kwargs))
f.close()
os.system("gnuplot {plots_dir}/{object}_by_{criterion}_{side}.gnuplot".format(plots_dir=PLOTS_DIR, **kwargs))
def make_plot(records, exp, criterion, side, obj):
f = open(f"/dev/shm/plots/{obj}_by_{criterion}_{side}.dat", "w")
ciphers = {}
impls = []
plain_line = None
idle_val = None
for record in records:
if record["exp"] == "idle":
idle_val = float(record[COL[obj]]) / float(record["time"])
if record["exp"] != exp:
continue
if record["setup"] == "none":
plain_line = "plain {}".format(float(record[COL[obj]]) - idle_val * float(record["time"]))
if plain_line == None:
return
for record in records:
if record["exp"] != exp:
continue
elif record["setup"] == side:
if record[COL[criterion]] not in ciphers:
ciphers[record[COL[criterion]]] = {}
ciphers[record[COL[criterion]]][record["impl"]] = float(record[COL[obj]]) - idle_val * float(record["time"])
if record["impl"] not in impls:
impls.append(record["impl"])
impls.sort()
f.write("{} none {}\n".format(criterion, " ".join(impls)))
f.write(plain_line+" -"*len(impls)+"\n")
for cipher in ciphers:
f.write("{}({}) - {}\n".format(
ALG_LABEL[cipher],
VER_LABEL[record["cipher"]],
" ".join([
str(ciphers[cipher][impl])
for impl in impls
]),
))
f.close()
gnuplot_histogram(object=obj, criterion=criterion, side=side, object_title=OBJ_TITLE[obj], criterion_title=CRITERION_TITLE[criterion], unit=UNIT[obj], nb_impls=len(impls))
if __name__ == "__main__":
logfile_name = sys.argv[1]
logfile = open(logfile_name, "r")
lines = logfile.readlines()
logfile.close()
colnames = lines[0].removesuffix("\n").split(" ")
records = []
for line in lines[1:]:
cols = line.removesuffix("\n").split(" ")
record = {}
for col in range(len(cols)):
record[colnames[col]] = cols[col]
records.append(record)
os.makedirs("/dev/shm/plots", exist_ok=True)
for side in ["client", "server"]:
make_plot(records, "impl-cipher-ver", "cipher", side, "cpu")
make_plot(records, "impl-cipher-ver", "cipher", side, "energy")
make_plot(records, "impl-cert-ver", "cert", side, "cpu")
make_plot(records, "impl-cert-ver", "cert", side, "energy")
make_plot(records, "impl-kex-ver", "kex", side, "cpu")
make_plot(records, "impl-kex-ver", "kex", side, "energy")

BIN
records/wikipedia Normal file

Binary file not shown.