[Cryptech-Commits] [user/shatov/modexp_fpga_model] 01/01: Initial commit of faster modular exponentiation model based on systolic architecture.

git at cryptech.is git at cryptech.is
Tue Jun 13 17:13:58 UTC 2017


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/modexp_fpga_model.

commit 53e92c5355aca120eab8d59e6904282c9e3b4ab1
Author: Pavel V. Shatov (Meister) <meisterpaul1 at yandex.ru>
AuthorDate: Tue Jun 13 20:11:58 2017 +0300

    Initial commit of faster modular exponentiation model based on systolic architecture.
---
 Makefile                         |  18 ++
 README.md                        |  10 +
 modexp_fpga_model.cpp            | 293 +++++++++++++++++++++++++++
 modexp_fpga_model.h              |  77 ++++++++
 modexp_fpga_model_montgomery.cpp | 417 +++++++++++++++++++++++++++++++++++++++
 modexp_fpga_model_montgomery.h   |  66 +++++++
 modexp_fpga_model_pe.cpp         | 119 +++++++++++
 modexp_fpga_model_pe.h           |  54 +++++
 test/384.key                     |   8 +
 test/384.txt                     |   1 +
 test/512.key                     |   9 +
 test/512.txt                     |   1 +
 test/format_test_vectors.py      | 302 ++++++++++++++++++++++++++++
 test/modexp_fpga_model_vectors.h |  46 +++++
 test/modexp_fpga_model_vectors.v |  68 +++++++
 test/regenerate_test_vectors.py  |  85 ++++++++
 16 files changed, 1574 insertions(+)

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5ac0366
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+CC=gcc
+CFLAGS=-c
+LDFLAGS=
+ 
+modexp_fpga_model: modexp_fpga_model_pe.o modexp_fpga_model_montgomery.o modexp_fpga_model.o
+	$(CC) $(LDFLAGS) modexp_fpga_model_pe.o modexp_fpga_model_montgomery.o modexp_fpga_model.o -o modexp_fpga_model
+
+modexp_fpga_model_pe.o: modexp_fpga_model_pe.cpp modexp_fpga_model.h modexp_fpga_model_pe.h
+	$(CC) $(CFLAGS) modexp_fpga_model_pe.cpp
+
+modexp_fpga_model_montgomery.o: modexp_fpga_model_montgomery.cpp modexp_fpga_model.h modexp_fpga_model_pe.h modexp_fpga_model_montgomery.h
+	$(CC) $(CFLAGS) modexp_fpga_model_montgomery.cpp
+
+modexp_fpga_model.o: modexp_fpga_model.cpp modexp_fpga_model.h modexp_fpga_model_montgomery.h test/modexp_fpga_model_vectors.h
+	$(CC) $(CFLAGS) modexp_fpga_model.cpp
+
+clean:
+	rm -f modexp_fpga_model *.o
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8db40cc
--- /dev/null
+++ b/README.md
@@ -0,0 +1,10 @@
+# modexp_fpga_model
+
+This reference model was written to help debug Verilog code, it mimics how an FPGA would do modular exponentiation using systolic Montgomery multiplier. Note, that the model may do weird (from CPU point of view, of course) things at times. Another important thing is that while FPGA modules are written to operate in true constant-time manner, this model itself doesn't take any active measures to keep run-time constant. Do **NOT** use it in production as-is!
+
+The model is split into low-level primitives (32-bit adder, 32-bit subtractor, 32x32-bit multiplier with pre-adder) and higher-level arithmetic routines (multiplier and exponentiator).
+
+This model uses tips and tricks from the following sources:
+1. [High-Speed RSA Implementation](ftp://ftp.rsasecurity.com/pub/pdfs/tr201.pdf)
+2. [Handbook of Applied Cryptography](http://cacr.uwaterloo.ca/hac/)
+3. [Montgomery Modular Multiplication on Reconfigurable Hardware: Systolic versus Multiplexed Implementation](https://www.hindawi.com/journals/ijrc/2011/127147/)
diff --git a/modexp_fpga_model.cpp b/modexp_fpga_model.cpp
new file mode 100644
index 0000000..b19cdeb
--- /dev/null
+++ b/modexp_fpga_model.cpp
@@ -0,0 +1,293 @@
+//
+// modexp_fpga_model.cpp
+// -----------------------------------------------
+// Model of fast modular exponentiation on an FPGA
+//
+// Authors: Pavel Shatov
+// Copyright (c) 2017, 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.
+//
+
+
+//----------------------------------------------------------------
+// Headers
+//----------------------------------------------------------------
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "modexp_fpga_model.h"
+#include "modexp_fpga_model_montgomery.h"
+#include "test/modexp_fpga_model_vectors.h"
+
+
+//----------------------------------------------------------------
+// Defined values
+//----------------------------------------------------------------
+#define OPERAND_WIDTH_384	384
+#define OPERAND_WIDTH_512	512
+
+#define OPERAND_NUM_WORDS_384	(OPERAND_WIDTH_384 / (CHAR_BIT * sizeof(FPGA_WORD)))
+#define OPERAND_NUM_WORDS_512	(OPERAND_WIDTH_512 / (CHAR_BIT * sizeof(FPGA_WORD)))
+
+
+//----------------------------------------------------------------
+// Test vectors
+//----------------------------------------------------------------
+static const FPGA_WORD N_384_ROM[] = N_384;		// 384-bit
+static const FPGA_WORD M_384_ROM[] = M_384;		//
+static const FPGA_WORD D_384_ROM[] = D_384;		//
+static const FPGA_WORD S_384_ROM[] = S_384;		//
+
+static const FPGA_WORD N_512_ROM[] = N_512;		// 512-bit
+static const FPGA_WORD M_512_ROM[] = M_512;		//
+static const FPGA_WORD D_512_ROM[] = D_512;		//
+static const FPGA_WORD S_512_ROM[] = S_512;		//
+
+
+//----------------------------------------------------------------
+// Prototypes
+//----------------------------------------------------------------
+void print_fpga_buffer		(const char *str, const FPGA_WORD *buf, size_t len);
+bool compare_fpga_buffers	(const FPGA_WORD *src, const FPGA_WORD *dst, size_t len);
+void load_value_from_rom	(const FPGA_WORD *src, FPGA_WORD *dst, size_t len);
+
+void modexp			(const FPGA_WORD *M, const FPGA_WORD *D,
+					 const FPGA_WORD *N,       FPGA_WORD *R, size_t len);
+
+bool test_modexp	(const FPGA_WORD *n_rom, const FPGA_WORD *m_rom,
+					 const FPGA_WORD *d_rom, const FPGA_WORD *s_rom, size_t len);
+
+
+//----------------------------------------------------------------
+int main()
+//----------------------------------------------------------------
+{
+	bool ok;
+
+	printf("Trying to sign 384-bit message...\n\n");
+	ok = test_modexp(N_384_ROM, M_384_ROM, D_384_ROM, S_384_ROM, OPERAND_NUM_WORDS_384);
+	if (!ok) return EXIT_FAILURE;
+
+	printf("Trying to sign 512-bit message...\n\n");
+	ok = test_modexp(N_512_ROM, M_512_ROM, D_512_ROM, S_512_ROM, OPERAND_NUM_WORDS_512);
+	if (!ok) return EXIT_FAILURE;
+
+	return EXIT_SUCCESS;
+}
+
+
+//----------------------------------------------------------------
+// Modular exponentiation routine
+//----------------------------------------------------------------
+void modexp(	const FPGA_WORD *M,
+				const FPGA_WORD *D,
+				const FPGA_WORD *N,
+				      FPGA_WORD *R,
+					  size_t     len)
+//----------------------------------------------------------------
+//
+// R = A ** B mod N
+//
+//----------------------------------------------------------------
+{
+		// temporary buffers
+	FPGA_WORD FACTOR [MAX_OPERAND_WORDS];
+	FPGA_WORD N_COEFF[MAX_OPERAND_WORDS];
+	FPGA_WORD M_FACTOR[MAX_OPERAND_WORDS];
+
+		// pre-calculate modulus-dependant coefficients
+	montgomery_calc_factor(N, FACTOR, len);
+	montgomery_calc_n_coeff(N, N_COEFF, len);
+		
+		// bring M into Montgomery domain
+	montgomery_multiply(M, FACTOR, N, N_COEFF, M_FACTOR, len);
+
+		/*
+		 * Montgomery multiplication adds an extra factor of 2 ^ -w to every product.
+		 * We pre-calculate a special factor of 2 ^ 2w and multiply the message
+		 * by this factor using our Montgomery multiplier. This way we get the message
+		 * with the an extra factor of just 2 ^ w:
+		 * (m) * (2 ^ 2w) * (2 ^ -w) = m * 2 ^ w 
+		 *
+		 * Now we feed this message with that extra factor to the binary exponentiation
+		 * routine. The current power of m will always keep that additional factor:
+		 * (p * 2 ^ w) * (p * 2 ^ w) * (2 ^ -w) = p ^ 2 * 2 ^ w
+		 *
+		 * The result starts at 1, i.e. without any extra factors. If at any particular
+		 * iteration it gets multiplied with the current power of m, the product will
+		 * not carry any extra factors, because the power's factor gets eliminated
+		 * by the extra factor of Montgomery multiplication:
+		 * (r) * (p * 2 ^ w) * (2 ^ -w) = r * p
+		 *
+		 * This way we don't need any extra post-processing to convert the final result
+		 * from Montgomery domain. 
+		 *
+		 */
+
+		// exponentiate
+	montgomery_exponentiate(M_FACTOR, D, N, N_COEFF, R, len);
+}
+
+
+//----------------------------------------------------------------
+// Copies words from src into dst reversing their order
+//----------------------------------------------------------------
+void load_value_from_rom(const FPGA_WORD *src, FPGA_WORD *dst, size_t len)
+//----------------------------------------------------------------
+//
+// This routine copies src into dst word-by-word reversing their order
+// in the process. This reversal is necessary because of the way C
+// arrays are initialized. This model requires the least significant
+// word of operand to be stored at array offset 0, while C places
+// the most significant word there instead.
+//
+// Suppose that the operand is 0xFEDCBA9876543210, now the following line
+// uint32_t X[2] = {0xFEDCBA98, 0x76543210}
+// will place the most significant word 0xFEDCBA98 at index [0].
+//
+//----------------------------------------------------------------
+{
+	size_t w;
+
+	for (w=0; w<len; w++)
+		dst[w] = src[len - (w + 1)];
+}
+
+
+//----------------------------------------------------------------
+// Compare two operands
+//----------------------------------------------------------------
+bool compare_fpga_buffers(const FPGA_WORD *src, const FPGA_WORD *dst, size_t len)
+//----------------------------------------------------------------
+//
+// This routine compares two multi-word intergers, it is used to compare
+// the calculated value against the reference one.
+//
+//----------------------------------------------------------------
+{
+	size_t w;	// word counter
+
+		// print all the values
+	print_fpga_buffer("  Expected:   M = ", src, len);
+	print_fpga_buffer("  Calculated: R = ", dst, len);
+
+		// compare values
+	for (w=0; w<len; w++)
+	{
+			// compare
+		if (src[w] != dst[w]) return false;
+	}
+
+		// values are the same
+	return true;
+}
+
+
+//----------------------------------------------------------------
+// Prints large multi-word integer
+//----------------------------------------------------------------
+void print_fpga_buffer(const char *str, const FPGA_WORD *buf, size_t len)
+//----------------------------------------------------------------
+{
+	size_t w, s;	// word counter
+
+		// print header
+	printf("%s", str);
+
+		// print all bytes
+	for (w=len; w>0; w--)
+	{	
+		printf("%08x", buf[w-1]);
+
+			// insert space after every group of 4 bytes or print new line
+		if (w > 1)
+		{
+			if (((len - w) % 4) == 3)
+			{	printf("\n");
+				s = strlen(str);
+				while (s)
+				{	printf(" ");
+					s--;
+				}
+			}
+			else printf(" ");
+		}
+	}
+
+		// print footer
+	printf("\n\n");
+}
+
+
+//----------------------------------------------------------------
+// Test the modular multiplication model
+//----------------------------------------------------------------
+bool test_modexp(const FPGA_WORD *n_rom, const FPGA_WORD *m_rom, const FPGA_WORD *d_rom, const FPGA_WORD *s_rom, size_t len)
+//----------------------------------------------------------------
+//
+// This routine uses the Montgomery exponentiation routine to
+// calculate r = m ** d mod m, and then compares it to the
+// reference value s.
+//
+//----------------------------------------------------------------
+{
+	bool ok;	// flag
+
+		// buffers
+	FPGA_WORD N[MAX_OPERAND_WORDS];
+	FPGA_WORD M[MAX_OPERAND_WORDS];
+	FPGA_WORD D[MAX_OPERAND_WORDS];
+	FPGA_WORD S[MAX_OPERAND_WORDS];
+	FPGA_WORD R[MAX_OPERAND_WORDS];
+
+		// fill buffers with test vector
+	load_value_from_rom(n_rom, N, len);
+	load_value_from_rom(m_rom, M, len);
+	load_value_from_rom(d_rom, D, len);
+	load_value_from_rom(s_rom, S, len);
+
+		// calculate power
+	modexp(M, D, N, R, len);
+
+		// check result
+	ok = compare_fpga_buffers(S, R, len);
+	if (!ok)
+	{	printf("\n    ERROR\n\n");
+		return false;
+	}
+
+		// everything went just fine
+	printf("\n    OK\n\n");
+	return true;
+}
+
+
+//----------------------------------------------------------------
+// End of file
+//----------------------------------------------------------------
diff --git a/modexp_fpga_model.h b/modexp_fpga_model.h
new file mode 100644
index 0000000..2a91d32
--- /dev/null
+++ b/modexp_fpga_model.h
@@ -0,0 +1,77 @@
+//
+// modexp_fpga_model.h
+// -----------------------------------------------
+// Model of fast modular exponentiation on an FPGA
+//
+// Authors: Pavel Shatov
+// Copyright (c) 2017, 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.
+//
+
+
+//----------------------------------------------------------------
+// Headers
+//----------------------------------------------------------------
+#include <stdint.h>		// uintNN_t
+#include <stddef.h>		// size_t
+#include <limits.h>		// CHAR_BIT
+
+
+//----------------------------------------------------------------
+// Data types
+//----------------------------------------------------------------
+typedef uint32_t  FPGA_WORD;		// FPGA data bus width
+typedef uint64_t _WIDE_WORD;		// only used internally to mimic DSP slice operation
+
+
+//----------------------------------------------------------------
+// Model settings
+//----------------------------------------------------------------
+#define MAX_OPERAND_WIDTH	512		// largest supported operand width in bits
+#define SYSTOLIC_WIDTH		128		// width of systolic array in bits
+
+
+//----------------------------------------------------------------
+// Handy values
+//----------------------------------------------------------------
+
+	// largest possible number of 32-bit words in an operand
+#define MAX_OPERAND_WORDS	(MAX_OPERAND_WIDTH / (CHAR_BIT * sizeof(FPGA_WORD)))
+
+	// number of words systolic array processes at once
+#define SYSTOLIC_NUM_WORDS		(SYSTOLIC_WIDTH    / (CHAR_BIT * sizeof(FPGA_WORD)))
+
+	// largest possible number of consequtive systolic cycles
+	// that are necessary to process entire operand
+#define MAX_SYSTOLIC_CYCLES		(MAX_OPERAND_WIDTH / SYSTOLIC_WIDTH)
+
+
+//----------------------------------------------------------------
+// End of file
+//----------------------------------------------------------------
diff --git a/modexp_fpga_model_montgomery.cpp b/modexp_fpga_model_montgomery.cpp
new file mode 100644
index 0000000..d1cca60
--- /dev/null
+++ b/modexp_fpga_model_montgomery.cpp
@@ -0,0 +1,417 @@
+//
+// modexp_fpga_model_montgomery.cpp
+// -------------------------------------------------------------
+// Montgomery modular multiplication and exponentiation routines
+//
+// Authors: Pavel Shatov
+// Copyright (c) 2017, 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.
+//
+
+
+//----------------------------------------------------------------
+// Headers
+//----------------------------------------------------------------
+#include "modexp_fpga_model.h"
+#include "modexp_fpga_model_pe.h"
+#include "modexp_fpga_model_montgomery.h"
+
+
+//----------------------------------------------------------------
+// Montgomery modular multiplier
+//----------------------------------------------------------------
+void montgomery_multiply(const FPGA_WORD *A, const FPGA_WORD *B, const FPGA_WORD *N, const FPGA_WORD *N_COEFF, FPGA_WORD *R, size_t len)
+//----------------------------------------------------------------
+//
+// R = A * B * 2^-len mod N
+//
+// The high-level algorithm is:
+//
+// 1. AB =  A * B
+// 2. Q  = AB * N_COEFF
+// 3. QN =  Q * N
+// 4. S  = AB + QN
+// 5. SN =  S - N
+// 6. R  = (SN < 0) ? S : SN
+// 7. R  = R >> len
+//
+//----------------------------------------------------------------
+{
+	size_t i, j, k;													// counters
+
+	bool select_s;													// flag
+
+	FPGA_WORD t_ab[MAX_SYSTOLIC_CYCLES][SYSTOLIC_NUM_WORDS];		// accumulators
+	FPGA_WORD t_q [MAX_SYSTOLIC_CYCLES][SYSTOLIC_NUM_WORDS];		//
+	FPGA_WORD t_qn[MAX_SYSTOLIC_CYCLES][SYSTOLIC_NUM_WORDS];		//
+
+	FPGA_WORD s_ab[MAX_SYSTOLIC_CYCLES][SYSTOLIC_NUM_WORDS];		// intermediate products
+	FPGA_WORD s_q [MAX_SYSTOLIC_CYCLES][SYSTOLIC_NUM_WORDS];		//
+	FPGA_WORD s_qn[MAX_SYSTOLIC_CYCLES][SYSTOLIC_NUM_WORDS];		//
+
+	FPGA_WORD c_in_ab[MAX_SYSTOLIC_CYCLES][SYSTOLIC_NUM_WORDS];		// input carries
+	FPGA_WORD c_in_q [MAX_SYSTOLIC_CYCLES][SYSTOLIC_NUM_WORDS];		//
+	FPGA_WORD c_in_qn[MAX_SYSTOLIC_CYCLES][SYSTOLIC_NUM_WORDS];		//
+	FPGA_WORD c_out_ab[MAX_SYSTOLIC_CYCLES][SYSTOLIC_NUM_WORDS];	// output carries
+	FPGA_WORD c_out_q [MAX_SYSTOLIC_CYCLES][SYSTOLIC_NUM_WORDS];	//
+	FPGA_WORD c_out_qn[MAX_SYSTOLIC_CYCLES][SYSTOLIC_NUM_WORDS];	//
+
+	FPGA_WORD c_in_s;												// 1-bit carry and borrow
+	FPGA_WORD b_in_sn;												//
+	FPGA_WORD c_out_s;												//
+	FPGA_WORD b_out_sn;												//
+
+	FPGA_WORD AB[2 * MAX_OPERAND_WORDS];							// final products
+	FPGA_WORD Q [2 * MAX_OPERAND_WORDS];							//
+	FPGA_WORD QN[2 * MAX_OPERAND_WORDS];							//
+
+	FPGA_WORD S [2 * MAX_OPERAND_WORDS];							// final sum
+	FPGA_WORD SN[2 * MAX_OPERAND_WORDS];							// final difference
+
+		// number of systolic cycles needed to multiply entire B by one word of A
+	size_t num_systolic_cycles = len / SYSTOLIC_NUM_WORDS;
+
+		// initialize arrays of accumulators and carries to zeroes
+	for (i=0; i<num_systolic_cycles; i++)
+		for (j=0; j<SYSTOLIC_NUM_WORDS; j++)
+			c_in_ab[i][j] = 0, c_in_q [i][j] = 0, c_in_qn[i][j] = 0,
+			t_ab[i][j]    = 0, t_q [i][j]    = 0, t_qn[i][j]    = 0;
+
+		// initialize 1-bit carry and borrow to zeroes too
+	c_in_s = 0, b_in_sn = 0;
+
+		// simultaneously calculate AB, Q, QN, S, SN
+	for (i = 0; i < (2 * len); i++)
+	{
+			// multiply entire B by current word of A to get AB
+			// multiply entire N_COEFF by current word of AB to get Q
+			// multiply entire N by current word of Q to get QN
+		for (k = 0; k < num_systolic_cycles; k++)
+		{
+				// simulate how a systolic array would work
+			for (j = 0; j < SYSTOLIC_NUM_WORDS; j++)
+			{
+					// current words of B, N_COEFF, N
+				FPGA_WORD Bj       = B      [k * SYSTOLIC_NUM_WORDS + j];
+				FPGA_WORD N_COEFFj = N_COEFF[k * SYSTOLIC_NUM_WORDS + j];
+				FPGA_WORD Nj       = N      [k * SYSTOLIC_NUM_WORDS + j];
+
+					// current word of A
+				FPGA_WORD Aj_ab = (i < len) ? A[i] : 0;
+
+					// AB = A * B		
+				pe_mul(Aj_ab, Bj, t_ab[k][j], c_in_ab[k][j], &s_ab[k][j], &c_out_ab[k][j]);
+
+					// store current word of AB
+				if ((k == 0) && (j == 0)) AB[i] = s_ab[0][0];
+
+					// current word of AB
+				FPGA_WORD Aj_q = (i < len) ? AB[i] : 0;
+
+					// Q = AB * N		
+				pe_mul(Aj_q, N_COEFFj, t_q[k][j], c_in_q[k][j], &s_q[k][j], &c_out_q[k][j]);
+
+					// store current word of Q
+				if ((k == 0) && (j == 0)) Q[i] = s_q[0][0];
+
+					// current word of Q
+				FPGA_WORD Aj_qn = (i < len) ? Q[i] : 0;
+
+					// QN = Q * N
+				pe_mul(Aj_qn, Nj, t_qn[k][j], c_in_qn[k][j], &s_qn[k][j], &c_out_qn[k][j]);
+
+					// store next word of QN
+				if ((k == 0) && (j == 0)) QN[i] = s_qn[0][0];
+			}
+
+				// propagate carries
+			for (j=0; j<SYSTOLIC_NUM_WORDS; j++)
+				c_in_ab[k][j] = c_out_ab[k][j],
+				c_in_q [k][j] = c_out_q [k][j],
+				c_in_qn[k][j] = c_out_qn[k][j];
+
+				// update accumulators
+			for (j=1; j<SYSTOLIC_NUM_WORDS; j++)
+			{
+				t_ab[k][j-1] = s_ab[k][j];
+				t_q [k][j-1] = s_q [k][j];
+				t_qn[k][j-1] = s_qn[k][j];
+			}
+			
+				// update accumulators
+			if (k > 0)
+				t_ab[k-1][SYSTOLIC_NUM_WORDS-1] = s_ab[k][0],
+				t_q [k-1][SYSTOLIC_NUM_WORDS-1] = s_q [k][0],
+				t_qn[k-1][SYSTOLIC_NUM_WORDS-1] = s_qn[k][0];
+
+		}
+	
+			// now it's time to simultaneously add and subtract
+
+			// current operand words
+		FPGA_WORD QNi = QN[i];
+		FPGA_WORD Ni  = (i < len) ? 0 : N[i-len];
+
+			// add, subtract
+		pe_add(AB[i], QNi, c_in_s,  &S[i],  &c_out_s);
+		pe_sub(S [i], Ni,  b_in_sn, &SN[i], &b_out_sn);
+
+			// propagate carry and borrow
+		c_in_s  = c_out_s;
+		b_in_sn = b_out_sn;
+	}
+
+		// flag select the right result
+	select_s = b_out_sn && !c_out_s;
+
+		// copy product into output buffer
+	for (i=0; i<len; i++)
+		R[i] = select_s ? S[i+len] : SN[i+len];
+}
+
+
+//----------------------------------------------------------------
+// Classic binary exponentiation
+//----------------------------------------------------------------
+void montgomery_exponentiate(const FPGA_WORD *A, const FPGA_WORD *B, const FPGA_WORD *N, const FPGA_WORD *N_COEFF, FPGA_WORD *R, size_t len)
+//----------------------------------------------------------------
+//
+// R = A ** B mod N
+//
+//----------------------------------------------------------------
+{
+	size_t word_cnt,   bit_cnt;			// counters
+	size_t word_index, bit_index;		// indices
+
+	bool flag_update_r;					// flag
+
+	FPGA_WORD P[MAX_OPERAND_WORDS];		// power of A
+	FPGA_WORD mask;						// mask		
+
+		// R = 1, P = 1
+	for (word_cnt=0; word_cnt<len; word_cnt++)
+		R[word_cnt] = (word_cnt > 0) ? 0 : 1,
+		P[word_cnt] = A[word_cnt];
+
+	FPGA_WORD M_PP[MAX_OPERAND_WORDS];	// intermediate buffer for next power
+	FPGA_WORD M_RP[MAX_OPERAND_WORDS];	// intermediate buffer for next result
+
+		// scan all bits of the exponent
+	for (bit_cnt=0; bit_cnt<(len * CHAR_BIT * sizeof(FPGA_WORD)); bit_cnt++)
+	{
+		montgomery_multiply(P, P, N, N_COEFF, M_PP, len);	// M_PP = P * P
+		montgomery_multiply(R, P, N, N_COEFF, M_RP, len);	// M_RP = R * P
+		
+		word_index = bit_cnt / (CHAR_BIT * sizeof(FPGA_WORD));
+		bit_index = bit_cnt & ((CHAR_BIT * sizeof(FPGA_WORD)) - 1);
+
+		mask = 1 << bit_index;	// current bit of exponent
+
+			// whether we need to update R (non-zero exponent bit)
+		flag_update_r = (B[word_index] & mask) == mask;
+
+			// always update P
+		for (word_cnt=0; word_cnt<len; word_cnt++)
+			P[word_cnt] = M_PP[word_cnt];
+
+			// only update R when necessary
+		if (flag_update_r)
+		{
+			for (word_cnt=0; word_cnt<len; word_cnt++)
+				R[word_cnt] = M_RP[word_cnt];
+		}
+	}
+}
+
+
+//----------------------------------------------------------------
+// Montgomery factor calculation
+//----------------------------------------------------------------
+void montgomery_calc_factor(const FPGA_WORD *N, FPGA_WORD *FACTOR, size_t len)
+//----------------------------------------------------------------
+//
+// FACTOR = 2 ** (2*len) mod N
+//
+// This routine calculates the factor that is necessary to bring
+// numbers into Montgomery domain. The high-level algorithm is:
+//
+// 1. f = 1
+// 2. for i=0 to 2*len-1
+// 3.   f1 = f << 1
+// 4.   f2 = f1 - n
+// 5.   f = (f2 < 0) ? f1 : f2
+//
+//----------------------------------------------------------------
+{
+	size_t i, j;		// counters
+
+	bool flag_keep_f;	// flag
+	
+		// temporary buffer
+	FPGA_WORD FACTOR_N[MAX_OPERAND_WORDS];
+
+		// carry and borrow
+	FPGA_WORD carry_in, carry_out;
+	FPGA_WORD borrow_in, borrow_out;
+
+		// FACTOR = 1
+	for (i=0; i<len; i++)
+		FACTOR[i] = (i > 0) ? 0 : 1;
+		
+		// do the math
+	for (i=0; i<(2 * len * CHAR_BIT * sizeof(FPGA_WORD)); i++)
+	{
+			// clear carry and borrow
+		carry_in = 0, borrow_in = 0;
+		
+			// calculate f1 = f << 1, f2 = f1 - n
+		for (j=0; j<len; j++)
+		{
+			carry_out = FACTOR[j] >> (sizeof(FPGA_WORD) * CHAR_BIT - 1);		// | M <<= 1
+			FACTOR[j] <<= 1, FACTOR[j] |= carry_in;								// |
+
+			pe_sub(FACTOR[j], N[j], borrow_in, &FACTOR_N[j], &borrow_out);		// MN = M - N
+	
+			carry_in = carry_out, borrow_in = borrow_out;						// propagate carry & borrow
+		}
+
+			// obtain flag
+		flag_keep_f = (borrow_out && !carry_out);
+
+			// now select the right value
+		for (j=0; j<len; j++)
+			FACTOR[j] = flag_keep_f ? FACTOR[j] : FACTOR_N[j];
+	}
+}
+
+
+//----------------------------------------------------------------
+// Montgomery modulus-dependent coefficient calculation
+//----------------------------------------------------------------
+void montgomery_calc_n_coeff(const FPGA_WORD *N, FPGA_WORD *N_COEFF, size_t len)
+//----------------------------------------------------------------
+//
+// N_COEFF = -N ** -1 mod 2 ** len
+//
+// This routine calculates the coefficient that is used during the reduction
+// phase of Montgomery multiplication to zero out the lower half of product.
+//
+// The high-level algorithm is:
+//
+// 1. R = 1
+// 2. NN = ~N + 1
+// 3. for i=1 to len-1
+// 4.   T = R * NN mod 2 ** len
+// 5.   if T[i] then
+// 6.     R = R + (1 << i)
+//
+//----------------------------------------------------------------
+{
+	size_t i, j, k;							// counters
+
+	FPGA_WORD NN[MAX_OPERAND_WORDS];		// temporary buffers
+	FPGA_WORD T [MAX_OPERAND_WORDS];		//
+	FPGA_WORD R [MAX_OPERAND_WORDS];		//
+	FPGA_WORD R1[MAX_OPERAND_WORDS];		//
+
+	bool flag_update_r;						// flag
+
+	FPGA_WORD nw, pwr;						//
+	FPGA_WORD sum_c_in, sum_c_out;			//
+	FPGA_WORD carry_in, carry_out;			//
+	FPGA_WORD mul_s, mul_c_in, mul_c_out;	//
+
+		// NN = -N mod 2 ** len = ~N + 1 mod 2 ** len
+	carry_in = 0;
+	for (i=0; i<len; i++)
+	{	nw = (i > 0) ? 0 : 1;								// nw = 1
+		pe_add(~N[i], nw, carry_in, &NN[i], &carry_out);	// NN = ~N + nw
+		carry_in = carry_out;								// propagate carry
+	}
+
+		// R = 1
+	for (i=0; i<len; i++)
+		R[i] = (i > 0) ? 0 : 1;
+
+		// calculate T = R * NN
+	for (k=1; k<(len * sizeof(FPGA_WORD) * CHAR_BIT); k++)
+	{
+			// T = 0
+		for (i=0; i<len; i++) T[i] = 0;
+
+			// T = NN * R
+		for (i=0; i<len; i++)
+		{
+			mul_c_in = 0;
+
+			for (j=0; j<(len-i); j++)
+			{
+			
+				pe_mul(R[j], NN[i], T[i+j], mul_c_in, &mul_s, &mul_c_out);
+				
+				T[i+j] = mul_s;
+				
+				mul_c_in = mul_c_out;
+			}
+		}
+
+			// get word and index indices
+		size_t word_index = k / (CHAR_BIT * sizeof(FPGA_WORD));
+		size_t bit_index = k & ((CHAR_BIT * sizeof(FPGA_WORD)) - 1);
+
+			// update bit mask
+		FPGA_WORD bit_mask = (1 << bit_index);
+
+		sum_c_in = 0;			// clear carry
+		flag_update_r = false;	// reset flag
+
+			// calculate R1 = R + 1 << (2 * len)
+		for (i=0; i<len; i++)
+		{
+			if (i == word_index) flag_update_r = (T[i] & bit_mask) == bit_mask;
+
+			pwr = (i == word_index) ? bit_mask : 0;
+			pe_add(R[i], pwr, sum_c_in,  &R1[i], &sum_c_out);
+			carry_in = carry_out;
+		}
+
+			// update r
+		for (i=0; i<len; i++)
+			R[i] = flag_update_r ? R1[i] : R[i];
+	}
+
+		// store output
+	for (i=0; i<len; i++)
+		N_COEFF[i] = R[i];
+}
+
+
+//----------------------------------------------------------------
+// End of file
+//----------------------------------------------------------------
diff --git a/modexp_fpga_model_montgomery.h b/modexp_fpga_model_montgomery.h
new file mode 100644
index 0000000..bb4dbae
--- /dev/null
+++ b/modexp_fpga_model_montgomery.h
@@ -0,0 +1,66 @@
+//
+// modexp_fpga_model_montgomery.h
+// -------------------------------------------------------------
+// Montgomery modular multiplication and exponentiation routines
+//
+// Authors: Pavel Shatov
+// Copyright (c) 2017, 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.
+//
+
+
+//----------------------------------------------------------------
+// Prototypes
+//----------------------------------------------------------------
+void montgomery_multiply(		const FPGA_WORD *A,
+								const FPGA_WORD *B,
+								const FPGA_WORD *N,
+								const FPGA_WORD *N_COEFF,
+								      FPGA_WORD *R,
+								      size_t     len);
+
+void montgomery_exponentiate(	const FPGA_WORD *A,
+								const FPGA_WORD *B,
+								const FPGA_WORD *N,
+								const FPGA_WORD *N_COEFF,
+								      FPGA_WORD *R,
+									  size_t     len);
+
+void montgomery_calc_factor(	const FPGA_WORD *N,
+								      FPGA_WORD *FACTOR,
+									  size_t     len);
+
+void montgomery_calc_n_coeff(	const FPGA_WORD *N,
+								      FPGA_WORD *N_COEFF,
+									  size_t     len);
+
+
+//----------------------------------------------------------------
+// End of file
+//----------------------------------------------------------------
diff --git a/modexp_fpga_model_pe.cpp b/modexp_fpga_model_pe.cpp
new file mode 100644
index 0000000..4f58bee
--- /dev/null
+++ b/modexp_fpga_model_pe.cpp
@@ -0,0 +1,119 @@
+//
+// modexp_fpga_model_pe.cpp
+// -----------------------------
+// Low-level processing elements
+//
+// Authors: Pavel Shatov
+// Copyright (c) 2017, 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.
+//
+
+
+//----------------------------------------------------------------
+// Headers
+//----------------------------------------------------------------
+#include "modexp_fpga_model.h"
+#include "modexp_fpga_model_pe.h"
+
+
+//----------------------------------------------------------------
+// Low-level 32-bit multiplier with pre-adder
+//----------------------------------------------------------------
+void pe_mul(	FPGA_WORD  a, FPGA_WORD  b,
+				FPGA_WORD  t, FPGA_WORD  c_in,
+				FPGA_WORD *p, FPGA_WORD *c_out)
+//----------------------------------------------------------------
+//
+// {c_out, p} = a * b + t + c_in
+//
+// Note, that the output will never overflow, because 0xFFFFFFFF^2 +
+// 2*0xFFFFFFFF is exactly 0xFFFFFFFFFFFFFFFF. Doh, we're lucky...
+//
+//----------------------------------------------------------------
+{
+	_WIDE_WORD s = (_WIDE_WORD)a * (_WIDE_WORD)b;				// get product from multiplier
+	
+	s += (_WIDE_WORD)t;											// trigger pre-adder
+	s += (_WIDE_WORD)c_in;										// take carry into account
+	
+	*p = (FPGA_WORD)s;											// return the lower part of result
+	*c_out = (FPGA_WORD)(s >> (CHAR_BIT * sizeof(FPGA_WORD)));	// return the higher part of result
+}
+
+
+//----------------------------------------------------------------
+// Low-level 32-bit adder
+//----------------------------------------------------------------
+void pe_add(	FPGA_WORD  a,
+				FPGA_WORD  b,
+				FPGA_WORD  c_in,
+				FPGA_WORD *s,
+				FPGA_WORD *c_out)
+//----------------------------------------------------------------
+//
+// {c_out, s} = a + b + c_in
+//
+//----------------------------------------------------------------
+{
+	_WIDE_WORD t = (_WIDE_WORD)a + (_WIDE_WORD)b;					// get sum from adder
+	
+	t += (_WIDE_WORD)(c_in & 1);									// take carry into account
+	
+	*s = (FPGA_WORD)t;												// return the lower part of resukt
+	*c_out = (FPGA_WORD)(t >> (CHAR_BIT * sizeof(FPGA_WORD))) & 1;	// return the higher part of result
+
+}
+
+
+//----------------------------------------------------------------
+// Low-level 32-bit subtractor
+//----------------------------------------------------------------
+void pe_sub(	FPGA_WORD  a,
+				FPGA_WORD  b,
+				FPGA_WORD  b_in,
+				FPGA_WORD *d,
+				FPGA_WORD *b_out)
+//----------------------------------------------------------------
+//
+// {b_out, d} = a - b - b_in
+//
+//----------------------------------------------------------------
+{
+	_WIDE_WORD t = (_WIDE_WORD)a - (_WIDE_WORD)b;					// get difference from subtractor
+	
+	t -= (_WIDE_WORD)(b_in & 1);									// take borrow into account
+	
+	*d = (FPGA_WORD)t;												// return the lower part of result
+	*b_out = (FPGA_WORD)(t >> (CHAR_BIT * sizeof(FPGA_WORD))) & 1;	// return the higher part of result
+}
+
+
+//----------------------------------------------------------------
+// End of file
+//----------------------------------------------------------------
diff --git a/modexp_fpga_model_pe.h b/modexp_fpga_model_pe.h
new file mode 100644
index 0000000..97e9a4a
--- /dev/null
+++ b/modexp_fpga_model_pe.h
@@ -0,0 +1,54 @@
+//
+// modexp_fpga_model_pe.h
+// -----------------------------
+// Low-level processing elements
+//
+// Authors: Pavel Shatov
+// Copyright (c) 2017, 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.
+//
+
+
+//----------------------------------------------------------------
+// Prototypes
+//----------------------------------------------------------------
+void pe_mul(	FPGA_WORD  a, FPGA_WORD  b,
+				FPGA_WORD  t, FPGA_WORD  c_in,
+				FPGA_WORD *p, FPGA_WORD *c_out);
+
+void pe_add(	FPGA_WORD  a, FPGA_WORD  b, FPGA_WORD c_in,
+				FPGA_WORD *s, FPGA_WORD *c_out);
+
+void pe_sub(	FPGA_WORD  a, FPGA_WORD  b, FPGA_WORD b_in,
+				FPGA_WORD *d, FPGA_WORD *b_out);
+
+
+//----------------------------------------------------------------
+// End of file
+//----------------------------------------------------------------
diff --git a/test/384.key b/test/384.key
new file mode 100644
index 0000000..30d3170
--- /dev/null
+++ b/test/384.key
@@ -0,0 +1,8 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIHzAgEAAjEAsGtN4xAGIU+qPIyubFaEd7jI2LIIaEBwO5umdHOeDuyVCg2KtrdE
+Pf6+aQzigQCZAgMBAAECMQCtJKMMdm2Nw+IQCwIk0cSwu2pjQld9+b6Jux7D3DJZ
+8Bo0P5NXoSWZoyiuL/he9AECGQDprEz2A7LYCn8dCR5J1fGgrCrk/7+b83UCGQDB
+Ro8+xpCSMVpNdLpHezA/Sy4Q0R9E6BUCGGm2woaV+8YTUZiANIyw1oSa/zjknvnd
+tQIYHtqCt4S/Q3c5cS/3JL4Xn6MCwZCAq2FZAhkA4ixTus6Idmm9mEaxkVocycbi
+bXrEdV0q
+-----END RSA PRIVATE KEY-----
diff --git a/test/384.txt b/test/384.txt
new file mode 100644
index 0000000..3b13314
--- /dev/null
+++ b/test/384.txt
@@ -0,0 +1 @@
+00d1bda66c3babf4e418ec5b184354145ed5b8aa0b62f138845515191e94e6250901814a138eda0556b54f831cd7605d
\ No newline at end of file
diff --git a/test/512.key b/test/512.key
new file mode 100644
index 0000000..d8a7271
--- /dev/null
+++ b/test/512.key
@@ -0,0 +1,9 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIBOwIBAAJBAO94tO2u4cx4ZZuZNTnV9eGkfCspWjjoxIXiuEajVGFP3h+U7nRi
+uo+ZH/7DYXK8ehF4QVZXK25Boj+o0CV66PcCAwEAAQJBAMlobEO74o1mdY74vJt4
+KOUuwoBKt2dF3oP8u6Atnrp4IV9MwvSTh7OO0LncbBKSMZRDaL7b8tt5FjI8STTN
++AECIQD+3qiJl8/becyocHTlq82hO+IBxMQW/RXyEwkxYf9ZNwIhAPCIkUdapg+T
+uZJ9ho95XFyOmNzyrTqtdJRBWDqWfc5BAiAlBNQ3//vp5fwK7yKbhWO9qoP+O8U7
+jZEVcxxfttsu6wIhANMmX7outlY4TRBuxwAN/ml1+HUFR9KZ0BwRXN1ZnKjBAiAz
+fzOZ5j1T7BjEseAZPm/Xj73fu9vfFGDYWaKcPUY0Cw==
+-----END RSA PRIVATE KEY-----
diff --git a/test/512.txt b/test/512.txt
new file mode 100644
index 0000000..51cb7af
--- /dev/null
+++ b/test/512.txt
@@ -0,0 +1 @@
+005536b643ea651f2fd3c70aa83659cbd0c1f47ba803373029c6b0826db486136b4f769c6bf531ff247d6d76ea4ad050dc0e82ccedf5fd1ce07abb5192204551
\ No newline at end of file
diff --git a/test/format_test_vectors.py b/test/format_test_vectors.py
new file mode 100644
index 0000000..dd8670d
--- /dev/null
+++ b/test/format_test_vectors.py
@@ -0,0 +1,302 @@
+#
+# format_test_vectors.py
+# ------------------------------------------
+# Formats test vectors for modexp_fpga_model
+#
+# Author: Pavel Shatov
+# Copyright (c) 2017, 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.
+#
+
+#
+# This script reads the test vectors generated by regenerate_test_vectors.py
+# and writes nicely formatted C header file.
+#
+
+#
+# imports
+#
+import subprocess
+
+#
+# get part of string between two markers
+#
+def string_between(s, s_left, s_right):
+	s_begin = s.index(s_left) + len(s_left)
+	s_end = s.index(s_right, s_begin)
+	return s[s_begin:s_end]
+
+#
+# load message from file
+#
+def read_message(key):
+	with open(key + ".txt", "r") as f:
+		return f.readlines()[0]
+	
+#
+# read modulus from file
+#
+def read_modulus(key):
+	openssl_command = ["openssl", "rsa", "-in", key + ".key", "-noout", "-modulus"]
+	openssl_stdout = subprocess.check_output(openssl_command).decode("utf-8")
+	return openssl_stdout.strip().split("=")[1]
+
+#
+# read private exponent from file
+#
+def read_secret(key):
+	openssl_command = ["openssl", "rsa", "-in", key + ".key", "-noout", "-text"]
+	openssl_stdout = subprocess.check_output(openssl_command).decode("utf-8")
+	openssl_secret = string_between(openssl_stdout, "privateExponent", "prime1")
+	openssl_secret = openssl_secret.replace(":", "")
+	openssl_secret = openssl_secret.replace("\n", "")
+	openssl_secret = openssl_secret.replace(" ", "")	
+	return openssl_secret
+
+# 
+# https://en.wikibooks.org/wiki/Algorithm_Implementation/Mathematics/Extended_Euclidean_algorithm
+#
+def egcd(a, b):
+    if a == 0:
+        return (b, 0, 1)
+    else:
+        (g, y, x) = egcd(b % a, a)
+        return (g, x - (b // a) * y, y)
+
+def modinv(a, m):
+    (g, x, y) = egcd(a, m)
+    if g != 1:
+        raise Exception("Can't invert a = " + a)
+    else:
+        return x % m
+		
+#
+# format one test vector
+#
+def format_c_header(f, key, n, m, d, s):
+
+		# write all numbers in vector
+	format_c_array(f, n, "#define N_" + str(key) + " \\\n")
+	format_c_array(f, m, "#define M_" + str(key) + " \\\n")
+	format_c_array(f, d, "#define D_" + str(key) + " \\\n")
+	format_c_array(f, s, "#define S_" + str(key) + " \\\n")
+
+#
+# calculate Montgomery factor
+#
+def calc_montgomery_factor(k, n):
+	
+	f = 1
+	
+	for i in range(2 * k):
+		f1 = 2 * f
+		f2 = f1 - n
+		if f2 < 0:
+			f = f1
+		else:
+			f = f2
+	
+	return f
+
+
+#
+# calculate Montgomery modulus-dependent helper coefficient
+#
+def calc_montgomery_n_coeff(k, n):
+
+	r = 1
+	b = 1 << k
+	
+	nn = b - n
+	
+	for i in range(k-1):
+		t = (r * nn) % b
+		mask = 1 << (i + 1)
+		if (t & mask) == mask:
+			r = r + (1 << (i + 1))
+
+	return r
+				
+
+	
+#
+# format one test vector
+#
+def format_verilog_include(f, key, n, m):
+
+		# calculate factor to bring message into Montgomery domain
+	factor = calc_montgomery_factor(int(key), n)
+	
+		# calculate helper coefficient for Montgomery multiplication
+	n_coeff = calc_montgomery_n_coeff(int(key), n)
+			
+		# calculate the extra coefficient Montgomery multiplication brings in
+	coeff = modinv(1 << int(key), n)
+	
+		# convert m into Montgomery representation
+	m_factor = (m * factor * coeff) % n
+		
+		# write all numbers
+	format_verilog_concatenation(f, m,        "localparam [" + str(int(key)-1) + ":0] M_"        + str(key) + " =\n")
+	format_verilog_concatenation(f, n,        "localparam [" + str(int(key)-1) + ":0] N_"        + str(key) + " =\n")
+	format_verilog_concatenation(f, n_coeff,  "localparam [" + str(int(key)-1) + ":0] N_COEFF_"  + str(key) + " =\n")
+	format_verilog_concatenation(f, factor,   "localparam [" + str(int(key)-1) + ":0] FACTOR_"   + str(key) + " =\n")
+	format_verilog_concatenation(f, coeff,    "localparam [" + str(int(key)-1) + ":0] COEFF_"    + str(key) + " =\n")
+	format_verilog_concatenation(f, m_factor, "localparam [" + str(int(key)-1) + ":0] M_FACTOR_" + str(key) + " =\n")
+	
+	
+#
+# nicely format multi-word integer into C array initializer
+#
+def format_c_array(f, n, s):
+
+		# print '#define 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 = ""
+
+			# 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")
+	
+if __name__ == "__main__":
+
+		# list of key lengths to process
+	keys = ["384", "512"]
+
+		# open output files
+	file_h = open('modexp_fpga_model_vectors.h', 'w')
+	file_v = open('modexp_fpga_model_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")
+
+	
+		# process all the keys
+	for key in keys:
+	
+			# prepare all the numbers
+		modulus = int(read_modulus(key), 16)		# read number n from .key file
+		message = int(read_message(key), 16)		# read number m from .txt file
+		secret  = int(read_secret(key),  16)		# read number d from .key file
+		signature = pow(message, secret, modulus)	# calculate signature
+		
+			# print all the numbers
+		print("key = " + key)
+		print("  modulus   = " + hex(modulus))
+		print("  message   = " + hex(message))
+		print("  secret    = " + hex(secret))
+		print("  signature = " + hex(signature))
+
+			# format numbers and write to file
+		format_c_header(file_h, key, modulus, message, secret, signature)
+		format_verilog_include(file_v, key, modulus, message)
+
+
+		# done
+	file_h.close()
+	
+		# everything went just fine
+	print("Test vectors formatted.")
+	
+#
+# End of file
+#
diff --git a/test/modexp_fpga_model_vectors.h b/test/modexp_fpga_model_vectors.h
new file mode 100644
index 0000000..d889ada
--- /dev/null
+++ b/test/modexp_fpga_model_vectors.h
@@ -0,0 +1,46 @@
+/* Generated automatically, do not edit. */
+
+#define N_384 \
+	{0xb06b4de3, 0x1006214f, 0xaa3c8cae, 0x6c568477, \
+	 0xb8c8d8b2, 0x08684070, 0x3b9ba674, 0x739e0eec, \
+	 0x950a0d8a, 0xb6b7443d, 0xfebe690c, 0xe2810099}
+
+#define M_384 \
+	{0x00d1bda6, 0x6c3babf4, 0xe418ec5b, 0x18435414, \
+	 0x5ed5b8aa, 0x0b62f138, 0x84551519, 0x1e94e625, \
+	 0x0901814a, 0x138eda05, 0x56b54f83, 0x1cd7605d}
+
+#define D_384 \
+	{0xad24a30c, 0x766d8dc3, 0xe2100b02, 0x24d1c4b0, \
+	 0xbb6a6342, 0x577df9be, 0x89bb1ec3, 0xdc3259f0, \
+	 0x1a343f93, 0x57a12599, 0xa328ae2f, 0xf85ef401}
+
+#define S_384 \
+	{0x65752d0f, 0x9a017293, 0x36bfa115, 0x4a7a81fc, \
+	 0xa76b945b, 0x49a3f645, 0x76801499, 0xb98e6a16, \
+	 0xd2467b6a, 0x75b7d614, 0x0fff0fde, 0xb31d1819}
+
+#define N_512 \
+	{0xef78b4ed, 0xaee1cc78, 0x659b9935, 0x39d5f5e1, \
+	 0xa47c2b29, 0x5a38e8c4, 0x85e2b846, 0xa354614f, \
+	 0xde1f94ee, 0x7462ba8f, 0x991ffec3, 0x6172bc7a, \
+	 0x11784156, 0x572b6e41, 0xa23fa8d0, 0x257ae8f7}
+
+#define M_512 \
+	{0x005536b6, 0x43ea651f, 0x2fd3c70a, 0xa83659cb, \
+	 0xd0c1f47b, 0xa8033730, 0x29c6b082, 0x6db48613, \
+	 0x6b4f769c, 0x6bf531ff, 0x247d6d76, 0xea4ad050, \
+	 0xdc0e82cc, 0xedf5fd1c, 0xe07abb51, 0x92204551}
+
+#define D_512 \
+	{0xc9686c43, 0xbbe28d66, 0x758ef8bc, 0x9b7828e5, \
+	 0x2ec2804a, 0xb76745de, 0x83fcbba0, 0x2d9eba78, \
+	 0x215f4cc2, 0xf49387b3, 0x8ed0b9dc, 0x6c129231, \
+	 0x944368be, 0xdbf2db79, 0x16323c49, 0x34cdf801}
+
+#define S_512 \
+	{0xcc2fc6b6, 0xe4849987, 0x75773499, 0xcb0792b0, \
+	 0xe79f4600, 0xb2d739c5, 0x1a661ac6, 0xd3bf2db5, \
+	 0xfd1e029d, 0xfe887387, 0x4312635f, 0xb2b54b8d, \
+	 0x5d3b379e, 0x161eaa4f, 0xedfd932b, 0x780f0203}
+
diff --git a/test/modexp_fpga_model_vectors.v b/test/modexp_fpga_model_vectors.v
new file mode 100644
index 0000000..7a2b8e9
--- /dev/null
+++ b/test/modexp_fpga_model_vectors.v
@@ -0,0 +1,68 @@
+/* Generated automatically, do not edit. */
+
+localparam [383:0] M_384 =
+	{32'h00d1bda6, 32'h6c3babf4, 32'he418ec5b, 32'h18435414, 
+	 32'h5ed5b8aa, 32'h0b62f138, 32'h84551519, 32'h1e94e625, 
+	 32'h0901814a, 32'h138eda05, 32'h56b54f83, 32'h1cd7605d};
+
+localparam [383:0] N_384 =
+	{32'hb06b4de3, 32'h1006214f, 32'haa3c8cae, 32'h6c568477, 
+	 32'hb8c8d8b2, 32'h08684070, 32'h3b9ba674, 32'h739e0eec, 
+	 32'h950a0d8a, 32'hb6b7443d, 32'hfebe690c, 32'he2810099};
+
+localparam [383:0] N_COEFF_384 =
+	{32'hd4511719, 32'h4d546bb2, 32'hdf614d49, 32'h81e00324, 
+	 32'h07b0ccd5, 32'h44ccffdc, 32'h1d37c210, 32'h1733fb82, 
+	 32'h35cf01cb, 32'hf7bf10e9, 32'h4238c3df, 32'h2712ac57};
+
+localparam [383:0] FACTOR_384 =
+	{32'h7643f0e6, 32'h7057fffc, 32'h92e27890, 32'h66a30c4a, 
+	 32'ha6e3ef37, 32'h97d72f8a, 32'h0d4a5392, 32'heb5ad4a1, 
+	 32'h0aa57383, 32'h1e3a5028, 32'h0cbaa40e, 32'hb17a468b};
+
+localparam [383:0] COEFF_384 =
+	{32'h9250be5e, 32'hc1e19afe, 32'h99341b1b, 32'hda823ac7, 
+	 32'hcc46fbd2, 32'he7da1ff3, 32'he36e49fd, 32'hd593b36c, 
+	 32'h09ae9f5e, 32'h7f44805d, 32'hf77c6019, 32'h45563ab6};
+
+localparam [383:0] M_FACTOR_384 =
+	{32'h8166125d, 32'h30aece3d, 32'h30d77d7e, 32'h8b0791ba, 
+	 32'h91e92683, 32'hc483bb6c, 32'h0ee1571d, 32'h6e28c2f5, 
+	 32'hff5e6b61, 32'h65fb6164, 32'hd3651e5a, 32'h746b8ca0};
+
+localparam [511:0] M_512 =
+	{32'h005536b6, 32'h43ea651f, 32'h2fd3c70a, 32'ha83659cb, 
+	 32'hd0c1f47b, 32'ha8033730, 32'h29c6b082, 32'h6db48613, 
+	 32'h6b4f769c, 32'h6bf531ff, 32'h247d6d76, 32'hea4ad050, 
+	 32'hdc0e82cc, 32'hedf5fd1c, 32'he07abb51, 32'h92204551};
+
+localparam [511:0] N_512 =
+	{32'hef78b4ed, 32'haee1cc78, 32'h659b9935, 32'h39d5f5e1, 
+	 32'ha47c2b29, 32'h5a38e8c4, 32'h85e2b846, 32'ha354614f, 
+	 32'hde1f94ee, 32'h7462ba8f, 32'h991ffec3, 32'h6172bc7a, 
+	 32'h11784156, 32'h572b6e41, 32'ha23fa8d0, 32'h257ae8f7};
+
+localparam [511:0] N_COEFF_512 =
+	{32'he20d94ee, 32'hee497b57, 32'h13d83f00, 32'h7445c269, 
+	 32'h7258393f, 32'h5a725633, 32'hf7349437, 32'h83681b0a, 
+	 32'h5a4be08f, 32'h7f49b1eb, 32'h09221d14, 32'h5bb82985, 
+	 32'h2599e229, 32'h1a15218d, 32'h62f8803c, 32'haa5fa739};
+
+localparam [511:0] FACTOR_512 =
+	{32'h220ef9ff, 32'h55989a78, 32'h722c5c1e, 32'ha466c81c, 
+	 32'h6e6e0423, 32'h1242da6d, 32'h1482351e, 32'h5d14212e, 
+	 32'h8fc70923, 32'hb24cad26, 32'h3f3538a9, 32'h4f0a6bdf, 
+	 32'h14ca36b9, 32'h819a72fc, 32'h9b5ea0ea, 32'h8b4fe28b};
+
+localparam [511:0] COEFF_512 =
+	{32'hd375442c, 32'h4e2dcb0d, 32'hcfd2f151, 32'hc694c180, 
+	 32'h3781078f, 32'hf2647315, 32'hc5f685c2, 32'ha75c0e9d, 
+	 32'h545f18cb, 32'hec66027f, 32'h229cb16f, 32'hc8613357, 
+	 32'h8f92a4c4, 32'h50de0908, 32'h6d8a8d78, 32'h7011f42d};
+
+localparam [511:0] M_FACTOR_512 =
+	{32'h099cc6e8, 32'h778f03a1, 32'h329890e9, 32'ha0a86dd2, 
+	 32'hd7354e49, 32'h00a2d28a, 32'h2b0848c6, 32'hb08915dc, 
+	 32'h663032a3, 32'h70734b62, 32'h2d30c132, 32'hefa75cc6, 
+	 32'h9f18b32a, 32'h97d6ddf8, 32'h2f6df2d0, 32'he9098874};
+
diff --git a/test/regenerate_test_vectors.py b/test/regenerate_test_vectors.py
new file mode 100644
index 0000000..dcd1bc8
--- /dev/null
+++ b/test/regenerate_test_vectors.py
@@ -0,0 +1,85 @@
+#
+# regenerate_test_vectors.py
+# ------------------------------------------------------
+# Generates a new test vectors set for modexp_fpga_model
+#
+# Author: Pavel Shatov
+# Copyright (c) 2017, 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.
+#
+
+#
+# This script generates a new pair of test vectors. Every test vector contains
+# public and private keypair and a demo message.
+#
+
+#
+# imports
+#
+import time
+import hashlib
+import subprocess
+
+if __name__ == "__main__":
+
+		# generate two new keys
+	subprocess.call(["openssl", "genrsa", "-out", "384.key",  "384"])
+	subprocess.call(["openssl", "genrsa", "-out", "512.key",  "512"])
+
+		# get current date and time
+	dt_date = time.strftime("%Y-%m-%d")
+	dt_time = time.strftime("%H:%M:%S")
+
+		# format demo message
+	msg = dt_date + " " + dt_time;
+	print("msg = '" + msg + "'")
+
+		# get two hashes of different lengths
+	m384 = hashlib.sha384(msg.encode("utf-8")).hexdigest()
+	m512 = hashlib.sha512(msg.encode("utf-8")).hexdigest()
+	
+		# zero out the leftmost byte of each hash to make sure, that its
+		# numerical value is smaller than the corresponding modulus
+	m384 = "00" + m384[2:]
+	m512 = "00" + m512[2:]
+
+		# save 384-bit message
+	with open("384.txt", "w") as file_txt:
+		file_txt.write(m384)
+		
+		# save 512-bit message
+	with open("512.txt", "w") as file_txt:
+		file_txt.write(m512)
+		
+		# everything went just fine
+	print("New random test vectors generated.")
+
+#
+# End of file
+#



More information about the Commits mailing list