Initial commit
This commit is contained in:
commit
a71491c068
13 changed files with 1470 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
netreplay
|
||||||
|
rpxy_*
|
||||||
270
README.md
Normal file
270
README.md
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
# TLS power measure benchmark
|
||||||
|
|
||||||
|
Goal: measure the power overhead of adding TLS to a client or a server, on realistic loads.
|
||||||
|
|
||||||
|
Realistic load implies using a real-world client (such as a web browser on a low-end device) and server (such as a video streaming platform on a typical server).
|
||||||
|
|
||||||
|
Problem: realistic clients have complex behaviors that go beyond a simple OpenSSL example code, and modern browsers and website won't work without HTTPS.
|
||||||
|
|
||||||
|
Assumption: most of the load added by the security layers is decoupled from the application (in both web browsers and web servers).
|
||||||
|
|
||||||
|
```
|
||||||
|
Experiments:
|
||||||
|
1: (Client) <=[TLS]=> (Proxy 1) <-[plain]-> (Proxy 2) <-[plain]-> (Proxy 3) <=[TLS]=> (Server)
|
||||||
|
2: (Client) <=[TLS]=> (Proxy 1) <==[TLS]==> (Proxy 2) <-[plain]-> (Proxy 3) <=[TLS]=> (Server)
|
||||||
|
3: (Client) <=[TLS]=> (Proxy 1) <-[plain]-> (Proxy 2) <==[TLS]==> (Proxy 3) <=[TLS]=> (Server)
|
||||||
|
^measure^
|
||||||
|
```
|
||||||
|
|
||||||
|
Client and server are identical in all experiments. The only modification made to the client is to add a trusted certificate owned by Proxy 1.
|
||||||
|
|
||||||
|
Only Proxy 2 must be a dedicated machine. The other parties may be placed on the same machine.
|
||||||
|
|
||||||
|
Call E1, E2, E3 the energy measured for Proxy 2 in experiments 1, 2, 3.
|
||||||
|
|
||||||
|
E2-E1 is the energy used to operate the server's half of a TLS connection.
|
||||||
|
|
||||||
|
E3-E1 is the energy used to operate the client's half of a TLS connection.
|
||||||
|
|
||||||
|
Client:
|
||||||
|
* Web browser trusting a certificate owned by Proxy 1
|
||||||
|
|
||||||
|
## Things to experiment
|
||||||
|
|
||||||
|
* Implementations
|
||||||
|
* OpenSSL 1.0.2
|
||||||
|
* OpenSSL 1.1.1
|
||||||
|
* OpenSSL 3.0
|
||||||
|
* OpenSSL 3.2
|
||||||
|
* OpenSSL 3.3
|
||||||
|
* OpenSSL 3.4
|
||||||
|
* OpenSSL 3.5
|
||||||
|
* OpenSSL 3.6
|
||||||
|
* [WolfSSL](https://github.com/wolfSSL/wolfssl)
|
||||||
|
* GnuTLS
|
||||||
|
* [NSS](https://github.com/nss-dev/nss) (used by Firefox) (?)
|
||||||
|
* opencryptoki (?)
|
||||||
|
* AWS-LC
|
||||||
|
* [BoringSSL](https://github.com/google/boringssl) (Chrome, Android)
|
||||||
|
* LibreSSL
|
||||||
|
* [SymCrypt](https://github.com/microsoft/SymCrypt) (used by Windows) ([install manually](https://github.com/microsoft/SymCrypt/releases))
|
||||||
|
* Versions
|
||||||
|
* TLS 1.2
|
||||||
|
* TLS 1.3
|
||||||
|
* Features
|
||||||
|
* TLS
|
||||||
|
* Ciphers
|
||||||
|
* TLS_AES_128_GCM_SHA256
|
||||||
|
* TLS_AES_256_GCM_SHA384
|
||||||
|
* TLS_CHACHA20_POLY1305_SHA256
|
||||||
|
* TLS_AES_128_CCM_SHA256
|
||||||
|
* TLS_AES_128_CCM_8_SHA256
|
||||||
|
* Key exchange groups
|
||||||
|
* secp256r1
|
||||||
|
* secp384r1
|
||||||
|
* secp521r1
|
||||||
|
* x25519
|
||||||
|
* x448
|
||||||
|
* ffdhe2048
|
||||||
|
* ffdhe3072
|
||||||
|
* ffdhe4096
|
||||||
|
* ffdhe6144
|
||||||
|
* ffdhe8192
|
||||||
|
* Signatures
|
||||||
|
* rsa_pkcs1_sha256
|
||||||
|
* rsa_pkcs1_sha384
|
||||||
|
* rsa_pkcs1_sha512
|
||||||
|
* ecdsa_secp256r1_sha256
|
||||||
|
* ecdsa_secp384r1_sha384
|
||||||
|
* ecdsa_secp521r1_sha512
|
||||||
|
* rsa_pss_rsae_sha256
|
||||||
|
* rsa_pss_rsae_sha384
|
||||||
|
* rsa_pss_rsae_sha512
|
||||||
|
* ed25519
|
||||||
|
* ed448
|
||||||
|
* rsa_pss_pss_sha256
|
||||||
|
* rsa_pss_pss_sha384
|
||||||
|
* rsa_pss_pss_sha512
|
||||||
|
* rsa_pkcs1_sha1
|
||||||
|
* ecdsa_sha1
|
||||||
|
* X.509
|
||||||
|
* Signature algorithm
|
||||||
|
* RSA2048
|
||||||
|
* RSA3072
|
||||||
|
* RSA4096
|
||||||
|
* EC 256
|
||||||
|
* EC 384
|
||||||
|
* SCT
|
||||||
|
* TODO !!!
|
||||||
|
* Usages
|
||||||
|
* Video streaming
|
||||||
|
* Full-speed download
|
||||||
|
* Real-time video call
|
||||||
|
* Mostly text browsing
|
||||||
|
* With or without ads
|
||||||
|
|
||||||
|
Most of the implementations can be used through RusTLS.
|
||||||
|
|
||||||
|
However RusTLS clients won't enable to force TLS1.2 if 1.3 is available.
|
||||||
|
|
||||||
|
### rpxy
|
||||||
|
|
||||||
|
Reverse-proxy utilisant RusTLS.
|
||||||
|
|
||||||
|
### WolfSSL
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/wolfSSL/wolfssl --depth 1
|
||||||
|
cd wolfssl
|
||||||
|
sh autogen.sh
|
||||||
|
./configure --enable-all --enable-all-crypto --disable-shared --prefix=/opt/wolfssl-rs/
|
||||||
|
make
|
||||||
|
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
|
||||||
|
|
||||||
|
* https://pub.h-brs.de/frontdoor/deliver/index/docId/4771/file/2019-ESP32-TLS-Power.pdf
|
||||||
|
* 2019
|
||||||
|
* TLS 1.2, 1.3
|
||||||
|
* ESP32
|
||||||
|
* WolfSSL
|
||||||
|
* https://www.semanticscholar.org/paper/Energy-Consumption-Framework-and-Analysis-of-on-Patterson-Buchanan/706736a29cef777e5dc50ba22b4788b2bfb4c6ef
|
||||||
|
* 2025
|
||||||
|
* RaspberryPi
|
||||||
|
* OpenSSL
|
||||||
|
* ML-KEM
|
||||||
|
* https://www.semanticscholar.org/paper/Energy-Profiling-and-Comparison-of-TLS-Protocols-Gatram-Reddy/9c061fe57a0008574b85919bc70fc803c6e66f06
|
||||||
|
* 2024
|
||||||
|
* RaspberryPi
|
||||||
|
* TLS PQ, TLS KEM, TLS
|
||||||
|
* https://www.semanticscholar.org/paper/Energy-Consumption-Evaluation-of-Post-Quantum-TLS-Tasopoulos-Dimopoulos/2ffc6d13349e2fa5f89aaf18e69ce2044ecef4fe
|
||||||
|
* 2023
|
||||||
|
* STM Nucleo
|
||||||
|
* WolfSSL
|
||||||
|
* TLS 1.3 PQ
|
||||||
|
* https://arxiv.org/pdf/2508.04583v2
|
||||||
|
* 2025
|
||||||
|
* TLS 1.3
|
||||||
|
* Nginx + Python requests
|
||||||
|
* https://github.com/MarcT0K/privacy-carbon-experiments
|
||||||
|
* https://davidtnaylor.com/CostOfTheS.pdf
|
||||||
|
* 2014
|
||||||
|
*
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
* [RFC8446 (TLS 1.3)](https://datatracker.ietf.org/doc/html/rfc8446)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Install sa on p2:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install acct
|
||||||
|
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 rpxy_rustls_ring
|
||||||
|
```
|
||||||
|
|
||||||
|
Add CA certificate on ArchLinux:
|
||||||
|
```bash
|
||||||
|
sudo cp /dev/shm/exp/certs/rsa2048/ca.crt /etc/ca-certificates/trust-source/anchors/ca-rsa2048.crt
|
||||||
|
sudo cp /dev/shm/exp/certs/rsa3072/ca.crt /etc/ca-certificates/trust-source/anchors/ca-rsa3072.crt
|
||||||
|
sudo cp /dev/shm/exp/certs/rsa4096/ca.crt /etc/ca-certificates/trust-source/anchors/ca-rsa4096.crt
|
||||||
|
sudo cp /dev/shm/exp/certs/secp384r1/ca.crt /etc/ca-certificates/trust-source/anchors/ca-secp384r1.crt
|
||||||
|
sudo chown root:root /etc/ca-certificates/trust-source/anchors/ca-*.crt
|
||||||
|
sudo update-ca-trust extract
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python exp.py make
|
||||||
|
python exp.py send-setups
|
||||||
|
python exp.py send-certs
|
||||||
|
python exp.py run
|
||||||
|
```
|
||||||
|
|
||||||
|
On Debian, update-certs says 0 certs added even if it has actually updated some certs. This step is still needed.
|
||||||
|
|
||||||
|
## Problems
|
||||||
|
|
||||||
|
### Youtube gives 502 bad gateway.
|
||||||
|
|
||||||
|
* Works with bare curl. (not hiding we're a bot)
|
||||||
|
* We have same JA3 fingerprint as Firefox.
|
||||||
|
* HTTP request is intact.
|
||||||
|
* JA4 fingerprint different from Firefox but existing for some browsers.
|
||||||
|
|
||||||
|
### Modèle d'expérience à revoir
|
||||||
|
|
||||||
|
* Le relai d'une vidéo streaming avec TLS prend max 3% d'un cœur sur le Pi3, soit pas beaucoup plus que le bruit.
|
||||||
|
* Il faudrait spammer avec plusieurs connexions pour voir un effet significatif.
|
||||||
|
* On peut spammer le streaming vidéo, mais pas le reste (antibots).
|
||||||
|
* On ne devrait pas spammer sur Renater...
|
||||||
|
* Il faut donc tout faire en local.
|
||||||
|
|
||||||
|
Solutions :
|
||||||
|
|
||||||
|
* Copier les sites en statique et les servir avec Apache. => OK pour des sites propres genre Wikipedia, WordPress (à voir pour la pub)
|
||||||
|
* Installer des instances => Peertube, WordPress
|
||||||
|
* Copier le trafic et le rejouer => risque de demander beaucoup de dev
|
||||||
|
|
||||||
|
### Youtube
|
||||||
|
|
||||||
|
Youtube utilise des trucs aléatoires en `RANDOM.googlevideo.com` pour la vidéo. Cependant il y a quelques domaines utilisés qui ne changent pas, du moins sur un même navigateur avec la même vidéo et sur une courte période.
|
||||||
|
Avant d'enregistrer le trafic, il faut observer les domaines utilisés puis générer les certificats et les redirections en fonction.
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
* Partie serveur sans TLS de netreplay -> sans SNI, il faut parser le HTTP >.<
|
||||||
|
* exp.py: détecter la fin du replay
|
||||||
|
* yoctowatt
|
||||||
|
* mesures CPU, mémoire, bande passante sur p2
|
||||||
|
* CPU: paquet acct, commande `sa -m`
|
||||||
604
exp.py
Normal file
604
exp.py
Normal file
|
|
@ -0,0 +1,604 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
import os, sys, subprocess
|
||||||
|
|
||||||
|
REPODIR = "/home/tuxmain/Documents/doc/tlsbench"
|
||||||
|
P2_SSH = "exp@p2"
|
||||||
|
P2_PSW = "exp"
|
||||||
|
P2_REPODIR = "/home/exp/exp"
|
||||||
|
EXPDIR = "/dev/shm/exp"
|
||||||
|
P2_ADDR = "192.168.3.14"
|
||||||
|
DOMAINS_ = [
|
||||||
|
# Apple
|
||||||
|
"apple.com", "www.apple.com", "graffiti-tags.apple.com", "securemetrics.apple.com",
|
||||||
|
"store.storeimages.cdn-apple.com",
|
||||||
|
"mzstatic.com", "is1-ssl.mzstatic.com",
|
||||||
|
# Youtube video
|
||||||
|
"youtube.com", "www.youtube.com",
|
||||||
|
"i.ytimg.com",
|
||||||
|
"fonts.gstatic.com", "www.gstatic.com",
|
||||||
|
"www.google.com", "accounts.google.com",
|
||||||
|
"yt3.ggpht.com",
|
||||||
|
"rr1---sn-gxo5uxg-jqbl.googlevideo.com",
|
||||||
|
"rr2---sn-gxo5uxg-jqbl.googlevideo.com",
|
||||||
|
"rr4---sn-q4fl6nds.googlevideo.com",
|
||||||
|
# Amazon
|
||||||
|
"amazon.com", "www.amazon.com",
|
||||||
|
# Wikipedia article
|
||||||
|
"fr.wikipedia.org", "upload.wikimedia.org",
|
||||||
|
# Google search
|
||||||
|
"www.google.com", "www.googleadservices.com", "www.gstatic.com", "csp.withgoogle.com", "ogads-pa.clients6.google.com", "play.google.com", "ssl.gstatic.com", "fonts.gstatic.com", "ogs.google.com",
|
||||||
|
# Peertube video
|
||||||
|
"videos.domainepublic.net",
|
||||||
|
]
|
||||||
|
WATTMETER = True
|
||||||
|
|
||||||
|
RECORDS = [
|
||||||
|
{ "filename": "youtube", "repeat": 10 },
|
||||||
|
#{ "filename": "peertube", "repeat": 10 },
|
||||||
|
#{ "filename": "wikipedia", "repeat": 10 },
|
||||||
|
#{ "filename": "apple", "repeat": 10 },
|
||||||
|
#{ "filename": "google", "repeat": 10 },
|
||||||
|
]
|
||||||
|
CERT_SIGN_ALGS = [
|
||||||
|
"prime256v1", # widely used
|
||||||
|
"secp384r1", # rarely used but supported by browsers because it's NIST standard
|
||||||
|
#"secp521r1", # not supported by browsers because NIST said it was not needed
|
||||||
|
"rsa2048", "rsa3072", "rsa4096", # widely used
|
||||||
|
]
|
||||||
|
IMPLS = [
|
||||||
|
#"aws_lc_rs", # Amazon's Rust crypto widely used in Rust stuff
|
||||||
|
#"boring", # Google's fork of OpenSSL used in Chrome and Android
|
||||||
|
"openssl", # widely used
|
||||||
|
#"ring", # used in most Rust stuff
|
||||||
|
#"symcrypt", # Microsoft's crypto
|
||||||
|
#"wolfcrypt" # used in embedded (won't build with rpxy for now)
|
||||||
|
]
|
||||||
|
# Symmetric ciphers
|
||||||
|
# They also allow to choose the TLS version.
|
||||||
|
CIPHERS = [
|
||||||
|
# TLS 1.3
|
||||||
|
"AES_256_GCM_SHA384",
|
||||||
|
"AES_128_GCM_SHA256",
|
||||||
|
"CHACHA20_POLY1305_SHA256",
|
||||||
|
# TLS 1.2
|
||||||
|
# ECDSA vs RSA refers to the certificate signature algorithm.
|
||||||
|
# DH is EC in either case, using the group specified below.
|
||||||
|
"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"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 = [
|
||||||
|
"X25519",
|
||||||
|
"SECP256R1",
|
||||||
|
"SECP384R1",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Testing all combinations would be too much. Instead we isolate independent parts.
|
||||||
|
EXPERIMENTS = {
|
||||||
|
# Compare ciphers among implementations and TLS versions
|
||||||
|
# "impl-cipher-ver": {
|
||||||
|
# "impls": IMPLS,
|
||||||
|
# "records": RECORDS,
|
||||||
|
# "ciphers": [
|
||||||
|
# "AES_128_GCM_SHA256",
|
||||||
|
# "AES_256_GCM_SHA384",
|
||||||
|
# "CHACHA20_POLY1305_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_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||||
|
# ],
|
||||||
|
# "kexes": ["X25519"],
|
||||||
|
# "cert": ["prime256v1"],
|
||||||
|
# },
|
||||||
|
# # Compare signatures among implementations and TLS versions
|
||||||
|
# "impl-cert-ver": {
|
||||||
|
# "impls": IMPLS,
|
||||||
|
# "records": RECORDS,
|
||||||
|
# "ciphers": [
|
||||||
|
# "AES_128_GCM_SHA256",
|
||||||
|
# "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
# ],
|
||||||
|
# "kexes": ["X25519"],
|
||||||
|
# "cert": [
|
||||||
|
# "prime256v1",
|
||||||
|
# #"secp384r1",
|
||||||
|
# "rsa2048",
|
||||||
|
# #"rsa3072", "rsa4096"
|
||||||
|
# ],
|
||||||
|
# },
|
||||||
|
# # Compare key exchange groups among implementations and TLS versions
|
||||||
|
# "impl-kex-ver": {
|
||||||
|
# "impls": IMPLS,
|
||||||
|
# "records": RECORDS,
|
||||||
|
# "ciphers": [
|
||||||
|
# "AES_128_GCM_SHA256",
|
||||||
|
# "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
# ],
|
||||||
|
# "kexes": ["X25519", "SECP256R1", "SECP384R1"],
|
||||||
|
# "cert": ["prime256v1"],
|
||||||
|
# },
|
||||||
|
"debug": {
|
||||||
|
"impls": IMPLS,
|
||||||
|
"records": RECORDS,
|
||||||
|
"ciphers": [
|
||||||
|
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
],
|
||||||
|
"kexes": ["SECP384R1"],
|
||||||
|
"cert": ["prime256v1"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
DOMAINS = []
|
||||||
|
for domain in DOMAINS_:
|
||||||
|
if not domain in DOMAINS:
|
||||||
|
DOMAINS.append(domain)
|
||||||
|
|
||||||
|
# JS to redirect the target domains to local (bypass DNS without altering system's config or webpages or packets)
|
||||||
|
SCRIPT_FIREFOX_HOSTS = """const gOverride = Cc["@mozilla.org/network/native-dns-override;1"].getService(Ci.nsINativeDNSResolverOverride);
|
||||||
|
gOverride.clearOverrides();
|
||||||
|
var names = """+str(DOMAINS)+""";
|
||||||
|
for(var i in names) {
|
||||||
|
gOverride.addIPOverride(names[i], "127.0.0.1");
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def getargv(arg:str, default="", n:int=1, args:list=sys.argv):
|
||||||
|
if arg in args and len(args) > args.index(arg)+n:
|
||||||
|
return args[args.index(arg)+n]
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def sh(cmds):
|
||||||
|
if type(cmds) == list:
|
||||||
|
for cmd in cmds:
|
||||||
|
print(cmd)
|
||||||
|
os.system(cmd)
|
||||||
|
elif type(cmds) == str:
|
||||||
|
print(cmds)
|
||||||
|
os.system(cmds)
|
||||||
|
else:
|
||||||
|
raise TypeError
|
||||||
|
|
||||||
|
def make_sk(outpath, alg):
|
||||||
|
sh({
|
||||||
|
"ed25519": [
|
||||||
|
f"openssl genpkey -out {outpath}.sec1 -algorithm ed25519",
|
||||||
|
f"openssl pkcs8 -topk8 -nocrypt -in {outpath}.sec1 -out {outpath} -outform PEM"
|
||||||
|
],
|
||||||
|
"prime256v1": [
|
||||||
|
f"openssl ecparam -genkey -name prime256v1 -noout -out {outpath}.sec1",
|
||||||
|
f"openssl pkcs8 -topk8 -nocrypt -in {outpath}.sec1 -out {outpath} -outform PEM"
|
||||||
|
],
|
||||||
|
"rsa2048": [
|
||||||
|
f"openssl genrsa -out {outpath} 2048"
|
||||||
|
],
|
||||||
|
"rsa3072": [
|
||||||
|
f"openssl genrsa -out {outpath} 3072"
|
||||||
|
],
|
||||||
|
"rsa4096": [
|
||||||
|
f"openssl genrsa -out {outpath} 4096"
|
||||||
|
],
|
||||||
|
"secp384r1": [
|
||||||
|
f"openssl ecparam -genkey -name secp384r1 -noout -out {outpath}.sec1",
|
||||||
|
f"openssl pkcs8 -topk8 -nocrypt -in {outpath}.sec1 -out {outpath} -outform PEM"
|
||||||
|
],
|
||||||
|
}[alg])
|
||||||
|
|
||||||
|
def make_ca_cert(outpath, skpath):
|
||||||
|
sh(f"openssl req -x509 -new -key {skpath} -sha256 -days 730 -out {outpath} -subj '/CN=Foobar Root CA/C=AT/ST=Lyon/L=Lyon/O=Foobar'")
|
||||||
|
|
||||||
|
def make_cert(outpath, skpath, capath, caskpath, name, domains):
|
||||||
|
sh(f"openssl req -new -key {skpath} -sha256 -out /tmp/tmp.csr -subj '/CN={name}/C=AT/ST=Lyon/L=Lyon/O={name}'")
|
||||||
|
ext = open("/tmp/tmp.v3.ext", "w")
|
||||||
|
ext.write("""authorityKeyIdentifier=keyid,issuer
|
||||||
|
basicConstraints=CA:FALSE
|
||||||
|
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
|
||||||
|
subjectAltName = @alt_names
|
||||||
|
[alt_names]
|
||||||
|
""")
|
||||||
|
i = 1
|
||||||
|
for domain in domains:
|
||||||
|
ext.write(f"DNS.{i} = {domain}\n")
|
||||||
|
i += 1
|
||||||
|
ext.write(f"DNS.{i} = {domain}.localhost\n")
|
||||||
|
i += 1
|
||||||
|
ext.write(f"DNS.{i} = {domain}.p2\n")
|
||||||
|
i += 1
|
||||||
|
ext.write(f"DNS.{i} = {domain}.p3\n")
|
||||||
|
i += 1
|
||||||
|
ext.close()
|
||||||
|
|
||||||
|
sh(f"openssl x509 -req -in /tmp/tmp.csr -CA {capath} -CAkey {caskpath} -CAcreateserial -out {outpath} -days 365 -sha256 -extfile /tmp/tmp.v3.ext")
|
||||||
|
|
||||||
|
def get_domain_root(domain):
|
||||||
|
last_dot = domain.rfind(".")
|
||||||
|
penultimate_dot = domain.rfind(".", 0, last_dot)
|
||||||
|
return domain[penultimate_dot+1:]
|
||||||
|
|
||||||
|
# Issue secret keys, CA cert and signed certs for given domains
|
||||||
|
# All using the same algorithm
|
||||||
|
def make_certs(outdir, domains, alg, make_ca):
|
||||||
|
if outdir[-1] != "/":
|
||||||
|
outdir += "/"
|
||||||
|
|
||||||
|
if make_ca:
|
||||||
|
make_sk(outdir+"ca.key", alg)
|
||||||
|
make_ca_cert(outdir+"ca.crt", outdir+"ca.key")
|
||||||
|
|
||||||
|
# Only make certs for root domains, and include subdomains
|
||||||
|
roots = {}
|
||||||
|
for domain in domains:
|
||||||
|
root = domain
|
||||||
|
if domain.count(".") > 1:
|
||||||
|
root = get_domain_root(domain)
|
||||||
|
if root in roots:
|
||||||
|
roots[root].append(domain)
|
||||||
|
else:
|
||||||
|
roots[root] = [domain]
|
||||||
|
for root in roots:
|
||||||
|
make_sk(outdir+root+".key", alg)
|
||||||
|
make_cert(outdir+root+".crt", outdir+root+".key", outdir+"ca.crt", outdir+"ca.key", root, roots[root])
|
||||||
|
|
||||||
|
# Make a cert for all domains because choosing a certificate as a proxy is a real pain
|
||||||
|
make_sk(outdir+"all.key", alg)
|
||||||
|
make_cert(outdir+"all.crt", outdir+"all.key", outdir+"ca.crt", outdir+"ca.key", "wikipedia.org", domains)
|
||||||
|
|
||||||
|
RPXY_CONFIGS = {
|
||||||
|
"plain": {
|
||||||
|
"listen_http": 80,
|
||||||
|
"listen_https": 443,
|
||||||
|
"app": """[apps.{app}]
|
||||||
|
server_name = "{domain}"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}" }}], set_host = "{domain}"}}]
|
||||||
|
[apps.{app}_localhost]
|
||||||
|
server_name = "{domain}.localhost"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}" }}], set_host = "{domain}"}}]
|
||||||
|
[apps.{app}_p2]
|
||||||
|
server_name = "{domain}.p2"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}" }}], set_host = "{domain}"}}]
|
||||||
|
"""
|
||||||
|
},
|
||||||
|
"tls": {
|
||||||
|
"listen_http": 80,
|
||||||
|
"listen_https": 443,
|
||||||
|
"app": """[apps.{app}]
|
||||||
|
server_name = "{domain}"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}", tls = true }}], set_host = "{domain}"}}]
|
||||||
|
[apps.{app}_localhost]
|
||||||
|
server_name = "{domain}.localhost"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}", tls = true }}], set_host = "{domain}"}}]
|
||||||
|
[apps.{app}_p2]
|
||||||
|
server_name = "{domain}.p2"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}", tls = true }}], set_host = "{domain}"}}]
|
||||||
|
"""
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
SETUPS = {
|
||||||
|
"none": {
|
||||||
|
"rpxy_config": "plain",
|
||||||
|
"netreplay_tls_mode": "none",
|
||||||
|
"p2_port": 80,
|
||||||
|
"listen_port": 80,
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"rpxy_config": "tls",
|
||||||
|
"netreplay_tls_mode": "server",
|
||||||
|
"p2_port": 80,
|
||||||
|
"listen_port": 443,
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"rpxy_config": "plain",
|
||||||
|
"netreplay_tls_mode": "client",
|
||||||
|
"p2_port": 443,
|
||||||
|
"listen_port": 80,
|
||||||
|
},
|
||||||
|
#"both": {
|
||||||
|
# "rpxy_config": "tls",
|
||||||
|
# "netreplay_tls_mode": "both",
|
||||||
|
# "p2_port": 443,
|
||||||
|
# "listen_port": 443,
|
||||||
|
#},
|
||||||
|
}
|
||||||
|
|
||||||
|
def make_rpxy_config(outdir, domains, cryptodir, config_name):
|
||||||
|
if outdir[-1] != "/":
|
||||||
|
outdir += "/"
|
||||||
|
if cryptodir[-1] != "/":
|
||||||
|
cryptodir += "/"
|
||||||
|
|
||||||
|
config = RPXY_CONFIGS[config_name]
|
||||||
|
f = open(outdir+config_name+".toml", "w")
|
||||||
|
f.write("listen_port = {}\nlisten_port_tls = {}\n".format(config["listen_http"], config["listen_https"]))
|
||||||
|
for domain in domains:
|
||||||
|
app = domain.replace(".", "_")
|
||||||
|
root = get_domain_root(domain)
|
||||||
|
f.write(config["app"].format(
|
||||||
|
app=app,
|
||||||
|
domain=domain,
|
||||||
|
cert=cryptodir+root+".crt",
|
||||||
|
key=cryptodir+root+".key",
|
||||||
|
))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def make_everything(expdir, domains, make_ca):
|
||||||
|
os.makedirs(expdir, exist_ok=True)
|
||||||
|
if expdir[-1] != "/":
|
||||||
|
expdir += "/"
|
||||||
|
for alg in CERT_SIGN_ALGS:
|
||||||
|
algdir = expdir+"certs/"+alg
|
||||||
|
os.makedirs(algdir, exist_ok=True)
|
||||||
|
make_certs(algdir, domains, alg, make_ca)
|
||||||
|
# this will be a symbolic link to the chosen certs directory
|
||||||
|
cryptodir = expdir+"current_certs"
|
||||||
|
configdir = expdir+"configs/"
|
||||||
|
os.makedirs(configdir, exist_ok=True)
|
||||||
|
for config_name in RPXY_CONFIGS:
|
||||||
|
make_rpxy_config(configdir, domains, cryptodir, config_name)
|
||||||
|
|
||||||
|
def choose_cert_alg(expdir, alg):
|
||||||
|
if expdir[-1] != "/":
|
||||||
|
expdir += "/"
|
||||||
|
try:
|
||||||
|
os.unlink(expdir+"current_certs")
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
pass
|
||||||
|
# WHY is dst pointing to src?????
|
||||||
|
os.symlink(expdir+"certs/"+alg, expdir+"current_certs", True)
|
||||||
|
|
||||||
|
def choose_impl(expdir, p, impl):
|
||||||
|
if expdir[-1] != "/":
|
||||||
|
expdir += "/"
|
||||||
|
os.symlink(os.getcwd()+"/rpxy_rustls_"+impl, expdir+str(p)+"_rpxy", False)
|
||||||
|
|
||||||
|
def run_rpxy(expdir, repodir, config_name, impl, ciphers=None, kexes=None):
|
||||||
|
if expdir[-1] != "/":
|
||||||
|
expdir += "/"
|
||||||
|
repodir = repodir.removesuffix("/")
|
||||||
|
env = {"RUST_LOG": "debug"}
|
||||||
|
if ciphers:
|
||||||
|
env["CIPHERS"] = ",".join(ciphers)
|
||||||
|
if kexes:
|
||||||
|
env["KEXES"] = ",".join(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"])]
|
||||||
|
if only_record != None:
|
||||||
|
cmd += ["--record", only_record]
|
||||||
|
print(" ".join(cmd))
|
||||||
|
return subprocess.Popen(cmd, env=env)
|
||||||
|
|
||||||
|
# https://stackoverflow.com/questions/8775598/how-to-start-a-background-process-with-nohup-using-fabric
|
||||||
|
def runbg(ssh, cmd, vars={}):
|
||||||
|
print("[SSH]", cmd)
|
||||||
|
strvars = ""
|
||||||
|
for var in vars:
|
||||||
|
strvars += f"export {var}="+vars[var]+" && "
|
||||||
|
return ssh.run(f"{strvars}dtach -n `mktemp -u /tmp/dtach.XXXX` {cmd}")
|
||||||
|
|
||||||
|
def get_cpu_stat(ssh):
|
||||||
|
res = ssh.run("/sbin/sa --list-all-names", hide=True)
|
||||||
|
for line in res.stdout.split("\n"):
|
||||||
|
if "rpxy" in line:
|
||||||
|
return float(re.finditer("\\s(\\d+\\.\\d+)cp\\s", line).__next__().group(1))
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
def get_net_stat(ssh):
|
||||||
|
res = ssh.run("cat /proc/net/netstat", hide=True)
|
||||||
|
items = res.stdout.split("\n")[3].split(" ")
|
||||||
|
bytes_in = int(items[7])
|
||||||
|
bytes_out = int(items[8])
|
||||||
|
return (bytes_in, bytes_out)
|
||||||
|
|
||||||
|
def run_exp(ssh, expdir, p2_path, exps, only_record=None):
|
||||||
|
wattmeter = None
|
||||||
|
if WATTMETER:
|
||||||
|
errmsg = YRefParam()
|
||||||
|
if YAPI.RegisterHub("usb", errmsg) != YAPI.SUCCESS:
|
||||||
|
sys.exit("init error" + errmsg.value)
|
||||||
|
wattmeter = YPower.FirstPower()
|
||||||
|
if wattmeter is None or not wattmeter.isOnline():
|
||||||
|
print("No YoctoWatt connected")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
sh("killall netreplay")
|
||||||
|
for expname in exps:
|
||||||
|
exp = exps[expname]
|
||||||
|
for impl in exp["impls"]:
|
||||||
|
try:
|
||||||
|
ssh.run(f"killall rpxy_rustls_{impl}")
|
||||||
|
except invoke.exceptions.UnexpectedExit as e:
|
||||||
|
pass
|
||||||
|
rpxy_cpu = get_cpu_stat(ssh)
|
||||||
|
logfile = open(expdir+"/log-"+str(int(time.time())), "w")
|
||||||
|
logfile.write("exp impl alg kex cipher setup record t_start t_end cpu bytes_in bytes_out Wh\n")
|
||||||
|
for expname in exps:
|
||||||
|
exp = exps[expname]
|
||||||
|
for impl in exp["impls"]:
|
||||||
|
for alg in exp["cert"]:
|
||||||
|
for kex in exp["kexes"]:
|
||||||
|
for cipher in exp["ciphers"]:
|
||||||
|
choose_cert_alg(expdir, alg)
|
||||||
|
ssh.run(f"python {p2_path}/exp.py cert {alg}")
|
||||||
|
for setup in SETUPS:
|
||||||
|
setupdir = expdir+"setups/"+setup
|
||||||
|
for record in exp["records"]:
|
||||||
|
print(f"EXPERIMENT {expname}: {impl} {alg} {kex} {cipher} {setup}")
|
||||||
|
p2_rpxy_config = SETUPS[setup]["rpxy_config"]
|
||||||
|
vars = {"CIPHERS": cipher, "KEXES": kex}
|
||||||
|
|
||||||
|
runbg(ssh, f"{p2_path}/rpxy_rustls_{impl} --config {expdir}/configs/{p2_rpxy_config}.toml --log-dir /dev/shm", vars)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
p2_bytes_in, p2_bytes_out = get_net_stat(ssh)
|
||||||
|
energy = 0
|
||||||
|
if WATTMETER:
|
||||||
|
energy = wattmeter.get_meter()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# TODO detect when netreplay has finished
|
||||||
|
try:
|
||||||
|
netreplay.wait()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
netreplay.kill()
|
||||||
|
try:
|
||||||
|
ssh.run(f"killall rpxy_rustls_{impl}")
|
||||||
|
except invoke.exceptions.UnexpectedExit as e:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
ssh.run(f"killall dtach")
|
||||||
|
except invoke.exceptions.UnexpectedExit as e:
|
||||||
|
pass
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
#time.sleep(30)
|
||||||
|
#sh("killall netreplay")
|
||||||
|
try:
|
||||||
|
ssh.run(f"killall rpxy_rustls_{impl}")
|
||||||
|
except invoke.exceptions.UnexpectedExit as e:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
ssh.run(f"killall dtach")
|
||||||
|
except invoke.exceptions.UnexpectedExit as e:
|
||||||
|
pass
|
||||||
|
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)
|
||||||
|
record_filename = record["filename"]
|
||||||
|
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
|
||||||
|
rpxy_cpu = new_rpxy_cpu
|
||||||
|
logfile.write(f"{expname} {impl} {alg} {kex} {cipher} {setup} {record_filename} {start} {end} {rpxy_cpu_diff} {p2_bytes_in_diff} {p2_bytes_out_diff} {energy}\n")
|
||||||
|
|
||||||
|
logfile.flush()
|
||||||
|
if WATTMETER:
|
||||||
|
YAPI.FreeAPI()
|
||||||
|
|
||||||
|
def update_certs():
|
||||||
|
dist = platform.freedesktop_os_release()["ID"]
|
||||||
|
if dist == "debian":
|
||||||
|
for alg in CERT_SIGN_ALGS:
|
||||||
|
sh([
|
||||||
|
f"sudo cp {EXPDIR}/certs/{alg}/ca.crt /usr/local/share/ca-certificates/ca-{alg}.crt",
|
||||||
|
f"sudo chown root:root /usr/local/share/ca-certificates/ca-{alg}.crt"
|
||||||
|
])
|
||||||
|
sh("sudo update-ca-certificates")
|
||||||
|
elif dist == "arch":
|
||||||
|
for alg in CERT_SIGN_ALGS:
|
||||||
|
sh([
|
||||||
|
f"sudo cp {EXPDIR}/certs/{alg}/ca.crt /etc/ca-certificates/trust-source/anchors/ca-{alg}.crt",
|
||||||
|
f"sudo chown root:root /etc/ca-certificates/trust-source/anchors/ca-{alg}.crt"
|
||||||
|
])
|
||||||
|
sh("sudo update-ca-trust extract")
|
||||||
|
|
||||||
|
# copy local dir src to remote parent dir dst (src's copy will be a subdir of dst)
|
||||||
|
def upload_dir(ssh, src, dst):
|
||||||
|
src = src.removesuffix("/")
|
||||||
|
src_name = src.split("/")[-1]
|
||||||
|
os.chdir(f"{src}/..")
|
||||||
|
os.system(f"tar -czf {src}/../tmp.tar.gz {src_name}")
|
||||||
|
print(ssh.put(src+"/../tmp.tar.gz", dst))
|
||||||
|
print(ssh.run(f"cd {dst} && tar -xf tmp.tar.gz"))
|
||||||
|
|
||||||
|
def connect_ssh():
|
||||||
|
ssh_passphrase = "--passphrase" in sys.argv
|
||||||
|
connect_kwargs = {}
|
||||||
|
if ssh_passphrase:
|
||||||
|
import getpass
|
||||||
|
connect_kwargs["passphrase"] = getpass.getpass("Enter passphrase for SSH key: ")
|
||||||
|
if P2_PSW != None:
|
||||||
|
connect_kwargs["password"] = P2_PSW
|
||||||
|
ssh = fabric.Connection(P2_SSH, connect_kwargs=connect_kwargs)
|
||||||
|
return ssh
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 2 or sys.argv[1] in ["h", "help", "?", "-h", "-help", "--help", "/?"]:
|
||||||
|
print("""Options:
|
||||||
|
make [-c] Create everything
|
||||||
|
cert <alg> Select cert signature algorithm
|
||||||
|
send Send configs and certs to p2
|
||||||
|
update-certs Update system's certs
|
||||||
|
run Run experiment
|
||||||
|
script Print Firefox script to override DNS
|
||||||
|
|
||||||
|
Signature algorithms:
|
||||||
|
{sig_algs}
|
||||||
|
|
||||||
|
Implementations:
|
||||||
|
{impls}
|
||||||
|
|
||||||
|
Make options:
|
||||||
|
-c Make CA cert (otherwise use already existing one)
|
||||||
|
|
||||||
|
Run options:
|
||||||
|
--passphrase Prompt SSH key decryption passphrase (when using pubkey login)
|
||||||
|
--count Do not run experiments but display number of experiments
|
||||||
|
--record <id> Only play this record
|
||||||
|
""".format(
|
||||||
|
sig_algs=" ".join(CERT_SIGN_ALGS),
|
||||||
|
impls=" ".join(IMPLS),
|
||||||
|
))
|
||||||
|
exit()
|
||||||
|
|
||||||
|
opt = sys.argv[1]
|
||||||
|
if opt == "make":
|
||||||
|
make_ca = "-c" in sys.argv
|
||||||
|
make_everything(EXPDIR, DOMAINS, make_ca)
|
||||||
|
elif opt == "cert":
|
||||||
|
alg = sys.argv[2]
|
||||||
|
if not alg in CERT_SIGN_ALGS:
|
||||||
|
print("Error: alg must be in", CERT_SIGN_ALGS)
|
||||||
|
exit(1)
|
||||||
|
choose_cert_alg(EXPDIR, alg)
|
||||||
|
elif opt == "send":
|
||||||
|
import fabric
|
||||||
|
ssh = connect_ssh()
|
||||||
|
upload_dir(ssh, EXPDIR, "/dev/shm")
|
||||||
|
elif opt == "update-certs":
|
||||||
|
import platform
|
||||||
|
update_certs()
|
||||||
|
elif opt == "run":
|
||||||
|
if "--count" in sys.argv:
|
||||||
|
exps = 0
|
||||||
|
for expname in EXPERIMENTS:
|
||||||
|
exp = EXPERIMENTS[expname]
|
||||||
|
exps += len(exp["impls"]) * len(exp["cert"]) * len(exp["kexes"]) * len(exp["ciphers"]) * len(exp["records"])
|
||||||
|
print("Experiments to make:", exps * len(SETUPS))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
import time
|
||||||
|
import invoke
|
||||||
|
import re
|
||||||
|
import fabric
|
||||||
|
|
||||||
|
if WATTMETER:
|
||||||
|
import yoctopuce
|
||||||
|
from yoctopuce.yocto_api import *
|
||||||
|
from yoctopuce.yocto_power import *
|
||||||
|
|
||||||
|
ssh = connect_ssh()
|
||||||
|
run_exp(ssh, EXPDIR, P2_REPODIR, EXPERIMENTS, only_record=getargv("--record", None))
|
||||||
|
elif opt == "script":
|
||||||
|
print(SCRIPT_FIREFOX_HOSTS)
|
||||||
|
else:
|
||||||
|
print("Unknown command, use help for help")
|
||||||
|
exit(1)
|
||||||
535
exp_proxies.py
Normal file
535
exp_proxies.py
Normal file
|
|
@ -0,0 +1,535 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
# OLD VERSION
|
||||||
|
# using 3 proxies between actual browser and actual server
|
||||||
|
|
||||||
|
import os, sys, subprocess
|
||||||
|
|
||||||
|
REPODIR = "/home/tuxmain/Documents/doc/tlsbench"
|
||||||
|
P2_SSH = "exp@p2"
|
||||||
|
P2_PSW = "exp"
|
||||||
|
P2_REPODIR = "/home/exp/exp"
|
||||||
|
EXPDIR = "/dev/shm/exp"
|
||||||
|
DOMAINS_ = [
|
||||||
|
# Apple
|
||||||
|
"apple.com", "www.apple.com", "graffiti-tags.apple.com",
|
||||||
|
"store.storeimages.cdn-apple.com",
|
||||||
|
"mzstatic.com", "is1-ssl.mzstatic.com"
|
||||||
|
# Youtube video
|
||||||
|
"youtube.com", "www.youtube.com", "i.ytimg.com", "fonts.gstatic.com", "www.google.com", "accounts.google.com", "yt3.ggpht.com", "www.gstatic.com", "fonts.googleapis.com", "www.googleadservices.com",
|
||||||
|
# W3C
|
||||||
|
"w3.org", "www.w3.org",
|
||||||
|
# Amazon
|
||||||
|
"amazon.com", "www.amazon.com",
|
||||||
|
# JA3 fingerprint test
|
||||||
|
"www.edgecomputing.live",
|
||||||
|
# JA4 fingerprint test
|
||||||
|
"ja4db.com",
|
||||||
|
# Local test
|
||||||
|
"foo.home",
|
||||||
|
# Peertube
|
||||||
|
"flim.txmn.tk",
|
||||||
|
]
|
||||||
|
|
||||||
|
#URL = "https://www.youtube.com/watch?v=IBWezPN4Ep8"
|
||||||
|
#URL = "https://www.edgecompute.live/tls/ja3"
|
||||||
|
#URL = "https://ja4db.com/id/ja4/"
|
||||||
|
#URL = "https://www.w3.org/"
|
||||||
|
#URL = "http://foo.home:8080/?hello=hi"
|
||||||
|
#URL = "https://www.apple.com/"
|
||||||
|
#URL = "https://www.amazon.com/"
|
||||||
|
URL = "https://flim.txmn.tk/w/4zpfvGB72oTL4hcSqdAwx8"
|
||||||
|
|
||||||
|
URLS = [
|
||||||
|
# Heavy showcase website
|
||||||
|
"https://www.apple.com/",
|
||||||
|
# Light showcase website
|
||||||
|
"https://librezo.fr/",
|
||||||
|
# Long Wikipedia article
|
||||||
|
"https://fr.wikipedia.org/wiki/Sp%C3%A9cial:Recherche?search=Victor+Hugo&sourceid=Mozilla-search",
|
||||||
|
# Youtube video
|
||||||
|
"https://www.youtube.com/watch?v=IBWezPN4Ep8",
|
||||||
|
# Peertube video
|
||||||
|
"https://videos.domainepublic.net/videos/watch/eaee7866-d209-4e5c-b7b0-443395b79c82",
|
||||||
|
# Google search
|
||||||
|
"https://www.google.com/search?q=where+do+birds+go+when+it+rains"
|
||||||
|
]
|
||||||
|
|
||||||
|
CERT_SIGN_ALGS = [
|
||||||
|
"prime256v1", # widely used
|
||||||
|
"secp384r1", # rarely used but supported by browsers because it's NIST standard
|
||||||
|
#"secp521r1", # not supported by browsers because NIST said it was not needed
|
||||||
|
"rsa2048", "rsa3072", "rsa4096", # widely used
|
||||||
|
]
|
||||||
|
IMPLS = [
|
||||||
|
#"aws_lc_rs", # Amazon's Rust crypto widely used in Rust stuff
|
||||||
|
#"boring", # Google's fork of OpenSSL used in Chrome and Android
|
||||||
|
#"openssl", # widely used
|
||||||
|
"ring", # used in most Rust stuff
|
||||||
|
#"symcrypt", # Microsoft's crypto
|
||||||
|
#"wolfcrypt" # used in embedded (won't build with rpxy for now)
|
||||||
|
]
|
||||||
|
# Symmetric ciphers
|
||||||
|
# They also allow to choose the TLS version.
|
||||||
|
CIPHERS = [
|
||||||
|
# TLS 1.3
|
||||||
|
"AES_256_GCM_SHA384",
|
||||||
|
"AES_128_GCM_SHA256",
|
||||||
|
"CHACHA20_POLY1305_SHA256",
|
||||||
|
# TLS 1.2
|
||||||
|
# ECDSA vs RSA refers to the certificate signature algorithm.
|
||||||
|
# DH is EC is aither case, using the group specified below.
|
||||||
|
"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"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 = [
|
||||||
|
"X25519",
|
||||||
|
"SECP256R1",
|
||||||
|
"SECP384R1",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Testing all combinations would be too much. Instead we isolate independent parts.
|
||||||
|
EXPERIMENTS = {
|
||||||
|
# Compare ciphers among implementations and TLS versions
|
||||||
|
"impl-cipher-ver": {
|
||||||
|
"impls": IMPLS,
|
||||||
|
"urls": [URL],
|
||||||
|
"ciphers": [
|
||||||
|
"AES_128_GCM_SHA256",
|
||||||
|
"AES_256_GCM_SHA384",
|
||||||
|
"CHACHA20_POLY1305_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_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||||
|
],
|
||||||
|
"kexes": ["X25519"],
|
||||||
|
"cert": ["prime256v1"],
|
||||||
|
},
|
||||||
|
# Compare signatures among implementations and TLS versions
|
||||||
|
"impl-cert-ver": {
|
||||||
|
"impls": IMPLS,
|
||||||
|
"urls": [URL],
|
||||||
|
"ciphers": [
|
||||||
|
"AES_128_GCM_SHA256",
|
||||||
|
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
],
|
||||||
|
"kexes": ["X25519"],
|
||||||
|
"cert": ["prime256v1", "secp384r1", "rsa2048", "rsa3072", "rsa4096"],
|
||||||
|
},
|
||||||
|
# Compare key exchange groups among implementations and TLS versions
|
||||||
|
"impl-kex-ver": {
|
||||||
|
"impls": IMPLS,
|
||||||
|
"urls": [URL],
|
||||||
|
"ciphers": [
|
||||||
|
"AES_128_GCM_SHA256",
|
||||||
|
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
],
|
||||||
|
"kexes": ["X25519", "SECP256R1", "SECP384R1"],
|
||||||
|
"cert": ["prime256v1"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
DOMAINS = []
|
||||||
|
for domain in DOMAINS_:
|
||||||
|
if not domain in DOMAINS:
|
||||||
|
DOMAINS.append(domain)
|
||||||
|
|
||||||
|
# JS to redirect the target domains to local (bypass DNS without altering system's config or webpages or packets)
|
||||||
|
SCRIPT_FIREFOX_HOSTS = """const gOverride = Cc["@mozilla.org/network/native-dns-override;1"].getService(Ci.nsINativeDNSResolverOverride);
|
||||||
|
gOverride.clearOverrides();
|
||||||
|
var names = """+str(DOMAINS)+""";
|
||||||
|
for(var i in names) {
|
||||||
|
gOverride.addIPOverride(names[i], "127.0.0.1");
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def getargv(arg:str, default:str="", n:int=1, args:list=sys.argv) -> str:
|
||||||
|
if arg in args and len(args) > args.index(arg)+n:
|
||||||
|
return args[args.index(arg)+n]
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def sh(cmds):
|
||||||
|
if type(cmds) == list:
|
||||||
|
for cmd in cmds:
|
||||||
|
os.system(cmd)
|
||||||
|
elif type(cmds) == str:
|
||||||
|
os.system(cmds)
|
||||||
|
else:
|
||||||
|
raise TypeError
|
||||||
|
|
||||||
|
def make_sk(outpath, alg):
|
||||||
|
sh({
|
||||||
|
"ed25519": [
|
||||||
|
f"openssl genpkey -out {outpath}.sec1 -algorithm ed25519",
|
||||||
|
f"openssl pkcs8 -topk8 -nocrypt -in {outpath}.sec1 -out {outpath} -outform PEM"
|
||||||
|
],
|
||||||
|
"prime256v1": [
|
||||||
|
f"openssl ecparam -genkey -name prime256v1 -noout -out {outpath}.sec1",
|
||||||
|
f"openssl pkcs8 -topk8 -nocrypt -in {outpath}.sec1 -out {outpath} -outform PEM"
|
||||||
|
],
|
||||||
|
"rsa2048": [
|
||||||
|
f"openssl genrsa -out {outpath} 2048"
|
||||||
|
],
|
||||||
|
"rsa3072": [
|
||||||
|
f"openssl genrsa -out {outpath} 3072"
|
||||||
|
],
|
||||||
|
"rsa4096": [
|
||||||
|
f"openssl genrsa -out {outpath} 4096"
|
||||||
|
],
|
||||||
|
"secp384r1": [
|
||||||
|
f"openssl ecparam -genkey -name secp384r1 -noout -out {outpath}.sec1",
|
||||||
|
f"openssl pkcs8 -topk8 -nocrypt -in {outpath}.sec1 -out {outpath} -outform PEM"
|
||||||
|
],
|
||||||
|
}[alg])
|
||||||
|
|
||||||
|
def make_ca_cert(outpath, skpath):
|
||||||
|
sh(f"openssl req -x509 -new -key {skpath} -sha256 -days 730 -out {outpath} -subj '/CN=Foobar Root CA/C=AT/ST=Lyon/L=Lyon/O=Foobar'")
|
||||||
|
|
||||||
|
def make_cert(outpath, skpath, capath, caskpath, name, domains):
|
||||||
|
sh(f"openssl req -new -key {skpath} -sha256 -out /tmp/tmp.csr -subj '/CN={name}/C=AT/ST=Lyon/L=Lyon/O={name}'")
|
||||||
|
ext = open("/tmp/tmp.v3.ext", "w")
|
||||||
|
ext.write("""authorityKeyIdentifier=keyid,issuer
|
||||||
|
basicConstraints=CA:FALSE
|
||||||
|
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
|
||||||
|
subjectAltName = @alt_names
|
||||||
|
[alt_names]
|
||||||
|
""")
|
||||||
|
i = 1
|
||||||
|
for domain in domains:
|
||||||
|
ext.write(f"DNS.{i} = {domain}\n")
|
||||||
|
i += 1
|
||||||
|
ext.write(f"DNS.{i} = {domain}.localhost\n")
|
||||||
|
i += 1
|
||||||
|
ext.write(f"DNS.{i} = {domain}.p2\n")
|
||||||
|
i += 1
|
||||||
|
ext.write(f"DNS.{i} = {domain}.p3\n")
|
||||||
|
i += 1
|
||||||
|
ext.close()
|
||||||
|
|
||||||
|
sh(f"openssl x509 -req -in /tmp/tmp.csr -CA {capath} -CAkey {caskpath} -CAcreateserial -out {outpath} -days 365 -sha256 -extfile /tmp/tmp.v3.ext")
|
||||||
|
|
||||||
|
def get_domain_root(domain):
|
||||||
|
last_dot = domain.rfind(".")
|
||||||
|
penultimate_dot = domain.rfind(".", 0, last_dot)
|
||||||
|
return domain[penultimate_dot+1:]
|
||||||
|
|
||||||
|
# Issue secret keys, CA cert and signed certs for given domains
|
||||||
|
# All using the same algorithm
|
||||||
|
def make_certs(outdir, domains, alg):
|
||||||
|
if outdir[-1] != "/":
|
||||||
|
outdir += "/"
|
||||||
|
|
||||||
|
make_sk(outdir+"ca.key", alg)
|
||||||
|
make_ca_cert(outdir+"ca.crt", outdir+"ca.key")
|
||||||
|
|
||||||
|
# Only make certs for root domains, and include subdomains
|
||||||
|
roots = {}
|
||||||
|
for domain in domains:
|
||||||
|
root = domain
|
||||||
|
if domain.count(".") > 1:
|
||||||
|
root = get_domain_root(domain)
|
||||||
|
if root in roots:
|
||||||
|
roots[root].append(domain)
|
||||||
|
else:
|
||||||
|
roots[root] = [domain]
|
||||||
|
for root in roots:
|
||||||
|
make_sk(outdir+root+".key", alg)
|
||||||
|
make_cert(outdir+root+".crt", outdir+root+".key", outdir+"ca.crt", outdir+"ca.key", root, roots[root])
|
||||||
|
|
||||||
|
RPXY_CONFIGS = {
|
||||||
|
"p1_plain": {
|
||||||
|
"listen_http": 80,
|
||||||
|
"listen_https": 443,
|
||||||
|
"app": """[apps.{app}]
|
||||||
|
server_name = "{domain}"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}.p2:42002" }}]}}]
|
||||||
|
[apps.{app}_localhost]
|
||||||
|
server_name = "{domain}.localhost"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}.p2:42002" }}]}}]
|
||||||
|
"""
|
||||||
|
},
|
||||||
|
"p1_tls": {
|
||||||
|
"listen_http": 80,
|
||||||
|
"listen_https": 443,
|
||||||
|
"app": """[apps.{app}]
|
||||||
|
server_name = "{domain}"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}.p2:43002", tls = true }}], set_host = "{domain}.p2"}}]
|
||||||
|
[apps.{app}_localhost]
|
||||||
|
server_name = "{domain}.localhost"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}.p2:43002", tls = true }}], set_host = "{domain}.p2"}}]
|
||||||
|
"""
|
||||||
|
},
|
||||||
|
"p2_plain": {
|
||||||
|
"listen_http": 42002,
|
||||||
|
"listen_https": 43002,
|
||||||
|
"app": """[apps.{app}]
|
||||||
|
server_name = "{domain}"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}.p3:42003" }}], set_host = "{domain}.p3"}}]
|
||||||
|
[apps.{app}_localhost]
|
||||||
|
server_name = "{domain}.localhost"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}.p3:42003" }}], set_host = "{domain}.p3"}}]
|
||||||
|
[apps.{app}_p2]
|
||||||
|
server_name = "{domain}.p2"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}.p3:42003" }}], set_host = "{domain}.p3"}}]
|
||||||
|
"""
|
||||||
|
},
|
||||||
|
"p2_tls": {
|
||||||
|
"listen_http": 42002,
|
||||||
|
"listen_https": 43002,
|
||||||
|
"app": """[apps.{app}]
|
||||||
|
server_name = "{domain}"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}.p3:43003", tls = true }}], set_host = "{domain}.p3"}}]
|
||||||
|
[apps.{app}_localhost]
|
||||||
|
server_name = "{domain}.localhost"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}.p3:43003", tls = true }}], set_host = "{domain}.p3"}}]
|
||||||
|
[apps.{app}_p2]
|
||||||
|
server_name = "{domain}.p2"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}.p3:43003", tls = true }}], set_host = "{domain}.p3"}}]
|
||||||
|
"""
|
||||||
|
},
|
||||||
|
"p3": {
|
||||||
|
"listen_http": 42003,
|
||||||
|
"listen_https": 43003,
|
||||||
|
"app": """[apps.{app}]
|
||||||
|
server_name = "{domain}"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}", tls = true }}], set_host = "{domain}"}}]
|
||||||
|
[apps.{app}_localhost]
|
||||||
|
server_name = "{domain}.localhost"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}", tls = true }}], set_host = "{domain}"}}]
|
||||||
|
[apps.{app}_p3]
|
||||||
|
server_name = "{domain}.p3"
|
||||||
|
tls = {{ tls_cert_path = "{cert}", tls_cert_key_path = "{key}", https_redirection = false }}
|
||||||
|
reverse_proxy = [{{ upstream = [{{ location = "{domain}", tls = true }}], set_host = "{domain}"}}]
|
||||||
|
"""
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
SETUPS = {
|
||||||
|
#"none": {
|
||||||
|
# "rpxy_configs": ["p1_plain", "p2_plain", "p3"],
|
||||||
|
#},
|
||||||
|
#"client": {
|
||||||
|
# "rpxy_configs": ["p1_plain", "p2_tls", "p3"],
|
||||||
|
#},
|
||||||
|
"server": {
|
||||||
|
"rpxy_configs": ["p1_tls", "p2_plain", "p3"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def make_rpxy_config(outdir, domains, cryptodir):
|
||||||
|
if outdir[-1] != "/":
|
||||||
|
outdir += "/"
|
||||||
|
if cryptodir[-1] != "/":
|
||||||
|
cryptodir += "/"
|
||||||
|
|
||||||
|
for config_name in RPXY_CONFIGS:
|
||||||
|
config = RPXY_CONFIGS[config_name]
|
||||||
|
f = open(outdir+config_name+".toml", "w")
|
||||||
|
f.write("listen_port = {}\nlisten_port_tls = {}\n".format(config["listen_http"], config["listen_https"]))
|
||||||
|
for domain in domains:
|
||||||
|
app = domain.replace(".", "_")
|
||||||
|
root = get_domain_root(domain)
|
||||||
|
f.write(config["app"].format(
|
||||||
|
app=app,
|
||||||
|
domain=domain,
|
||||||
|
cert=cryptodir+root+".crt",
|
||||||
|
key=cryptodir+root+".key",
|
||||||
|
))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def make_everything(expdir, domains):
|
||||||
|
os.makedirs(expdir, exist_ok=True)
|
||||||
|
if expdir[-1] != "/":
|
||||||
|
expdir += "/"
|
||||||
|
for alg in CERT_SIGN_ALGS:
|
||||||
|
algdir = expdir+"certs/"+alg
|
||||||
|
os.makedirs(algdir, exist_ok=True)
|
||||||
|
make_certs(algdir, domains, alg)
|
||||||
|
# this will be a symbolic link to the chosen certs directory
|
||||||
|
cryptodir = expdir+"current_certs"
|
||||||
|
for setup in SETUPS:
|
||||||
|
setupdir = expdir+"setups/"+setup
|
||||||
|
os.makedirs(setupdir, exist_ok=True)
|
||||||
|
make_rpxy_config(setupdir, domains, cryptodir)
|
||||||
|
|
||||||
|
def choose_cert_alg(expdir, alg):
|
||||||
|
if expdir[-1] != "/":
|
||||||
|
expdir += "/"
|
||||||
|
try:
|
||||||
|
os.unlink(expdir+"current_certs")
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
pass
|
||||||
|
# WHY is dst pointing to src?????
|
||||||
|
os.symlink(expdir+"certs/"+alg, expdir+"current_certs", True)
|
||||||
|
|
||||||
|
def choose_impl(expdir, p, impl):
|
||||||
|
if expdir[-1] != "/":
|
||||||
|
expdir += "/"
|
||||||
|
os.symlink(os.getcwd()+"/rpxy_rustls_"+impl, expdir+str(p)+"_rpxy", False)
|
||||||
|
|
||||||
|
def run_rpxy(expdir, repodir, setup, p, impl, ciphers=None, kexes=None):
|
||||||
|
if expdir[-1] != "/":
|
||||||
|
expdir += "/"
|
||||||
|
repodir = repodir.removesuffix("/")
|
||||||
|
env = {"RUST_LOG": "debug"}
|
||||||
|
if ciphers:
|
||||||
|
env["CIPHERS"] = ",".join(ciphers)
|
||||||
|
if kexes:
|
||||||
|
env["KEXES"] = ",".join(KEXES)
|
||||||
|
print("RUN", [repodir+"/rpxy_rustls_"+impl, "--config", expdir+"setups/"+setup+"/"+p+".toml"])
|
||||||
|
return subprocess.Popen([repodir+"/rpxy_rustls_"+impl, "--config", expdir+"setups/"+setup+"/"+p+".toml"], env=env)
|
||||||
|
|
||||||
|
# https://stackoverflow.com/questions/8775598/how-to-start-a-background-process-with-nohup-using-fabric
|
||||||
|
def runbg(ssh, cmd, vars={}):
|
||||||
|
strvars = ""
|
||||||
|
for var in vars:
|
||||||
|
strvars += f"export {var}="+vars[var]+" && "
|
||||||
|
return ssh.run(f"{strvars}dtach -n `mktemp -u /tmp/dtach.XXXX` {cmd}")
|
||||||
|
|
||||||
|
def run_exp(ssh, expdir, p2_path, exps):
|
||||||
|
from selenium import webdriver
|
||||||
|
import time
|
||||||
|
import invoke
|
||||||
|
|
||||||
|
logfile = open(expdir+"/log-"+str(int(time.time())), "w")
|
||||||
|
|
||||||
|
driver = webdriver.Firefox()
|
||||||
|
driver.get("about:config")
|
||||||
|
driver.execute_script(SCRIPT_FIREFOX_HOSTS)
|
||||||
|
time.sleep(1)
|
||||||
|
for expname in exps:
|
||||||
|
exp = exps[expname]
|
||||||
|
for impl in exp["impls"]:
|
||||||
|
sh(f"killall rpxy_rustls_{impl}")
|
||||||
|
try:
|
||||||
|
ssh.run(f"killall rpxy_rustls_{impl}")
|
||||||
|
except invoke.exceptions.UnexpectedExit as e:
|
||||||
|
pass
|
||||||
|
for impl in exp["impls"]:
|
||||||
|
for alg in exp["cert"]:
|
||||||
|
for kex in exp["kexes"]:
|
||||||
|
for cipher in exp["ciphers"]:
|
||||||
|
choose_cert_alg(expdir, alg)
|
||||||
|
ssh.run(f"python {p2_path}/exp.py cert {alg}")
|
||||||
|
for setup in SETUPS:
|
||||||
|
print(f"EXPERIMENT {expname}: {impl} {alg} {kex} {cipher} {setup}")
|
||||||
|
setupdir = expdir+"setups/"+setup
|
||||||
|
p1 = run_rpxy(expdir, REPODIR, setup, SETUPS[setup]["rpxy_configs"][0], impl)
|
||||||
|
p3 = run_rpxy(expdir, REPODIR, setup, SETUPS[setup]["rpxy_configs"][2], impl)
|
||||||
|
p2_rpxy_config = SETUPS[setup]["rpxy_configs"][1]
|
||||||
|
vars = {"CIPHERS": cipher, "KEXES": kex}
|
||||||
|
|
||||||
|
runbg(ssh, f"{p2_path}/rpxy_rustls_{impl} --config {expdir}/setups/{setup}/{p2_rpxy_config}.toml --log-dir /dev/shm", vars)
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
for url in exp["urls"]:
|
||||||
|
driver.get(url)
|
||||||
|
#time.sleep(20)
|
||||||
|
input("???")
|
||||||
|
end = time.time()
|
||||||
|
logfile.write(f"{expname} {impl} {alg} {kex} {cipher} {setup} {start} {end}\n")
|
||||||
|
|
||||||
|
# DO the experiment
|
||||||
|
|
||||||
|
p1.kill()
|
||||||
|
p3.kill()
|
||||||
|
ssh.run(f"killall rpxy_rustls_{impl}")
|
||||||
|
ssh.run(f"killall dtach")
|
||||||
|
logfile.flush()
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
def update_certs():
|
||||||
|
import platform
|
||||||
|
dist = platform.freedesktop_os_release()["ID"]
|
||||||
|
if dist == "debian":
|
||||||
|
for alg in CERT_SIGN_ALGS:
|
||||||
|
sh([
|
||||||
|
f"sudo cp {EXPDIR}/certs/{alg}/ca.crt /usr/local/share/ca-certificates/ca-{alg}.crt",
|
||||||
|
f"sudo chown root:root /usr/local/share/ca-certificates/ca-{alg}.crt"
|
||||||
|
])
|
||||||
|
sh("sudo update-ca-certificates")
|
||||||
|
elif dist == "arch":
|
||||||
|
for alg in CERT_SIGN_ALGS:
|
||||||
|
sh([
|
||||||
|
f"sudo cp {EXPDIR}/certs/{alg}/ca.crt /etc/ca-certificates/trust-source/anchors/ca-{alg}.crt",
|
||||||
|
f"sudo chown root:root /etc/ca-certificates/trust-source/anchors/ca-{alg}.crt"
|
||||||
|
])
|
||||||
|
sh("sudo update-ca-trust extract")
|
||||||
|
|
||||||
|
# copy local dir src to remote parent dir dst (src's copy will be a subdir of dst)
|
||||||
|
def upload_dir(ssh, src, dst):
|
||||||
|
src = src.removesuffix("/")
|
||||||
|
src_name = src.split("/")[-1]
|
||||||
|
os.chdir(f"{src}/..")
|
||||||
|
os.system(f"tar -czf {src}/../tmp.tar.gz {src_name}")
|
||||||
|
print(ssh.put(src+"/../tmp.tar.gz", dst))
|
||||||
|
print(ssh.run(f"cd {dst} && tar -xf tmp.tar.gz"))
|
||||||
|
|
||||||
|
def connect_ssh():
|
||||||
|
import fabric
|
||||||
|
ssh_passphrase = "--passphrase" in sys.argv
|
||||||
|
connect_kwargs = {}
|
||||||
|
if ssh_passphrase:
|
||||||
|
import getpass
|
||||||
|
connect_kwargs["passphrase"] = getpass.getpass("Enter passphrase for SSH key: ")
|
||||||
|
if P2_PSW != None:
|
||||||
|
connect_kwargs["password"] = P2_PSW
|
||||||
|
ssh = fabric.Connection(P2_SSH, connect_kwargs=connect_kwargs)
|
||||||
|
return ssh
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 2 or sys.argv[1] in ["h", "help", "?", "-h", "-help", "--help", "/?"]:
|
||||||
|
print("""Options:
|
||||||
|
make Create everything
|
||||||
|
cert <alg> Select cert signature algorithm
|
||||||
|
send
|
||||||
|
update-certs
|
||||||
|
run Run experiment
|
||||||
|
|
||||||
|
Signature algorithms:
|
||||||
|
{sig_algs}
|
||||||
|
|
||||||
|
Implementations:
|
||||||
|
{impls}
|
||||||
|
|
||||||
|
Run options:
|
||||||
|
--passphrase Prompt SSH key decryption passphrase (when using pubkey login)
|
||||||
|
""".format(
|
||||||
|
sig_algs=" ".join(CERT_SIGN_ALGS),
|
||||||
|
impls=" ".join(IMPLS),
|
||||||
|
))
|
||||||
|
exit()
|
||||||
|
|
||||||
|
opt = sys.argv[1]
|
||||||
|
if opt == "make":
|
||||||
|
make_everything(EXPDIR, DOMAINS)
|
||||||
|
elif opt == "cert":
|
||||||
|
alg = sys.argv[2]
|
||||||
|
if not alg in CERT_SIGN_ALGS:
|
||||||
|
print("Error: alg must be in", CERT_SIGN_ALGS)
|
||||||
|
exit(1)
|
||||||
|
choose_cert_alg(EXPDIR, alg)
|
||||||
|
elif opt == "send":
|
||||||
|
ssh = connect_ssh()
|
||||||
|
upload_dir(ssh, EXPDIR, "/dev/shm")
|
||||||
|
elif opt == "update-certs":
|
||||||
|
update_certs()
|
||||||
|
elif opt == "run":
|
||||||
|
ssh = connect_ssh()
|
||||||
|
run_exp(ssh, EXPDIR, P2_REPODIR, EXPERIMENTS)
|
||||||
|
else:
|
||||||
|
print("Unknown command, use help for help")
|
||||||
|
exit(1)
|
||||||
6
install-certs-arch.sh
Normal file
6
install-certs-arch.sh
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
sudo cp /dev/shm/exp/certs/rsa2048/ca.crt /etc/ca-certificates/trust-source/anchors/ca-rsa2048.crt
|
||||||
|
sudo cp /dev/shm/exp/certs/rsa3072/ca.crt /etc/ca-certificates/trust-source/anchors/ca-rsa3072.crt
|
||||||
|
sudo cp /dev/shm/exp/certs/rsa4096/ca.crt /etc/ca-certificates/trust-source/anchors/ca-rsa4096.crt
|
||||||
|
sudo cp /dev/shm/exp/certs/secp384r1/ca.crt /etc/ca-certificates/trust-source/anchors/ca-secp384r1.crt
|
||||||
|
sudo chown root:root /etc/ca-certificates/trust-source/anchors/ca-*.crt
|
||||||
|
sudo update-ca-trust extract
|
||||||
6
install-certs-debian.sh
Normal file
6
install-certs-debian.sh
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
sudo cp /dev/shm/exp/certs/rsa2048/ca.crt /usr/local/share/ca-certificates/ca-rsa2048.crt
|
||||||
|
sudo cp /dev/shm/exp/certs/rsa3072/ca.crt /usr/local/share/ca-certificates/ca-rsa3072.crt
|
||||||
|
sudo cp /dev/shm/exp/certs/rsa4096/ca.crt /usr/local/share/ca-certificates/ca-rsa4096.crt
|
||||||
|
sudo cp /dev/shm/exp/certs/secp384r1/ca.crt /usr/local/share/ca-certificates/ca-secp384r1.crt
|
||||||
|
sudo chown root:root /usr/local/share/ca-certificates/ca-*.crt
|
||||||
|
sudo update-ca-certificates
|
||||||
7
mkcacert.sh
Normal file
7
mkcacert.sh
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Generate CA secret key
|
||||||
|
openssl genrsa -aes256 -out ca.key -passout pass:foo 4096
|
||||||
|
|
||||||
|
# Generate CA certificate
|
||||||
|
openssl req -x509 -new -nodes -key certs/ca.key -sha256 -days 1826 -out certs/ca.crt -passin pass:foo -subj '/CN=MyOrg Root CA/C=AT/ST=Vienna/L=Vienna/O=MyOrg'
|
||||||
29
mkcert.sh
Normal file
29
mkcert.sh
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# $1 is the first script argument, that will be the certificate's name.
|
||||||
|
# Other arguments are other domain names to be added to the certificate.
|
||||||
|
|
||||||
|
# Generate secret key
|
||||||
|
openssl req -new -nodes -out certs/$1.csr -newkey rsa:2048 -keyout certs/$1.key -subj "/CN=$1/C=AT/ST=Vienna/L=Vienna/O=MyOrg"
|
||||||
|
|
||||||
|
# Write certificate information
|
||||||
|
cat > certs/$1.v3.ext << EOF
|
||||||
|
authorityKeyIdentifier=keyid,issuer
|
||||||
|
basicConstraints=CA:FALSE
|
||||||
|
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
|
||||||
|
subjectAltName = @alt_names
|
||||||
|
[alt_names]
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Iterate script arguments
|
||||||
|
let i=1
|
||||||
|
for name in "$@"
|
||||||
|
do
|
||||||
|
echo "DNS.$i = $name" >> certs/$1.v3.ext
|
||||||
|
let i++
|
||||||
|
echo "DNS.$i = $name.localhost" >> certs/$1.v3.ext
|
||||||
|
let i++
|
||||||
|
done
|
||||||
|
|
||||||
|
# Sign certificate with CA
|
||||||
|
openssl x509 -req -in certs/$1.csr -CA certs/ca.crt -CAkey certs/ca.key -CAcreateserial -out certs/$1.crt -days 730 -sha256 -extfile certs/$1.v3.ext -passin pass:foo
|
||||||
3
mkcerts.sh
Normal file
3
mkcerts.sh
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
sh mkcert.sh apple.com www.apple.com securemetrics.apple.com
|
||||||
|
sh mkcert.sh mzstatic.com www.mzstatic.com is1-ssl.mzstatic.com
|
||||||
BIN
records/apple
Normal file
BIN
records/apple
Normal file
Binary file not shown.
BIN
records/google
Normal file
BIN
records/google
Normal file
Binary file not shown.
BIN
records/youtube
Normal file
BIN
records/youtube
Normal file
Binary file not shown.
8
run.sh
Normal file
8
run.sh
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#kissdns kissdns.json 5532
|
||||||
|
RUST_LOG=debug ./rpxy_rustls_ring --config experiments/p1_tls.toml &
|
||||||
|
RUST_LOG=debug ./rpxy_rustls_ring --config experiments/p2_tls.toml &
|
||||||
|
RUST_LOG=debug ./rpxy_rustls_ring --config experiments/p3.toml &
|
||||||
|
|
||||||
|
# By default, ^C does not kill subprocesses. This fixes it.
|
||||||
|
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
|
||||||
|
wait
|
||||||
Loading…
Add table
Add a link
Reference in a new issue