[Cryptech-Commits] [user/shatov/curve25519_fpga_model] 06/14: Test vector for X25519

git at cryptech.is git at cryptech.is
Mon Sep 24 18:52:50 UTC 2018


This is an automated email from the git hooks/post-receive script.

meisterpaul1 at yandex.ru pushed a commit to branch master
in repository user/shatov/curve25519_fpga_model.

commit 8e57d28817a48eae2d5df88cddf5dc81421c9ed2
Author: Pavel V. Shatov (Meister) <meisterpaul1 at yandex.ru>
AuthorDate: Mon Sep 24 21:37:16 2018 +0300

    Test vector for X25519
---
 vectors/x25519/alice.key                 |   3 +
 vectors/x25519/bob.key                   |   3 +
 vectors/x25519/format_test_vector.py     | 374 +++++++++++++++++++++++++++++++
 vectors/x25519/regenerate_test_vector.py |  98 ++++++++
 vectors/x25519/x25519_test_vector.h      |  22 ++
 5 files changed, 500 insertions(+)

diff --git a/vectors/x25519/alice.key b/vectors/x25519/alice.key
new file mode 100644
index 0000000..939797d
--- /dev/null
+++ b/vectors/x25519/alice.key
@@ -0,0 +1,3 @@
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VuBCIEIBCMF7aoYJIROc6rNDTmtUCcgD1Fz1pemeN1bm26p5dA
+-----END PRIVATE KEY-----
diff --git a/vectors/x25519/bob.key b/vectors/x25519/bob.key
new file mode 100644
index 0000000..775a0cd
--- /dev/null
+++ b/vectors/x25519/bob.key
@@ -0,0 +1,3 @@
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VuBCIEIBDN3nhskfw9TZ53kpmLbgHwLwpDQjr4TZT0NrF1ojtp
+-----END PRIVATE KEY-----
diff --git a/vectors/x25519/format_test_vector.py b/vectors/x25519/format_test_vector.py
new file mode 100644
index 0000000..51f76d1
--- /dev/null
+++ b/vectors/x25519/format_test_vector.py
@@ -0,0 +1,374 @@
+#
+# format_test_vector.py
+# -----------------------------------------
+# Formats test vector for x25519_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: This will only work with OpenSSL 1.1.0, version 1.0.2 doesn't
+# support the X25519 algoritm. If you system library is the older version,
+# you can pass location of specific OpenSSL binary to use on the command line.
+#
+
+
+#
+# imports
+#
+import sys
+import subprocess
+import pysodium
+
+
+#
+# variables
+#
+OPENSSL = ""
+
+
+#
+# format one test vector
+#
+def format_c_header(	f,
+						da, qax,
+						db, qbx,
+						qabx):
+	
+		# convert to integers
+	da_int   = int(da.hex(), 16)
+	db_int   = int(db.hex(), 16)
+	qax_int  = int(qax.hex(), 16)
+	qbx_int  = int(qbx.hex(), 16)
+	qabx_int = int(qabx.hex(), 16)
+	
+		# write all numbers in vector
+	format_c_array(f, da_int,   "#define X25519_DA"    + " \\\n")
+	format_c_array(f, qax_int,  "#define X25519_QA_X"  + " \\\n")
+
+	format_c_array(f, db_int,   "#define X25519_DB"    + " \\\n")
+	format_c_array(f, qbx_int,  "#define X25519_QB_X"  + " \\\n")
+
+	format_c_array(f, qabx_int, "#define X25519_QAB_X" + " \\\n")
+	
+# #
+# # format one test vector
+# #
+# def format_verilog_include(	f, curve, n,
+							# da, qax, qay,
+							# db, qbx, qby,
+							# sx, sy,
+							# gx, gy,
+							# hx, hy,
+							# qa2x, qa2y,
+							# qb2x, qb2y):
+
+	# if curve == CURVE_P256:
+		# curve_str = "P_256"
+		# msb_index = "255"
+
+	# if curve == CURVE_P384:
+		# curve_str = "P_384"
+		# msb_index = "383"
+
+		# # write all numbers in vector
+	# format_verilog_concatenation(f, n,    "localparam [" + msb_index + ":0] " + curve_str + "_N"     + " =\n")
+
+	# format_verilog_concatenation(f, da,   "localparam [" + msb_index + ":0] " + curve_str + "_DA"    + " =\n")
+	# format_verilog_concatenation(f, qax,  "localparam [" + msb_index + ":0] " + curve_str + "_QA_X"  + " =\n")
+	# format_verilog_concatenation(f, qay,  "localparam [" + msb_index + ":0] " + curve_str + "_QA_Y"  + " =\n")
+	# format_verilog_concatenation(f, qa2x, "localparam [" + msb_index + ":0] " + curve_str + "_QA2_X" + " =\n")
+	# format_verilog_concatenation(f, qa2y, "localparam [" + msb_index + ":0] " + curve_str + "_QA2_Y" + " =\n")
+
+	# format_verilog_concatenation(f, db,   "localparam [" + msb_index + ":0] " + curve_str + "_DB"    + " =\n")
+	# format_verilog_concatenation(f, qbx,  "localparam [" + msb_index + ":0] " + curve_str + "_QB_X"  + " =\n")
+	# format_verilog_concatenation(f, qby,  "localparam [" + msb_index + ":0] " + curve_str + "_QB_Y"  + " =\n")
+	# format_verilog_concatenation(f, qb2x, "localparam [" + msb_index + ":0] " + curve_str + "_QB2_X" + " =\n")
+	# format_verilog_concatenation(f, qb2y, "localparam [" + msb_index + ":0] " + curve_str + "_QB2_Y" + " =\n")
+
+	# format_verilog_concatenation(f, sx,   "localparam [" + msb_index + ":0] " + curve_str + "_S_X"   + " =\n")
+	# format_verilog_concatenation(f, sy,   "localparam [" + msb_index + ":0] " + curve_str + "_S_Y"   + " =\n")
+
+	# format_verilog_concatenation(f, gx,   "localparam [" + msb_index + ":0] " + curve_str + "_G_X"   + " =\n")
+	# format_verilog_concatenation(f, gy,   "localparam [" + msb_index + ":0] " + curve_str + "_G_Y"   + " =\n")
+
+	# format_verilog_concatenation(f, hx,   "localparam [" + msb_index + ":0] " + curve_str + "_H_X"   + " =\n")
+	# format_verilog_concatenation(f, hy,   "localparam [" + msb_index + ":0] " + curve_str + "_H_Y"   + " =\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")
+
+# 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, qx where
+	# d is private key and qx 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 or 384 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)
+
+		# change byte order to msb...lsb
+	key_priv_bytes = key_priv_bytes[::-1]
+	key_pub_bytes = key_pub_bytes[::-1]
+	
+		# convert to integers
+	key_priv_int = int(key_priv_bytes.hex(), 16)
+	key_pub_int = int(key_pub_bytes.hex(), 16)
+	
+		# another sanity check (make sure, that key_pub is actually key_priv * G)
+	key_pub_calc_bytes = pysodium.crypto_scalarmult_curve25519_base(key_priv_bytes[::-1])[::-1]
+	key_pub_calc_int = int(key_pub_calc_bytes.hex(), 16)
+	
+	if (key_pub_int != key_pub_calc_int): sys.exit()
+		
+		# done
+	return key_priv_bytes, key_pub_bytes
+
+	#
+	# returns the shared secret key
+	#
+def calc_shared_key(key_priv_bytes, key_pub_bytes):
+
+	key_shared_bytes = pysodium.crypto_scalarmult_curve25519(key_priv_bytes[::-1], key_pub_bytes[::-1])[::-1]
+	return key_shared_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('x25519_test_vector.h', 'w')
+		#file_v = open('ecdh_test_vectors.v', '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
+		da, qax = get_key(OPENSSL, "alice")
+		db, qbx = get_key(OPENSSL, "bob")
+					
+			# we derive the shared secret two different ways (from Alice's and
+			# from Bob's perspective, they must be identical of course
+		qabx = calc_shared_key(da, qbx)	# Alice's secret
+		qbax = calc_shared_key(db, qax)	# Bob's secret
+		
+		if (qabx != qbax): sys.exit()
+			
+		print("Derived shared secret.");
+
+			# format numbers and write to file
+		format_c_header(	file_h,
+							da, qax,
+							db, qbx,
+							qabx)
+							
+	#	format_verilog_include(	file_v, next_curve, n,
+	#							da, qax, qay,
+	#							db, qbx, qby,
+	#							QAB.x, QBA.y,
+	#							G.x, G.y,
+	#							H.x, H.y,
+	#							QA2.x, QA2.y,
+	#							QB2.x, QB2.y)
+
+			# done
+		file_h.close()
+		#file_v.close()
+		
+			# everything went just fine
+		print("Test vector formatted.")
+
+#
+# End of file
+#
diff --git a/vectors/x25519/regenerate_test_vector.py b/vectors/x25519/regenerate_test_vector.py
new file mode 100644
index 0000000..2d56be8
--- /dev/null
+++ b/vectors/x25519/regenerate_test_vector.py
@@ -0,0 +1,98 @@
+#
+# regenerate_test_vector.py
+# ------------------------------------------------------------
+# Generates a new randomized test vector for x25519_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: regenerate_test_vector.py [openssl_binary]"
+#
+
+#
+# This script generates a test vector. The test vector contains two
+# private keys.
+#
+
+#
+# IMPORTANT: This will only work with OpenSSL 1.1.0, version 1.0.2 doesn't
+# support the X25519 algoritm. If you system library is the older version,
+# you can pass location of specific OpenSSL binary to use on the command line.
+#
+
+#
+# imports
+#
+import sys
+import subprocess
+
+#
+# variables
+#
+SECRET_KEY_ALICE = "alice.key"
+SECRET_KEY_BOB   = "bob.key"
+
+OPENSSL = ""
+
+
+#
+# openssl_genpkey
+#
+def openssl_genpkey(binary, filename):
+		
+	subprocess.call([binary, "genpkey", "-algorithm", "X25519", "-out", filename])
+
+	
+#
+# __main__
+#
+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)
+		
+		# generate two new private keys
+	if len(OPENSSL) > 0:
+		openssl_genpkey(OPENSSL, SECRET_KEY_ALICE)
+		openssl_genpkey(OPENSSL, SECRET_KEY_BOB)
+
+
+#
+# End of file
+#
diff --git a/vectors/x25519/x25519_test_vector.h b/vectors/x25519/x25519_test_vector.h
new file mode 100644
index 0000000..90e65b1
--- /dev/null
+++ b/vectors/x25519/x25519_test_vector.h
@@ -0,0 +1,22 @@
+/* Generated automatically, do not edit. */
+
+#define X25519_DA \
+	{0x4097a7ba, 0x6d6e75e3, 0x995e5acf, 0x453d809c, \
+	 0x40b5e634, 0x34abce39, 0x119260a8, 0xb6178c10}
+
+#define X25519_QA_X \
+	{0x001c3a34, 0x022f49eb, 0x9261cd26, 0xe1d07b77, \
+	 0x78c975d3, 0x1a26aae4, 0x3e6b7b69, 0x0c01d3b9}
+
+#define X25519_DB \
+	{0x693ba275, 0xb136f494, 0x4df83a42, 0x430a2ff0, \
+	 0x016e8b99, 0x92779e4d, 0x3dfc916c, 0x78decd10}
+
+#define X25519_QB_X \
+	{0x0c364b59, 0x88b70c7e, 0x22871817, 0x471552a6, \
+	 0x8bf22c0e, 0xe4c7e096, 0xa7e0f167, 0x2157f598}
+
+#define X25519_QAB_X \
+	{0x6339d13b, 0x26df661e, 0x18d23492, 0x2dcc8232, \
+	 0x85ef9b36, 0xfc92aa83, 0x0116b7ef, 0xcdc8c52b}
+



More information about the Commits mailing list