diff --git a/.gitignore b/.gitignore index e43b0f988953ae3a84b00331d0ccf5f7d51cb3cf..dde3895fc112ad34a839b2fed9210ac2288a959b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .DS_Store +*.pyc diff --git a/bsp_UOV.py b/bsp_UOV.py new file mode 100644 index 0000000000000000000000000000000000000000..4d9b5d28f5825fc24704c4cbc964cd299068f7f8 --- /dev/null +++ b/bsp_UOV.py @@ -0,0 +1,33 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function +from sage.all import * + +from classes.helpers.PublicKey import PublicKey +from classes.helpers.PrivateKey import PrivateKey +from classes.UnbalancedOilAndVinegar import UnbalancedOilAndVinegar + +# Endlicher Körper mit 5 Elementen: +finite_field = GF(5, 'a') + +m = 2 # Zwei Gleichungen +n = 3 # Drei Variablen + +"""TEST UOV""" + +# Initialize simple UOV +UOV = UnbalancedOilAndVinegar(finite_field, n, m, keyfile="./keys/bsp_uov2.priv") +private_key = UOV.private_key +public_key = UOV.public_key + +# use a random vector +msg = vector(finite_field, [1, 4]) + +# generate Signature +sig = UOV.sign(msg) + +# verify signature is valid +print("msg =", msg) +print("sig =", sig) +print("UOV.verify(msg, sig):", UOV.verify(msg, sig)) diff --git a/classes/MatsumotoImaiA.py b/classes/MatsumotoImaiA.py new file mode 100644 index 0000000000000000000000000000000000000000..3eec629050f0829d3cd132fe4a406dd12c28342c --- /dev/null +++ b/classes/MatsumotoImaiA.py @@ -0,0 +1,83 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, print_function +from sage.all import * + +from .helpers.AffineTransformation import AffineTransformation +from .helpers.PrivateKey import PrivateKey +from .helpers.PrivateKey import PublicKey +from .helpers.EncryptionScheme import EncryptionScheme + + +class MatsumotoImaiA(EncryptionScheme): + """Matsumoto Imai Scheme A Implementation""" + + def phi(self, polynomail_a): + return polynomail_a.list() + + def phi_inv(self, vector_a): + return self.extension_field(vector_a) + + def __init__(self, finite_field, n, keyfile=None): + self.finite_field = finite_field + self.n = n + + # generate or load private & public key + if keyfile is not None: + self.private_key = PrivateKey(None, None, None, keyfile) + self.public_key = self.private_key.generate_public_key() + else: + self.generate_keys(n) + + def generate_keys(self, n): + k = self.finite_field + (a,) = k.gens() + + ring = PolynomialRing(k, 'x') + (x,) = ring.gens() + + q = len(k) + + # alle Kandidaten für theta werden gesucht + thetas = [] + qn = q**n - 1 + for theta in xrange(1, n - 1): + qd = q**theta + 1 + (common_divider, t, _) = xgcd(qd, qn) + if common_divider == 1: + thetas.append((theta, t)) + + # ein zufälliges Theta wird ausgewählt + print(len(thetas)) + (self.theta, self.theta_invers) = thetas[int(random() * len(thetas))] + + S = AffineTransformation( + random_invertible_matrix(k, n), random_vector(k, n)) + T = AffineTransformation( + random_invertible_matrix(k, n), random_vector(k, n)) + + # der Erweiterungskörper wird gebaut + gx = ring.irreducible_element(n) + multivariate_ring = PolynomialRing(k, n) + extension_field = PolynomialRing(multivariate_ring, 't').quotient_ring(gx, 'T') + self.extension_field = extension_field + multi_vars = multivariate_ring.gens() + + Sx = list(S(vector(multi_vars))) + + pre_F = self.phi_invers(Sx) + + '''Now the MAGIC happens''' + post_F = pre_F * pre_F**(4**theta) + + Pr = S.inverse(vector(post_F.list())) + SPTx = T(vector(post_F.list())) + + self.private_key = PrivateKey(S, Pr, T) + self.public_key = PublicKey(SPTx) + + def invert_MQ(self, msg): + X = self.phi_inv(list(msg)) + X = X**self.theta_invers + return vector(self.phi(X)) diff --git a/classes/UnbalancedOilAndVinegar.py b/classes/UnbalancedOilAndVinegar.py index a243b9e29b1b219a13581900ec1d70092648868b..e9bfd927d32b325e88e826ce71deb8c4ba01882d 100644 --- a/classes/UnbalancedOilAndVinegar.py +++ b/classes/UnbalancedOilAndVinegar.py @@ -1,3 +1,6 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- + from __future__ import absolute_import, print_function from sage.all import * import copy as cp @@ -12,7 +15,7 @@ from .helpers.SignatureScheme import SignatureScheme class UnbalancedOilAndVinegar(SignatureScheme): """Unalanced Oil and Vinegar Implementation""" - def __init__(self, finite_field, ring, n, m, keyfile=None): + def __init__(self, finite_field, n, m, keyfile=None): """ UOV wird mit dem Grundkörper `F`, Länge der Nachrichten `n` und Länge der Signatur `m` initialisiert @@ -98,7 +101,7 @@ class UnbalancedOilAndVinegar(SignatureScheme): F = self.finite_field # wichtig ist, dass private_key.Pr `UOV`-Gestalt hat - Pr = cp.copy(self.private_key.P) + Pr = cp.copy(self.private_key.Pr) sig = [] @@ -109,8 +112,10 @@ class UnbalancedOilAndVinegar(SignatureScheme): x[i] = random_value(F) sig.append(x[i]) + print("random_values =", sig ) + # Durch einsetzen der belegten Variablen wird P linearisiert - for i in xrange(0, len(P)): + for i in xrange(0, len(Pr)): Pr[i] = Pr[i](x) # Die gelösten Variablen werden entfernt @@ -134,8 +139,9 @@ class UnbalancedOilAndVinegar(SignatureScheme): b = msg - b # sagemath bietet eine einfache Methode die Lösung des GS zu finden + # sollte keine Lösung gefunden werden können muss die for x in A.solve_right(b): sig.append(x) - # Die Lösung von "P(sig) = msg" wird zurückgegeben + # Die Lösung von "Pr(sig) = msg" wird zurückgegeben return vector(F, sig) diff --git a/classes/__init__.py b/classes/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/classes/helpers/AffineTransformation.py b/classes/helpers/AffineTransformation.py index ca63c101b7e306bd3d8d90fa89773d87e2b1d5f9..cbafeb02dc46513a4fe6269062cc44e71e94feba 100644 --- a/classes/helpers/AffineTransformation.py +++ b/classes/helpers/AffineTransformation.py @@ -1,3 +1,6 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- + from __future__ import absolute_import, division, print_function from sage.all import * @@ -16,20 +19,20 @@ class AffineTransformation(): self.M = M # invertierbare Matrix self.y = y # Vektor - def __call__(self, vector): + def __call__(self, v): """Anwenden der Transformation auf den gegebenen Vektor""" - # Sollte `vector` eine Liste sein -> Typecast + # Sollte `v` eine Liste sein -> Typecast if not isinstance(vector, sage.structure.element.Vector): - vector = vector(vector) + v = vector(v) - return (self.M * vector) + self.y + return (self.M * v) + self.y - def inverse(self, vector): + def inverse(self, v): """Anwenden der inversen Transformation auf den gegebenen Vektor""" - # Sollte `vector` eine Liste sein -> Typecast + # Sollte `v` eine Liste sein -> Typecast if not isinstance(vector, sage.structure.element.Vector): - vector = vector(vector) + v = vector(v) - return self.M.inverse() * (vector - self.y) + return self.M.inverse() * (v - self.y) diff --git a/classes/helpers/EncryptionScheme.py b/classes/helpers/EncryptionScheme.py index a59d1ddde4a597946feca1454ccfbcb0317acc8b..688512ad79a91be617778194a338c94607ae7b0b 100644 --- a/classes/helpers/EncryptionScheme.py +++ b/classes/helpers/EncryptionScheme.py @@ -1,3 +1,5 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- from sage.all import * from .SignatureScheme import SignatureScheme diff --git a/classes/helpers/PolynomialSystem.py b/classes/helpers/PolynomialSystem.py new file mode 100644 index 0000000000000000000000000000000000000000..d913c8cf341c77f2e8b4d5d57a5f39622b7e8d45 --- /dev/null +++ b/classes/helpers/PolynomialSystem.py @@ -0,0 +1,20 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function + + +class PolynomialSystem(object): + def __init__(self, P): + self.P = P + + def __repr__(self): + return self.P + + def __call__(self, variables): + + ret = [] + for p in self.P: + ret.append(p(variables)) + + return ret diff --git a/classes/helpers/PrivateKey.py b/classes/helpers/PrivateKey.py index 6797c77666741526f27e36535cc4d6e77820bd84..14c66a0b34c52424e0099a369a95f163ee74a632 100644 --- a/classes/helpers/PrivateKey.py +++ b/classes/helpers/PrivateKey.py @@ -1,3 +1,6 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- + from __future__ import absolute_import, division, print_function from sage.all import vector import cPickle as pickle @@ -9,7 +12,7 @@ class PrivateKey(object): def __init__(self, S, Pr, T, path=None): # wird ein Schlüssel als Dateipfad übergeben, so wird dieser geladen if path is not None: - (self.S, self.P, self.T) = self.load(path) + (S, Pr, T) = self.load(path) self.S = S # Affine Transformation self.Pr = Pr # Polynomsystem @@ -17,7 +20,7 @@ class PrivateKey(object): def generate_public_key(self): # in `x` sind die Variablen x1,...,xn - x = vector(self.P[0].parent().gens()) + x = vector(self.Pr[0].parent().gens()) # angewendet auf die Verschlüsselung ergibt # dies das öffentliche Polynom @@ -29,15 +32,17 @@ class PrivateKey(object): # Die Verschlüsselung wurde angepasst um nicht von # dem PublicKey abzuhängen sondern von (S,Pr,T) sx = list(self.S(vector(message))) - spx = self.Pr(list(sx)) - return self.T(spx) + spx = [] + for p in self.Pr: + spx.append(p(sx)) + return self.T(vector(spx)) """ Es folgt eine sehr einfache Möglichkeit den Schlüssel zu speichern und entsprechend auch wieder zu laden """ - def save(self, path="./keys/uov.priv"): + def save(self, path="./keys/key.priv"): with open(path, 'wb') as output: - pickle.dump((self.S, self.P, self.T), + pickle.dump((self.S, self.Pr, self.T), output, pickle.HIGHEST_PROTOCOL) def load(self, path): diff --git a/classes/helpers/PublicKey.py b/classes/helpers/PublicKey.py index c6d75b2f4b9cc9e323553a36cb64b84b0a716d6f..8ffd221d1908fc9402b7f3e5cba4e5134ea37b18 100644 --- a/classes/helpers/PublicKey.py +++ b/classes/helpers/PublicKey.py @@ -1,4 +1,8 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- + from __future__ import absolute_import, print_function +from sage.all import vector import cPickle as pickle @@ -20,16 +24,21 @@ class PublicKey(object): def encrypt(self, msg): # der Vektor `msg` muss als Liste an Sage übergeben werden # Sage kann dann eigenständig das Polynomsystem ausrechen - return self.P(list(msg)) + msg = list(msg) + solution = [] + for p in self.P: + solution.append(p(msg)) + + return vector(solution) - def verify(self, msg, signature): + def verify(self, msg, sig): # Vergleiche gegebene Singatur mit generierter Signatur - return signature == self.encrypt(msg) + return msg == self.encrypt(sig) """ Es folgt eine sehr einfache Möglichkeit den Schlüssel zu speichern und entsprechend auch wieder zu laden """ - def save(self, path="./keys/uov.pub"): + def save(self, path="./keys/key.pub"): with open(path, 'wb') as output: pickle.dump(self.P, output, pickle.HIGHEST_PROTOCOL) diff --git a/classes/helpers/SignatureScheme.py b/classes/helpers/SignatureScheme.py index 63e88815a24d3aff2565fcc1deffe5c07a4269c0..ab6b722b63eb71dc8bb549168c4e937cc27418cc 100644 --- a/classes/helpers/SignatureScheme.py +++ b/classes/helpers/SignatureScheme.py @@ -1,3 +1,6 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- + from sage.all import * @@ -22,7 +25,7 @@ class SignatureScheme(object): '''Platzhalter für alle Signatur- und Verschlüsselungsverfahren''' raise NotImplementedError - def verify(self, signature, msg): + def verify(self, msg, sig): # da SignatureScheme nicht direkt von PublicKey erbt wird # die Verifikation extra zur Verfügung gestellt - return self.public_key.verify(signature, msg) + return self.public_key.verify(msg, sig) diff --git a/classes/helpers/sage_extensions.py b/classes/helpers/sage_extensions.py index 6e872321ab39f169e004abac4eeab79dc34fd3d1..59f7a50d7a72080a7028f98cd74e0c210dc3b1d1 100644 --- a/classes/helpers/sage_extensions.py +++ b/classes/helpers/sage_extensions.py @@ -1,3 +1,6 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- + from __future__ import absolute_import from sage.all import random_matrix, random_vector, random diff --git a/keys/bsp_uov.priv b/keys/bsp_uov.priv new file mode 100644 index 0000000000000000000000000000000000000000..b58b64b7dff26ab5dfef0d56e179a060c63bb4f9 Binary files /dev/null and b/keys/bsp_uov.priv differ diff --git a/keys/bsp_uov2.priv b/keys/bsp_uov2.priv new file mode 100644 index 0000000000000000000000000000000000000000..9888565e6ce5c97005eb2b57c3a08dfa7b77ac05 Binary files /dev/null and b/keys/bsp_uov2.priv differ diff --git a/test.py b/test.py new file mode 100644 index 0000000000000000000000000000000000000000..8d6db94ea84398147c526612bdfeab4b06a89d4b --- /dev/null +++ b/test.py @@ -0,0 +1,28 @@ +from __future__ import absolute_import, division, print_function +from sys import exit +from sage.all import * + +from classes.helpers.AffineTransformation import * + +k = GF(4, 'a') # hat modulus x^2 + x + 1 +(a,) = k.gens() +n = 3 + +ring = PolynomialRing(k, 'x') +(x,) = ring.gens() +gx = x**3 + x + 1 + +K = ring.quotient_ring(gx, 'X') + +q = len(k) + +thetas = [] +qn = q**n - 1 +print("qn =", qn) +for theta in xrange(1, n): + qd = q**theta + 1 + print("qd =", qd) + (common_divider, t, _) = xgcd(qd, qn) + if common_divider == 1: + thetas.append((theta, t)) +thetas diff --git a/test_UOV.py b/test_UOV.py new file mode 100644 index 0000000000000000000000000000000000000000..30f7225286c467ccdc8e2002e26979e1134f296e --- /dev/null +++ b/test_UOV.py @@ -0,0 +1,43 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function +from sys import exit +from sage.all import * + +from classes.UnbalancedOilAndVinegar import UnbalancedOilAndVinegar + +# Variables that may be set by CLI +q = 5 # Number of elements in F +m = 4 # length of message +n = 6 # length of signature + +# Computed Variables +if n <= m: + exit(1) + +oil = n - m # number of oil variables +vinegar = m # number of vinegar variables + +# Finite field with q elements +finite_field = GF(q, 'a') + +# Multivariate Polynomial Ring over +# `finite_field` with `n` variables +ring = PolynomialRing(finite_field, 'x', n) + +"""TEST UOV""" + +# Initialize simple UOV +UOV = UnbalancedOilAndVinegar(finite_field, n, m) + +# use a random vector +msg = random_vector(finite_field, m) + +# generate Signature +sig = UOV.sign(msg) + +# verify signature is valid +print("msg =", msg) +print("sig =", sig) +print("UOV.verify(msg, sig):", UOV.verify(msg, sig))