diff --git a/.gitignore b/.gitignore index cdc0cb4..288e813 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.json netreplay* powercap __pycache__ diff --git a/crawler.py b/crawler.py index b6704ba..89f932c 100644 --- a/crawler.py +++ b/crawler.py @@ -1,6 +1,7 @@ import json,re, subprocess, sys, threading, time -BATCH = 50 +WRITE_INTERVAL = 1000 +BATCH = 200 TIMEOUT = 4 CAPTURES = { "read_bytes": "SSL handshake has read (\\d+) bytes and written \\d+ bytes\n", @@ -21,7 +22,9 @@ def probe(domain, ossl): ossl.stdin.write(b"") ossl.stdin.close() + lock.acquire() results[domain] = output = ossl.stdout.read().decode() + lock.release() ossl.kill() @@ -29,7 +32,15 @@ if __name__ == "__main__": if sys.argv[1] == "crawl": threads = [] f = open(sys.argv[2], "r") + i = 0 for line in f.readlines()[1:]: + if i % WRITE_INTERVAL == 0: + f = open(f"/dev/shm/crawl-{i}.json", "w") + lock.acquire() + json.dump(results, f) + lock.release() + f.close() + i += 1 #line = line.removesuffix("\n") line = line[:line.find(",")-1] print("start", line) @@ -37,7 +48,7 @@ if __name__ == "__main__": ossl = subprocess.Popen(["openssl", "s_client", "-no-interactive", addr], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) t = threading.Thread(target=probe, args=(line, ossl,)) threads.append((t, ossl, time.time())) - time.sleep(0.05) + time.sleep(0.02) t.start() if len(threads) >= BATCH: print("wait") @@ -73,9 +84,40 @@ if __name__ == "__main__": for r in CAPTURES: regexes[r] = re.compile(CAPTURES[r]) stats = {} + summary = { + "cert": { + "none": 0, + "secp256r1": 0, + "secp384r1": 0, + "secp521r1": 0, + "rsa512": 0, + "rsa1024": 0, + "rsa2048": 0, + "rsa3072": 0, + "rsa4096": 0, + }, + "cipher": { + "none": 0, + "aes128": 0, + "aes256": 0, + "chacha20": 0, + }, + "kx": { + "none": 0, + "x25519mlkem768": 0, + "x25519": 0, + "rsa": 0, + }, + "version": { + "none": 0, + "1.2": 0, + "1.3": 0, + } + } f = open(sys.argv[2], "r") c = json.load(f) for domain in c: + domain_stats = {} for r in CAPTURES: try: val = regexes[r].finditer(c[domain]).__next__().group(1) @@ -88,7 +130,54 @@ if __name__ == "__main__": stats[r][val] = 1 else: stats[r][val] += 1 + domain_stats[r] = val except StopIteration: pass #print("Not found:", line, r) - print(stats) + if "cert_sig" not in domain_stats: + summary["cert"]["none"] += 1 + elif domain_stats["cert_sig"] == "ecdsa_secp256r1_sha256": + summary["cert"]["secp256r1"] += 1 + elif domain_stats["cert_sig"] == "ecdsa_secp384r1_sha384": + summary["cert"]["secp384r1"] += 1 + elif domain_stats["cert_sig"] == "ecdsa_secp521r1_sha512": + summary["cert"]["secp521r1"] += 1 + elif "rsa" in domain_stats["cert_sig"]: + summary["cert"]["rsa{}".format(domain_stats["cert_pk_size"])] += 1 + if "cipher" not in domain_stats: + summary["cipher"]["none"] += 1 + elif "AES_128" in domain_stats["cipher"] or "AES128" in domain_stats["cipher"]: + summary["cipher"]["aes128"] += 1 + elif "AES_256" in domain_stats["cipher"] or "AES256" in domain_stats["cipher"]: + summary["cipher"]["aes256"] += 1 + elif "CHACHA20" in domain_stats["cipher"]: + summary["cipher"]["chacha20"] += 1 + if "kx" not in domain_stats: + summary["kx"]["none"] += 1 + elif domain_stats["kx"] == "X25519MLKEM768": + summary["kx"]["x25519mlkem768"] += 1 + elif domain_stats["kx"] == "X25519" or domain_stats["kx"] == "ECDH": + summary["kx"]["x25519"] += 1 + elif domain_stats["kx"] == "DH": + summary["kx"]["rsa"] += 1 + if "protocol" not in domain_stats: + summary["version"]["none"] += 1 + elif domain_stats["protocol"] == "TLSv1.3": + summary["version"]["1.3"] += 1 + elif domain_stats["protocol"] == "TLSv1.2": + summary["version"]["1.2"] += 1 + #if "kx" in domain_stats and domain_stats["kx"] == "DH": + # print(c[domain]) + # exit(0) + if "-t" in sys.argv: # text output + for cat in stats: + print(f"{cat}:") + for item in stats[cat]: + print(" {}:\t{}".format(item, stats[cat][item])) + elif "-s" in sys.argv: # summary + for cat in summary: + print(f"{cat}:") + for item in summary[cat]: + print(" {}:\t{}".format(item, summary[cat][item])) + else: + print(stats) diff --git a/exp.py b/exp.py index d11f287..e8064d2 100644 --- a/exp.py +++ b/exp.py @@ -51,7 +51,7 @@ CONFIGS = { True, ], "records": [ - { "filename": "wikipedia", "repeat": 10 }, + { "filename": "wikipedia", "repeat": 5 }, ], "repo_dir": "/home/tuxmain/reps/tlsbench", "exp_dir": "/dev/shm/exp", @@ -73,7 +73,8 @@ CONFIGS = { "impl-cipher-ver", "impl-cert-ver", "impl-kex-ver", - "zrtt" + "zrtt", + #"realistic" ], "sides": [ "client", @@ -139,6 +140,8 @@ CONFIGS = { "impl-cipher-ver", "impl-cert-ver", "impl-kex-ver", + "zrtt", + #"realistic", ], "sides": [ "client", @@ -149,8 +152,7 @@ CONFIGS = { True, ], "records": [ - { "filename": "wikipedia", "repeat": 400 }, - { "filename": "youtube", "repeat": 100 }, + { "filename": "wikipedia", "repeat": 2000 }, ], "repo_dir": "/home/tuxmain/reps/tlsbench", "exp_dir": "/dev/shm/exp", @@ -164,7 +166,7 @@ CONFIGS = { "perf": False, "rapl": False, "listen_port": 8080, - "idle": "idle - - - - - - - - - 600.0001013278961 0.0 735 4942 1.7759999999999962 0 -", + "idle": "idle - - - - - - - - - 1200.0000903606415 0 589 4764 3.478999999999999 0 -", "notify_listen": ("0.0.0.0", 8090), "notify_addr": "192.168.3.1:8090", }, @@ -388,9 +390,9 @@ EXPERIMENTS = { "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", + "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"], @@ -401,7 +403,7 @@ EXPERIMENTS = { "impls": IMPLS, "ciphers": [ "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"], "cert": [ @@ -418,7 +420,7 @@ EXPERIMENTS = { "impls": IMPLS, "ciphers": [ "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", @@ -453,6 +455,33 @@ EXPERIMENTS = { "cert": ["prime256v1"], "earlydata": ["0"], }, + "realistic": { + "impls": IMPLS, + "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", + #"SECP256R1", + #"SECP384R1", + "X25519MLKEM768", + #"SECP256R1MLKEM768", + #"MLKEM768", + ], + "cert": [ + #"prime256v1", + #"secp384r1", + "rsa2048", + #"rsa3072", + #"rsa4096", + ], + "earlydata": ["0"], + }, } # Some algorithms are not available in all implementations @@ -893,7 +922,7 @@ def run_exp(config, only_record=None, idle=False, shutdown=False, debug=False): start = time.time() # Wait for the client to terminate - signal.alarm(600) + signal.alarm(3600) try: notify_socket.recv(4) except Timeout: diff --git a/makecerts.py b/makecerts.py index 3df04da..a2002bd 100644 --- a/makecerts.py +++ b/makecerts.py @@ -9,8 +9,8 @@ CERTS_DIR = "/dev/shm/exp/certs/" ALGS = ["prime256v1", "secp384r1", "rsa2048", "rsa3072", "rsa4096"] DOMAINS = [ #"txmn.tk", - "wikipedia.org", - #"youtube.com" + #"wikipedia.org", + "youtube.com" ] def sh(cmds): @@ -135,11 +135,11 @@ def replace_keys(cert, key, cas): # Sign digest = None sig_alg = cert.get_signature_algorithm() - if b"SHA384" in sig_alg: + if b"SHA384" in sig_alg or b"sha384" in sig_alg: digest = "sha384" - elif b"SHA256" in sig_alg: + elif b"SHA256" in sig_alg or b"sha256" in sig_alg: digest = "sha256" - elif b"SHA512" in sig_alg: + elif b"SHA512" in sig_alg or b"sha512" in sig_alg: digest = "sha512" if digest == None: print("Unknown signature algorithm:", sig_alg) diff --git a/plots.py b/plots.py index 3a29f91..a19d3a2 100644 --- a/plots.py +++ b/plots.py @@ -118,9 +118,6 @@ def gnuplot_stacked_histogram(**kwargs): titleline = "" if kwargs["maketitle"]: titleline = 'set title font "CMU Sans Serif,12" "{object_title} by {criterion_title} ({record}, {side}{machine}) ({unit})"'.format(**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}_{record}.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 @@ -146,10 +143,63 @@ set term pict2e font ",10" set output "{plots_dir}/{object}_by_{criterion}_{side}_{record}.tex" set key font ",10" spacing 0.8 replot -""".format(plots_dir=PLOTS_DIR, cluster=cluster, titleline=titleline, **kwargs).replace("aws_lc", "aws-lc")) +""".format(plots_dir=PLOTS_DIR, titleline=titleline, **kwargs).replace("aws_lc", "aws-lc")) f.close() os.system("gnuplot {plots_dir}/{object}_by_{criterion}_{side}_{record}.gnuplot".format(plots_dir=PLOTS_DIR, **kwargs)) +def gnuplot_stacked_histogram_grouped(**kwargs): + if "machine" in kwargs and kwargs["machine"] != None: + kwargs["machine"] = ", " + kwargs["machine"] + else: + kwargs["machine"] = "" + titleline = "" + if kwargs["maketitle"]: + titleline = 'set title font "CMU Sans Serif,12" "{object_title} by {criterion_title} ({record}, {side}{machine}) ({unit})"'.format(**kwargs) + cluster = "" + cluster_i = 0 + for group in kwargs["groups"]: + if cluster_i > 0: + cluster += ",\\\n\t" + cluster += f' newhistogram "{group}"' + for i in range(3, kwargs["nb_functions"]+2): + if i == 3: + cluster += ',\\\n\t "{plots_dir}/{object}_by_{criterion}_{side}_{record}_{group}.dat"'.format(group=group, plots_dir=PLOTS_DIR, **kwargs) + else: + cluster += ',\\\n\t ""' + if cluster_i == 0: + cluster += f' using {i}:xticlabels(2) title col' + else: + cluster += f' using {i}:xticlabels(2) notitle' + cluster_i += 1 + f = open("{plots_dir}/{object}_by_{criterion}_{side}_{record}_{group_by}.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}_{record}_{group_by}.png" +set boxwidth 0.9 absolute +set style fill solid 1.0 border lt -1 +set style histogram rowstacked gap 0 +set style data histograms +{titleline} +set xtics border in scale 0,0 nomirror noenhanced rotate by 30 right +set lmargin 9 +set rmargin 1 +set bmargin 5 +set tmargin 2.5 +set key fixed left top vertical Left noenhanced nobox invert reverse opaque +set colorbox vertical origin screen 0.9, 0.2 size screen 0.05, 0.6 front noinvert bdefault +set xrange [ * : * ] noreverse writeback +set yrange [ 0 : * ] +set grid y lt 1 lw .75 lc "gray" +plot{cluster} +#set term cairolatex pdf +set term pict2e font ",10" +set output "{plots_dir}/{object}_by_{criterion}_{side}_{record}_{group_by}.tex" +set key font ",10" spacing 0.8 +replot +""".format(plots_dir=PLOTS_DIR, cluster=cluster, titleline=titleline, **kwargs).replace("aws_lc", "aws-lc")) + f.close() + os.system("gnuplot {plots_dir}/{object}_by_{criterion}_{side}_{record}_{group_by}.gnuplot".format(plots_dir=PLOTS_DIR, **kwargs)) + def make_log_plot(logs, exp, criterion, side, obj, record, machine=None, version=None, maketitle=True): f = open(f"/dev/shm/plots/{obj}_by_{criterion}_{side}_{record}.dat", "w") ciphers = {} @@ -283,6 +333,64 @@ def make_profile_plot(logs, exp, criterion, side, record, no_flamegraph=False, m maketitle=maketitle ) +def make_profile_plot_grouped(logs, exp, criterion, side, record, group_by, no_flamegraph=False, machine=None, version=None, maketitle=False): + runs = [] + functions = [] + files = {} + + for log in logs: + if version != None and VER_LABEL[log["cipher"]] != version: + continue + if log["exp"] == exp and log["record"] == record and log["side"] == side and log["tls"] == "1": + svg_filename = log["prof"] + ".svg" + if not no_flamegraph: + os.system("flamegraph --perfdata {} -o {}".format(log["prof"], svg_filename)) + try: + profile_results = profile.extract_from_file(svg_filename) + except FileNotFoundError: + print(f"Cannot read {svg_filename}") + continue + print(profile_results) + for function in profile_results: + if function not in functions: + functions.append(function) + log_group = log[group_by] + runs.append({ + criterion: log[COL[criterion]], + "impl": log["impl"], + "group": log_group, + "functions": profile_results, + }) + if log_group not in files: + files[log_group] = open(f"/dev/shm/plots/profile_by_{criterion}_{side}_{record}_{log_group}.dat", "w") + for group in files: + files[group].write("{} {} {}\n".format(group_by, criterion, " ".join(functions))) + for run in runs: + files[run["group"]].write("{} {} {}\n".format( + run["group"], + ALG_LABEL[run[criterion]], + " ".join([ + str(run["functions"][function][0]) + for function in functions + ]), + )) + for group in files: + files[group].close() + gnuplot_stacked_histogram_grouped( + object="profile", + criterion=criterion, + side=side, + object_title=OBJ_TITLE["profile"], + criterion_title=CRITERION_TITLE[criterion], + unit=UNIT["profile"], + record=record, + nb_functions=len(functions)+1, + machine=machine, + maketitle=maketitle, + group_by=group_by, + groups=[group for group in files] + ) + # Are CPU and energy proportional def make_linear_regression(logs): idle_cpu = None @@ -418,9 +526,12 @@ if __name__ == "__main__": elif cmd == "prof": for side in ["client", "server"]: for record in records: - make_profile_plot(logs, "impl-cipher-ver", "cipher", side, record, no_flamegraph=no_flamegraph, machine=machine, maketitle=maketitle, version="1.3") - make_profile_plot(logs, "impl-cert-ver", "cert", side, record, no_flamegraph=no_flamegraph, machine=machine, maketitle=maketitle, version="1.3") - make_profile_plot(logs, "impl-kex-ver", "kex", side, record, no_flamegraph=no_flamegraph, machine=machine, maketitle=maketitle, version="1.3") + #make_profile_plot(logs, "impl-cipher-ver", "cipher", side, record, no_flamegraph=no_flamegraph, machine=machine, maketitle=maketitle, version="1.3") + #make_profile_plot(logs, "impl-cert-ver", "cert", side, record, no_flamegraph=no_flamegraph, machine=machine, maketitle=maketitle, version="1.3") + #make_profile_plot(logs, "impl-kex-ver", "kex", side, record, no_flamegraph=no_flamegraph, machine=machine, maketitle=maketitle, version="1.3") + make_profile_plot_grouped(logs, "impl-cipher-ver", "cipher", side, record, "impl", no_flamegraph=no_flamegraph, machine=machine, maketitle=maketitle, version="1.3") + make_profile_plot_grouped(logs, "impl-cert-ver", "cert", side, record, "impl", no_flamegraph=no_flamegraph, machine=machine, maketitle=maketitle, version="1.3") + make_profile_plot_grouped(logs, "impl-kex-ver", "kex", side, record, "impl", no_flamegraph=no_flamegraph, machine=machine, maketitle=maketitle, version="1.3") elif cmd == "correl": from scipy import stats import matplotlib.pyplot as plt diff --git a/servers.txt b/servers.txt deleted file mode 100644 index f3b1528..0000000 --- a/servers.txt +++ /dev/null @@ -1,2 +0,0 @@ -domain_name,output_rank,date -google.com.,1,2025-12-18