diff options
| -rw-r--r-- | src/cz/crcs/ectester/reader/ECTesterReader.java | 5 | ||||
| -rw-r--r-- | src/cz/crcs/ectester/standalone/ECTesterStandalone.java | 6 | ||||
| -rwxr-xr-x | util/plot_gen.py | 151 |
3 files changed, 97 insertions, 65 deletions
diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java index 377e2ff..abc7264 100644 --- a/src/cz/crcs/ectester/reader/ECTesterReader.java +++ b/src/cz/crcs/ectester/reader/ECTesterReader.java @@ -439,7 +439,7 @@ public class ECTesterReader { respWriter.outputResponse(allocate); OutputStreamWriter keysFile = FileUtil.openFiles(cfg.outputs); - keysFile.write("index;time;pubW;privS\n"); + keysFile.write("index;genTime;exportTime;pubW;privS\n"); int generated = 0; int retry = 0; @@ -451,7 +451,6 @@ public class ECTesterReader { Command.Generate generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL); Response.Generate response = generate.send(); - long elapsed = response.getDuration(); respWriter.outputResponse(response); Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send(); @@ -469,7 +468,7 @@ public class ECTesterReader { String pub = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false); String priv = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false); - String line = String.format("%d;%d;%s;%s\n", generated, elapsed / 1000000, pub, priv); + String line = String.format("%d;%d;%d;%s;%s\n", generated, response.getDuration() / 1000000, export.getDuration() / 1000000, pub, priv); keysFile.write(line); keysFile.flush(); generated++; diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java index b6f5478..e75274d 100644 --- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -325,7 +325,7 @@ public class ECTesterStandalone { kpg.initialize(spec); } - System.out.println("index;nanotime;pubW;privS;secret"); + System.out.println("index;time[nano];pubW;privS;secret"); int amount = Integer.parseInt(cli.getOptionValue("ecdh.amount", "1")); for (int i = 0; i < amount; ++i) { @@ -435,7 +435,7 @@ public class ECTesterStandalone { kpg.initialize(new ECGenParameterSpec(curveName)); } - System.out.println("index;data;signtime;verifytime;pubW;privS;signature;verified"); + System.out.println("index;data;signTime[nano];verifyTime[nano];pubW;privS;signature;verified"); int amount = Integer.parseInt(cli.getOptionValue("ecdsa.amount", "1")); for (int i = 0; i < amount; ++i) { @@ -499,7 +499,7 @@ public class ECTesterStandalone { String curveName = cli.getOptionValue("generate.curve-name"); kpg.initialize(new ECGenParameterSpec(curveName)); } - System.out.println("index;nanotime;pubW;privS"); + System.out.println("index;time[nano];pubW;privS"); int amount = Integer.parseInt(cli.getOptionValue("generate.amount", "1")); for (int i = 0; i < amount; ++i) { diff --git a/util/plot_gen.py b/util/plot_gen.py index 98d8261..f6cd8e4 100755 --- a/util/plot_gen.py +++ b/util/plot_gen.py @@ -13,19 +13,24 @@ import numpy as np import matplotlib.pyplot as plt -import matplotlib.ticker as ticker -import matplotlib.colors as colors -from operator import itemgetter +from matplotlib import ticker, colors from copy import deepcopy import argparse +def hw(i): + res = 0 + while i: + res += 1 + i &= i - 1 + return res + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Plot results of ECTester key generation timing.") parser.add_argument("-o", "--output", dest="output", type=argparse.FileType("wb"), help="Write image to [file], do not display.", metavar="file") - parser.add_argument("--pub", dest="pub", action="store_true", help="Show public key scatter plot.") - parser.add_argument("--priv", dest="priv", action="store_true", help="Show private key scatter plot.") - parser.add_argument("--hist", dest="hist", action="store_true", help="Show histogram.") - parser.add_argument("--hw-hist", dest="hw_hist", action="store_true", help="Show Hamming weight 2D histogram (private key Hamming weight and generation time).") + parser.add_argument("--priv", dest="priv", action="store_true", help="Show private key MSB heatmap plot.") + parser.add_argument("--hist", dest="hist", action="store_true", help="Show keygen time histogram.") + parser.add_argument("--export-hist", dest="export_hist", action="store_true", help="Show export time histogram.") + parser.add_argument("--hw-hist", dest="hw_hist", action="store_true", help="Show Hamming weight heatmap (private key Hamming weight and keygen time).") parser.add_argument("--skip-first", dest="skip_first", action="store_true", help="Skip first entry, as it's usually a large outlier.") parser.add_argument("-t", "--title", dest="title", type=str, nargs="?", default="", help="What title to give the figure.") parser.add_argument("file", type=str, help="The file to plot(csv).") @@ -35,99 +40,127 @@ if __name__ == "__main__": with open(opts.file, "r") as f: header = f.readline() header_names = header.split(";") + if len(header_names) not in (4, 5): + print("Bad data?") + exit(1) - plots = [opts.priv, opts.pub, opts.hist, opts.hw_hist] + plots = [opts.priv, opts.hist, opts.export_hist, opts.hw_hist] n_plots = sum(plots) if n_plots == 0: - n_plots = 4 - plots = [True, True, True, True] + if len(header_names) == 4: + n_plots = 3 + else: + n_plots = 4 + plots = [True for _ in range(n_plots)] + + if plots[2] and len(header_names) != 5: + n_plots = n_plots - 1 + if n_plots == 0: + print("Nothing to plot.") + exit(1) + plots[2] = False hx = lambda x: int(x, 16) - data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={2: hx, 3: hx}, dtype=np.dtype([("index","u4"), ("time","u4"), ("pub", "O"), ("priv", "O")])) + if len(header_names) == 4: + data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={2: hx, 3: hx}, dtype=np.dtype([("index", "u4"), ("gen_time", "u4"), ("pub", "O"), ("priv", "O")])) + else: + data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={3: hx, 4: hx}, dtype=np.dtype([("index", "u4"), ("gen_time", "u4"), ("export_time", "u4"), ("pub", "O"), ("priv", "O")])) + if opts.skip_first: data = data[1:] - if "nano" in header_names[1]: - unit = r"$\mu s$" - time_data = map(lambda x: x[1]//1000, data) - else: - unit = r"ms" - time_data = map(itemgetter(1), data) - time_data = list(time_data) - priv_data = list(map(itemgetter(2), data)) - pub_data = list(map(itemgetter(3), data)) + gen_time_data = data["gen_time"] + export_time_data = None + if "export_time" in data.dtype.names: + export_time_data = data["export_time"] + pub_data = data["pub"] + priv_data = data["priv"] + + + gen_unit = "ms" + if header_names[1].endswith("[nano]"): + gen_unit = r"$\mu s$" + gen_time_data = list(map(lambda x: x[1]//1000, gen_time_data)) + export_unit = "ms" + if len(header_names) == 5 and header_names[2].endswith("[nano]"): + export_unit = r"$\mu s$" + export_time_data = list(map(lambda x: x[1]//1000, export_time_data)) plt.style.use("ggplot") fig = plt.figure() layout_kwargs = {} if opts.title is None: fig.suptitle(opts.file) - layout_kwargs["rect"] = [0, 0.02, 1, 0.98] + #layout_kwargs["rect"] = [0, 0.02, 1, 0.98] elif opts.title: fig.suptitle(opts.title) - layout_kwargs["rect"] = [0, 0.02, 1, 0.98] - fig.tight_layout(**layout_kwargs) + #layout_kwargs["rect"] = [0, 0.02, 1, 0.98] + fig.tight_layout(**layout_kwargs) + + max_gen_time = max(gen_time_data) + min_gen_time = min(gen_time_data) + bit_size = len(bin(max(priv_data))) - 2 + + cmap = deepcopy(plt.cm.plasma) + cmap.set_bad("black") plot_i = 1 if plots[0]: axe_private = fig.add_subplot(n_plots, 1, plot_i) - axe_private.scatter(time_data, priv_data, marker="x", s=10) - axe_private.set_ylabel("private key value\n(big endian)") - axe_private.set_xlabel("time ({})".format(unit)) + priv_msb = np.array(list(map(lambda x: x >> (bit_size - 8), priv_data)), dtype=np.dtype("u1")) + heatmap, xedges, yedges = np.histogram2d(priv_msb, gen_time_data, bins=[128, max_gen_time - min_gen_time]) + extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]] + axe_private.imshow(heatmap.T, extent=extent, aspect="auto", cmap=cmap, origin="low", interpolation="nearest", norm=colors.LogNorm()) + axe_private.set_xlabel("private key MSB value\n(big endian)") + axe_private.set_ylabel("time ({})".format(gen_unit)) plot_i += 1 if plots[1]: - axe_public = fig.add_subplot(n_plots, 1, plot_i) - axe_public.scatter(time_data, pub_data, marker="x", s=10) - axe_public.set_ylabel("public key value\n(big endian)") - axe_public.set_xlabel("time ({})".format(unit)) + axe_hist = fig.add_subplot(n_plots, 1, plot_i) + time_max = max(gen_time_data) + time_min = min(gen_time_data) + time_avg = np.average(gen_time_data) + time_median = np.median(gen_time_data) + axe_hist.hist(gen_time_data, bins=int((time_max - time_min)/1.2), log=True) + axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="blue", label="avg = {}".format(time_avg)) + axe_hist.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median)) + axe_hist.set_ylabel("count\n(log)") + axe_hist.set_xlabel("keygen time ({})".format(gen_unit)) + axe_hist.xaxis.set_major_locator(ticker.MultipleLocator()) + axe_hist.legend(loc="best") plot_i += 1 if plots[2]: axe_hist = fig.add_subplot(n_plots, 1, plot_i) - time_max = max(time_data) - time_avg = np.average(time_data) - time_median = np.median(time_data) - axe_hist.hist(time_data, bins=time_max//3, log=True) - axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="red", label="avg = {}".format(time_avg)) + time_max = max(export_time_data) + time_min = min(export_time_data) + time_avg = np.average(export_time_data) + time_median = np.median(export_time_data) + axe_hist.hist(export_time_data, bins=int((time_max - time_min)/1.2), log=True) + axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="blue", label="avg = {}".format(time_avg)) axe_hist.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median)) axe_hist.set_ylabel("count\n(log)") - axe_hist.set_xlabel("time ({})".format(unit)) - axe_hist.xaxis.set_major_locator(ticker.MaxNLocator()) + axe_hist.set_xlabel("export time ({})".format(export_unit)) + axe_hist.xaxis.set_major_locator(ticker.MultipleLocator()) axe_hist.legend(loc="best") plot_i += 1 if plots[3]: - priv_bit_bins = {} - for i in range(len(data)): - skey = priv_data[i] - time = time_data[i] - skey_hw = 0 - while skey: - skey_hw += 1 - skey &= skey - 1 - if skey_hw in priv_bit_bins: - priv_bit_bins[skey_hw].append(time) - else: - priv_bit_bins[skey_hw] = [time] - priv_bit_x = [] - priv_bit_y = [] - for k,v in priv_bit_bins.items(): - priv_bit_x.extend([k] * len(v)) - priv_bit_y.extend(v) axe_priv_hist = fig.add_subplot(n_plots, 1, plot_i) - h, xe, ye = np.histogram2d(priv_bit_x, priv_bit_y, bins=[max(priv_bit_bins) - min(priv_bit_bins), (max(time_data) - min(time_data))//5]) - cmap = deepcopy(plt.cm.plasma) - cmap.set_bad("black") + priv_hw = np.array(list(map(hw, priv_data)), dtype=np.dtype("u2")) + h, xe, ye = np.histogram2d(priv_hw, gen_time_data, bins=[max(priv_hw) - min(priv_hw), max_gen_time - min_gen_time]) im = axe_priv_hist.imshow(h.T, origin="low", cmap=cmap, aspect="auto", extent=[xe[0], xe[-1], ye[0], ye[-1]], norm=colors.LogNorm()) + axe_priv_hist.axvline(x=bit_size//2, alpha=0.7, linestyle="dotted", color="white", label=str(bit_size//2) + " bits") axe_priv_hist.set_xlabel("private key Hamming weight") - axe_priv_hist.set_ylabel("time ({})".format(unit)) + axe_priv_hist.set_ylabel("time ({})".format(gen_unit)) + axe_priv_hist.legend(loc="best") fig.colorbar(im, ax=axe_priv_hist) if plot_i > 2: - fig.text(0.01, 0.02, "Data size: {}".format(len(time_data)), size="small") + fig.text(0.01, 0.02, "Data size: {}".format(len(gen_time_data)), size="small") if opts.output is None: + plt.tight_layout() plt.show() else: fig.set_size_inches(12, 10) |
