From 800fc6c9b5d0c9c6706e5333101cc12af351a327 Mon Sep 17 00:00:00 2001 From: "Pavel V. Shatov (Meister)" Date: Fri, 9 Nov 2018 17:55:22 +0300 Subject: Added randomized test vector. --- vectors/ed25519/format_test_vector.py | 297 ++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 vectors/ed25519/format_test_vector.py (limited to 'vectors/ed25519/format_test_vector.py') diff --git a/vectors/ed25519/format_test_vector.py b/vectors/ed25519/format_test_vector.py new file mode 100644 index 0000000..e0173d3 --- /dev/null +++ b/vectors/ed25519/format_test_vector.py @@ -0,0 +1,297 @@ +# +# format_test_vector.py +# ----------------------------------------- +# Formats test vector for ed25519_fpga_model +# +# Author: Pavel Shatov +# Copyright (c) 2017-2018, NORDUnet A/S +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# - Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the NORDUnet nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + + +# +USAGE = "USAGE: format_test_vector.py [openssl_binary]" +# + +# +# This script reads the test vector generated by regenerate_test_vector.py +# and writes nicely formatted C header file and Verilog include file. +# + +# +# IMPORTANT: As far as I understand, this will only work with OpenSSL 1.1.1, +# versions 1.1.0 and 1.0.2 don't seem to support the Ed25519 algoritm. If you +# system library is the older version, you can pass the location of the +# specific OpenSSL binary to use on the command line. +# + + +# +# imports +# +import sys +import subprocess +import hashlib + + +# +# variables +# +OPENSSL = "" + + +# +# format one test vector +# +def format_c_header(f, d, qy): + + # write all numbers in vector + format_c_array(f, d, "#define ED25519_D_HASHED_LSB_6" + " \\\n") + format_c_array(f, qy, "#define ED25519_Q_Y_6" + " \\\n") + + +# +# format one test vector +# +def format_verilog_include(f, d, qy): + + # write all numbers in vector + format_verilog_concatenation(f, d, "localparam [255:0] ED25519_D_HASHED_LSB_6 =\n") + format_verilog_concatenation(f, qy, "localparam [255:0] ED25519_Q_Y_6 =\n") + + +# +# nicely format multi-word integer into C array initializer +# +def format_c_array(f, n, s): + + # print '#define XYZ \' + f.write(s) + + # convert number to hex string and prepend it with zeroes if necessary + n_hex = hex(n).lstrip("0x").rstrip("L") + while (len(n_hex) % 8) > 0: + n_hex = "0" + n_hex + + # get number of 32-bit words + num_words = len(n_hex) // 8 + + # print all words in n + w = 0 + while w < num_words: + + n_part = "" + + # add tab for every new line + if w == 0: + n_part += "\t{" + elif (w % 4) == 0: + n_part += "\t " + + # add current word + n_part += "0x" + n_hex[8 * w : 8 * (w + 1)] + + # add separator or newline + if (w + 1) == num_words: + n_part += "}\n" + else: + n_part += ", " + if (w % 4) == 3: + n_part += "\\\n" + + w += 1 + + # write current part + f.write(n_part) + + # write final newline + f.write("\n") + +# +# nicely format wide number into Verilog concatenation +# +def format_verilog_concatenation(f, n, s): + + # print 'localparam ZZZ =' + f.write(s) + + # convert number to hex string and prepend it with zeroes if necessary + n_hex = hex(n).split("0x")[1] + while (len(n_hex) % 8) > 0: + n_hex = "0" + n_hex + + # get number of 32-bit words + num_words = len(n_hex) // 8 + + # print all words in n + w = 0 + while w < num_words: + + n_part = "" + + if w == 0: + n_part += "\t{" + elif (w % 4) == 0: + n_part += "\t " + + n_part += "32'h" + n_hex[8 * w : 8 * (w + 1)] + + if (w + 1) == num_words: + n_part += "};\n" + else: + n_part += ", " + if (w % 4) == 3: + n_part += "\n" + w += 1 + + f.write(n_part) + + f.write("\n") + + + # + # returns d, qy where + # d is the private key and qy is the corresponding public key + # +def get_key(openssl, party): + + # generate private key filename + key_file = party + ".key" + + # retrieve key components using openssl + openssl_command = [openssl, "pkey", "-in", key_file, "-noout", "-text"] + openssl_stdout = subprocess.check_output(openssl_command).decode("utf-8") + stdout_lines = openssl_stdout.splitlines() + + key_priv = "" + key_pub = "" + + found_priv = False + found_pub = False + + # process lines looking for "priv:" and "pub:" markers + for line in stdout_lines: + + # found private key marker? + if line == "priv:": + found_priv = True + found_pub = False + continue + + # found public key marker? + if line == "pub:": + found_pub = True + found_priv = False + continue + + # found part of private key? + if found_priv: + if not line.startswith(" "): + found_priv = False + continue + else: + key_priv += line.strip() + + # found part of public key? + if found_pub: + if not line.startswith(" "): + found_pub = False + continue + else: + key_pub += line.strip() + + # do some cleanup and sanity checking on private key + # * remove colons + # * check length (256 bits) + key_priv = key_priv.replace(":", "") + if len(key_priv) != (256 / 4): sys.exit() + + # do some cleanup and sanity checking on public key + # * remove colons + # * check length (256 bits) + key_pub = key_pub.replace(":", "") + if len(key_pub) != (256 / 4): sys.exit() + + # convert to byte arrays + key_priv_bytes = bytes.fromhex(key_priv) + key_pub_bytes = bytes.fromhex(key_pub) + + # hash private key + key_priv_hashed_bytes = hashlib.sha512(key_priv_bytes).digest() + + # take the lower half and reverse byte order + key_priv_hashed_lsb_bytes = key_priv_hashed_bytes[:32][::-1] + + # done + return key_priv_hashed_lsb_bytes, key_pub_bytes + + +if __name__ == "__main__": + + # detect whether user requested some specific binary + if len(sys.argv) == 1: + OPENSSL = "openssl" + print("Using system OpenSSL library.") + elif len(sys.argv) == 2: + OPENSSL = sys.argv[1] + print("Using OpenSSL binary '" + OPENSSL + "'...") + else: + print(USAGE) + + if len(OPENSSL) > 0: + + # open output files + file_h = open('ed25519_test_vector_randomized.h', 'w') + file_v = open('ed25519_test_vector_randomized.vh', 'w') + + # write headers + file_h.write("/* Generated automatically, do not edit. */\n\n") + file_v.write("/* Generated automatically, do not edit. */\n\n") + + # load keys + d, qy = get_key(OPENSSL, "test") + + # convert to integers + d_int = int(d.hex(), 16) + qy_int = int(qy.hex(), 16) + + # format numbers and write to file + format_c_header(file_h, d_int, qy_int) + format_verilog_include(file_v, d_int, qy_int) + + # done + file_h.close() + file_v.close() + + # everything went just fine + print("Test vector formatted.") + +# +# End of file +# -- cgit v1.2.3