diff options
Diffstat (limited to 'vector')
-rw-r--r-- | vector/.gitignore | 3 | ||||
-rw-r--r-- | vector/README.md | 10 | ||||
-rw-r--r-- | vector/vector_format.py | 67 | ||||
-rw-r--r-- | vector/vector_regenerate.py | 48 | ||||
-rw-r--r-- | vector/vector_util.py | 319 |
5 files changed, 0 insertions, 447 deletions
diff --git a/vector/.gitignore b/vector/.gitignore deleted file mode 100644 index f9daf64..0000000 --- a/vector/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/__pycache__/ -/*_randomized.key -/*_randomized.py diff --git a/vector/README.md b/vector/README.md deleted file mode 100644 index 3bd1853..0000000 --- a/vector/README.md +++ /dev/null @@ -1,10 +0,0 @@ -ModExpNG -======== - -Ranzomized test vector generation scripts for ModExpNG core model. - - * `vector_regenerate.py` generates a new random RSA keypair using OpenSSL. Each invocation overwrites the keypair, the old one is not retained. **Never use the generated keypair for anything outside of this model!** - * `vector_format.py` processes the previously generated keypair. It first generates a "random" demo message to be signed and a "random" blinding factor, signs the message and checks the signature using Python's built-in math. If everything goes well, it writes the formatted test vector to a file. - * `vector_util.py` is a helper module. - -To obtain a test vector, optionally edit _KEY_LENGTH_ in `vector_regenerate.py` to set desired key length, then run the script to generate randomized key file. Then optionally edit _KEY_LENGTH_ in `vector_format.py` to match key length and change _RND_SEED_MESSAGE_ to get a different demo message and _RND_SEED_BLINDING_ to get a different blinding factor. Finally run the script to obtain randomized test vector module. diff --git a/vector/vector_format.py b/vector/vector_format.py deleted file mode 100644 index a3e7e81..0000000 --- a/vector/vector_format.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/python3 -# -# -# Formats a new test vector for ModExpNG core model. -# -# -# Copyright (c) 2019, 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. -# - -import sys -import vector_util - -SCRIPT_USAGE = "USAGE: vector_format.py [openssl_binary]" - -KEY_LENGTH = 1024 - -RNG_SEED_MESSAGE = 1 -RNG_SEED_BLINDING = 2 - - -if __name__ == "__main__": - - # ModInv fails otherwise... - sys.setrecursionlimit(int(1.5 * KEY_LENGTH)) - - OPENSSL_BINARY = vector_util.openssl_binary(SCRIPT_USAGE) - - if len(OPENSSL_BINARY) > 0: - - MESSAGE = vector_util.random_message(RNG_SEED_MESSAGE, KEY_LENGTH) - BLINDING = vector_util.random_blinding(RNG_SEED_BLINDING, KEY_LENGTH) - VECTOR = vector_util.load_vector(OPENSSL_BINARY, KEY_LENGTH) - - vector_ok = VECTOR.selfcheck(MESSAGE, BLINDING) - if vector_ok: - vector_util.save_vector(VECTOR) - print("Test vector formatted.") - else: - print("Failed to format test vector.") - diff --git a/vector/vector_regenerate.py b/vector/vector_regenerate.py deleted file mode 100644 index 34c6384..0000000 --- a/vector/vector_regenerate.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/python3 -# -# -# Generates a new ranzomized test vector for ModExpNG core model. -# -# -# Copyright (c) 2019, 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. -# - -import vector_util - -SCRIPT_USAGE = "USAGE: vector_regenerate.py [openssl_binary]" - -KEY_LENGTH = 1024 - -if __name__ == "__main__": - - OPENSSL_BINARY = vector_util.openssl_binary(SCRIPT_USAGE) - - if len(OPENSSL_BINARY) > 0: - vector_util.openssl_genrsa(OPENSSL_BINARY, KEY_LENGTH) diff --git a/vector/vector_util.py b/vector/vector_util.py deleted file mode 100644 index 37e4fb6..0000000 --- a/vector/vector_util.py +++ /dev/null @@ -1,319 +0,0 @@ -#!/usr/bin/python3 -# -# -# Helper routines for ModExpNG randomized test vector generator. -# -# -# Copyright (c) 2019, 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. -# - - -import sys -import random -import subprocess -from enum import Enum, auto - - -class VectorPiece(Enum): - VectorPieceOther = auto() - VectorPieceN = auto() - VectorPieceD = auto() - VectorPieceP = auto() - VectorPieceQ = auto() - VectorPieceDP = auto() - VectorPieceDQ = auto() - VectorPieceQINV = auto() - - -class Vector: - - # public exponent - _f4 = 0x10001 - - def __init__(self, length): - self._bits = length - self._n = "" - self._d = "" - self._p = "" - self._q = "" - self._dp = "" - self._dq = "" - self._qinv = "" - - def _add_piece(self, type, value): - value = value.replace(":", "") - value = value.replace("\r", "") - value = value.replace("\n", "") - value = value.replace(" ", "") - - if type == VectorPiece.VectorPieceN: self._n += value - elif type == VectorPiece.VectorPieceD: self._d += value - elif type == VectorPiece.VectorPieceP: self._p += value - elif type == VectorPiece.VectorPieceQ: self._q += value - elif type == VectorPiece.VectorPieceDP: self._dp += value - elif type == VectorPiece.VectorPieceDQ: self._dq += value - elif type == VectorPiece.VectorPieceQINV: self._qinv += value - else: raise Exception("Invalid vector piece type!") - - def _calc_mont_factor(self, length, modulus): - return pow(2, 2*length, modulus) - - def _calc_mod_coeff(self, length, modulus): - - pwr = pow(2, length) - pwr_mask = pwr - 1 - - r = 1 - b = 1 - - nn = ((modulus ^ pwr_mask) + 1) % pwr - - for i in range(1, length): - - b = (b << 1) % pwr - t = (r * nn) % pwr - - if t & (1 << i): r += b - - return r - - def _calc_blind_y(self, x, modulus): - x_inv = self._modinv(x, modulus) - return pow(x_inv, self._f4, modulus) - - def _egcd(self, a, b): - if a == 0: - return (b, 0, 1) - else: - g, y, x = self._egcd(b % a, a) - return (g, x - (b // a) * y, y) - - def _modinv(self, a, m): - g, x, y = self._egcd(a, m) - if g != 1: - raise Exception("_modinv() failed!") - else: - return x % m - - def selfcheck(self, message, blinding): - - self.m = message # message (padded) - self.n = int(self._n, 16) # modulus - self.d = int(self._d, 16) # private key - self.p = int(self._p, 16) # part of modulus - self.q = int(self._q, 16) # part of modulus - self.dp = int(self._dp, 16) # smaller exponent - self.dq = int(self._dq, 16) # smaller exponent - self.qinv = int(self._qinv, 16) # helper coefficient - - self.x = blinding - self.y = self._calc_blind_y(self.x, self.n) - - # check modulus - if self.n == 0: - print("ERROR: n == 0") - return False - - if self.n != self.p * self.q: - print("ERROR: n != (p * q)") - return False - - # check smaller exponents - if self.dp != (self.d % (self.p-1)): - print("ERROR: dp != (d % (p-1))") - return False - - if self.dq != (self.d % (self.q-1)): - print("ERROR: dq != (d % (q-1))") - return False - - # sign to obtain known good value - s_reference = pow(message, self.d, self.n) - - # blind message - message_blinded = (message * self.y) % self.n - - # sign blinded message - s_blinded = pow(message_blinded, self.d, self.n) - - # unblind signature - s_unblinded = (s_blinded * self.x) % self.n - - # check, that x and y actually work - if s_unblinded != s_reference: - print("ERROR: s_unblinded != s_reference!") - return False - - # try to do crt with the blinded message - sp_blinded = pow(message_blinded, self.dp, self.p) - sq_blinded = pow(message_blinded, self.dq, self.q) - - # recover full blinded signature - sr_blinded = sp_blinded - sq_blinded - if sr_blinded < 0: sr_blinded += self.p - - sr_qinv_blinded = (sr_blinded * self.qinv) % self.p - - s_crt_blinded = sq_blinded + self.q * sr_qinv_blinded - - # unblind crt signature - s_crt_unblinded = (s_crt_blinded * self.x) % self.n - - if s_crt_unblinded != s_reference: - print("ERROR: s_crt_unblinded != s_reference!") - return False - - self.n_factor = self._calc_mont_factor(self._bits + 16, self.n) - self.p_factor = self._calc_mont_factor(self._bits // 2 + 16, self.p) - self.q_factor = self._calc_mont_factor(self._bits // 2 + 16, self.q) - - self.n_coeff = self._calc_mod_coeff(self._bits + 16, self.n) - self.p_coeff = self._calc_mod_coeff(self._bits // 2 + 16, self.p) - self.q_coeff = self._calc_mod_coeff(self._bits // 2 + 16, self.q) - - print("Test vector checked.") - - return True - - -def openssl_binary(usage): - - # nothing so far - openssl = "" - - # user didn't overide anything - if len(sys.argv) == 1: - openssl = "openssl" - print("Using system OpenSSL library.") - - # user requested some specific binary - elif len(sys.argv) == 2: - openssl = sys.argv[1] - print("Using OpenSSL binary '" + openssl + "'...") - - # didn't understand command line - else: - print(usage) - - # return path to selected binary (if any) - return openssl - - -def openssl_genrsa(binary, length): - - filename = str(length) + "_randomized.key" - subprocess.call([binary, "genrsa", "-out", filename, str(length)]) - - -def random_message(seed, length): - - message = 0 - num_bytes = length // 8 - 1 - - random.seed(seed) - - for i in range(num_bytes): - message <<= 8 - message += random.getrandbits(8) - - return message - - -def random_blinding(seed, length): - - blinding = 0 - num_bytes = length // 8 - 1 - - random.seed(seed) - - for i in range(num_bytes): - blinding <<= 8 - blinding += random.getrandbits(8) - - return blinding - - -def load_vector(binary, length): - - vector = Vector(length) - piece_type = VectorPiece.VectorPieceOther - - filename = str(length) + "_randomized.key" - openssl_command = [binary, "rsa", "-in", filename, "-noout", "-text"] - openssl_stdout = subprocess.check_output(openssl_command).decode("utf-8").splitlines() - - for line in openssl_stdout: - if line.startswith("RSA Private-Key:"): piece_type = VectorPiece.VectorPieceOther - elif line.startswith("modulus:"): piece_type = VectorPiece.VectorPieceN - elif line.startswith("publicExponent:"): piece_type = VectorPiece.VectorPieceOther - elif line.startswith("privateExponent:"): piece_type = VectorPiece.VectorPieceD - elif line.startswith("prime1:"): piece_type = VectorPiece.VectorPieceP - elif line.startswith("prime2:"): piece_type = VectorPiece.VectorPieceQ - elif line.startswith("exponent1:"): piece_type = VectorPiece.VectorPieceDP - elif line.startswith("exponent2:"): piece_type = VectorPiece.VectorPieceDQ - elif line.startswith("coefficient:"): piece_type = VectorPiece.VectorPieceQINV - else: vector._add_piece(piece_type, line) - - return vector - - -def save_vector(vector): - - filename = "vector_" + str(vector._bits) + "_randomized.py" - print("Writing to '%s'..." % filename) - - f = open(filename, 'w') - - f.write("# Generated automatically, do not edit.\n\n") - - f.write("class Vector:\n") - f.write(" m = 0x%x\n" % vector.m) - f.write(" n = 0x%x\n" % vector.n) - f.write(" d = 0x%x\n" % vector.d) - f.write(" p = 0x%x\n" % vector.p) - f.write(" q = 0x%x\n" % vector.q) - f.write(" dp = 0x%x\n" % vector.dp) - f.write(" dq = 0x%x\n" % vector.dq) - f.write(" qinv = 0x%x\n" % vector.qinv) - f.write(" n_factor = 0x%x\n" % vector.n_factor) - f.write(" p_factor = 0x%x\n" % vector.p_factor) - f.write(" q_factor = 0x%x\n" % vector.q_factor) - f.write(" n_coeff = 0x%x\n" % vector.n_coeff) - f.write(" p_coeff = 0x%x\n" % vector.p_coeff) - f.write(" q_coeff = 0x%x\n" % vector.q_coeff) - f.write(" x = 0x%x\n" % vector.x) - f.write(" y = 0x%x\n" % vector.y) - - f.close() - - -# -# End of file -# |