diff --git a/bsp_MIA.py b/bsp_MIA.py new file mode 100644 index 0000000000000000000000000000000000000000..ae6eed20dd1e172d17273392bb158c350b649da1 --- /dev/null +++ b/bsp_MIA.py @@ -0,0 +1,54 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function +from sage.all import * + +from classes.MatsumotoImaiAExample import MatsumotoImaiAExample + +# Endlicher Körper mit 5 Elementen: +finite_field = GF(2**2, 'a') +(a,) = finite_field.gens() + +n = 3 # Drei Variablen + +"""TEST MIA""" + +# MIA mit den Schlüsseln initialisieren +# MatsumotoImaiAExample setzt die sonst zufälligen Werte +# wie in den Beispielen und printed mehr +MIA = MatsumotoImaiAExample(finite_field, n) + + +# Schlüssel im sage CLI nutzbar machen +private_key = MIA.private_key +public_key = MIA.public_key + +# Schlüssel ausgeben +print("Privater Schlüssel:") +print("S:\n{}x + {}".format(private_key.S.M, private_key.S.y)) +print("Pr:\n{}".format(private_key.Pr)) +print("S:\n{}x + {}".format(private_key.T.M, private_key.T.y)) + +print("\nÖffentlicher Schlüssel:") +print("P:\n{}".format(public_key.P)) + +# Die Nachricht soll (1, a, a^2) sein +msg = vector(finite_field, [1, a, a**2]) +print("\n\nmsg =", msg) + +# Nachricht verschlüsseln +enc = public_key.encrypt(msg) +print("public_key.encrypt(msg): enc =", enc) + +# Verifizieren, dass enc auch entschlüsselt wird +print("MIA.decryt(enc):", MIA.decrypt(enc)) + +# Signatur erstellen +msg = vector(finite_field, [a, 1, 1]) +print("\nmsg =", msg) +sig = MIA.sign(msg) +print("sig =", sig) + +# Signatur verifizieren +print("public_key.verify(msg, sig):", public_key.verify(msg, sig)) diff --git a/bsp_UOV.py b/bsp_UOV.py index 4d9b5d28f5825fc24704c4cbc964cd299068f7f8..cd28c51416f44c67643317d840188579a52450fb 100644 --- a/bsp_UOV.py +++ b/bsp_UOV.py @@ -4,9 +4,7 @@ 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 +from classes.UnbalancedOilAndVinegarExample import * # Endlicher Körper mit 5 Elementen: finite_field = GF(5, 'a') @@ -16,18 +14,32 @@ n = 3 # Drei Variablen """TEST UOV""" -# Initialize simple UOV -UOV = UnbalancedOilAndVinegar(finite_field, n, m, keyfile="./keys/bsp_uov2.priv") +# UOV mit den Schlüsseln initialisieren +# UnbalancedOilAndVinegarExample setzt die sonst zufälligen Werte +# wie in den Beispielen und printed mehr +UOV = UnbalancedOilAndVinegarExample( + finite_field, n, m, keyfile="./keys/bsp_uov.priv") + +# Schlüssel im sage CLI nutzbar machen private_key = UOV.private_key public_key = UOV.public_key -# use a random vector +# Schlüssel ausgeben +print("Privater Schlüssel:") +print("S:\n{}x + {}".format(private_key.S.M, private_key.S.y)) +print("Pr:\n{}".format(private_key.Pr)) +print("S:\n{}x + {}".format(private_key.T.M, private_key.T.y)) + +print("\nÖffentlicher Schlüssel:") +print("P:\n{}".format(public_key.P)) + +# Die Nachricht soll (1, 4) sein msg = vector(finite_field, [1, 4]) +print("\n\nmsg =", msg) -# generate Signature +# Signatur erstellen sig = UOV.sign(msg) - -# verify signature is valid -print("msg =", msg) print("sig =", sig) -print("UOV.verify(msg, sig):", UOV.verify(msg, sig)) + +# Signatur verifizieren +print("public_key.verify(msg, sig):", public_key.verify(msg, sig)) diff --git a/classes/AffineTransformation.py b/classes/AffineTransformation.py new file mode 100644 index 0000000000000000000000000000000000000000..ca63c101b7e306bd3d8d90fa89773d87e2b1d5f9 --- /dev/null +++ b/classes/AffineTransformation.py @@ -0,0 +1,35 @@ +from __future__ import absolute_import, division, print_function +from sage.all import * + + +class AffineTransformation(): + def __init__(self, M, y): + """ + Eine affine Transformation wird mit einer invertierbaren + Matrix und einem beliebigen Vektor initialisiert + """ + + # Abbruchbedingung für nicht invertierbare Matrizen + if not M.is_invertible(): + exit(1) + + self.M = M # invertierbare Matrix + self.y = y # Vektor + + def __call__(self, vector): + """Anwenden der Transformation auf den gegebenen Vektor""" + + # Sollte `vector` eine Liste sein -> Typecast + if not isinstance(vector, sage.structure.element.Vector): + vector = vector(vector) + + return (self.M * vector) + self.y + + def inverse(self, vector): + """Anwenden der inversen Transformation auf den gegebenen Vektor""" + + # Sollte `vector` eine Liste sein -> Typecast + if not isinstance(vector, sage.structure.element.Vector): + vector = vector(vector) + + return self.M.inverse() * (vector - self.y) diff --git a/classes/MatsumotoImaiAExample.py b/classes/MatsumotoImaiAExample.py new file mode 100644 index 0000000000000000000000000000000000000000..228e8252ade232ac8d954ffc335d1a71086715a3 --- /dev/null +++ b/classes/MatsumotoImaiAExample.py @@ -0,0 +1,79 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, print_function +from sage.all import * +import copy as cp + +from .helpers.sage_extensions import random_value, random_invertible_matrix + +from .helpers.AffineTransformation import AffineTransformation +from .helpers.PrivateKey import PrivateKey +from .helpers.PrivateKey import PublicKey +from .helpers.EncryptionScheme import EncryptionScheme + + +class MatsumotoImaiAExample(EncryptionScheme): + """Matsumoto Imai Scheme A implementation""" + + def phi(self, polynomail_a): + return polynomail_a.list() + + def phi_inv(self, vector_a): # -> b::Polynomial + 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() + self.generate_example_keys() + + def generate_example_keys(self): + k = self.finite_field + (a,) = k.gens() + n = 3 + + ring = PolynomialRing(k, 'x') + (x,) = ring.gens() + gx = x**3 + x + 1 + + (self.theta, self.theta_invers) = (2, 26) + + ML1 = matrix(k, [[a**2, a, a], [a, 1, 0], [1, 0, 1]]) + yL1 = vector(k, [0, 1, a]) + T = AffineTransformation(ML1, yL1) + + ML2 = matrix(k, [[1, 0, a], [0, 1, a], [1, a, 0]]) + yL2 = vector(k, [a, a**2, a**2]) + S = AffineTransformation(ML2, yL2) + + multivariate_ring = PolynomialRing(k, 'x1, x2, x3', 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_inv(Sx) # <- phi_invers + + # Now the MAGIC happens + # post_F = self.F(pre_F) + + post_F = extension_field("1 + (a + 1)*x1 + (a)*x2 + x3 + x1*x2 + (a)*x1*x3 + (a + 1)*x2*x3 + ((a) + (a)*x1 + x2 + (a + 1)*x3 + x1^2 + (a + 1)*x1*x2 + x2^2 + x2*x3)*T + ((a + 1) + (a + 1)*x1 + (a)*x2 + (a)*x3 + x1^2 + x1*x2 + (a)*x1*x3 + (a + 1)*x2^2 + (a)*x2*x3 + (a + 1)*x3^2)*T^2") + + SPTx = T(vector(post_F.list())) + + Pr = T.inverse(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)) + return vector(self.phi(X**self.theta_invers)) diff --git a/classes/UnbalancedOilAndVinegarExample.py b/classes/UnbalancedOilAndVinegarExample.py new file mode 100644 index 0000000000000000000000000000000000000000..a3d38a96becf9fedec131e5f5df82841c52fdad6 --- /dev/null +++ b/classes/UnbalancedOilAndVinegarExample.py @@ -0,0 +1,151 @@ +#!/usr/bin/sage +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, print_function +from sage.all import * +import copy as cp + +from .helpers.sage_extensions import random_value, random_invertible_matrix + +from .helpers.AffineTransformation import AffineTransformation +from .helpers.PrivateKey import PrivateKey +from .helpers.SignatureScheme import SignatureScheme + + +class UnbalancedOilAndVinegarExample(SignatureScheme): + """Unalanced Oil and Vinegar Implementation""" + + 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 + + Der Pfad zum privaten Schlüssel ist optional + """ + + # speichere die initialen Werte + self.finite_field = finite_field + self.ring = PolynomialRing(finite_field, 'x', n) + + self.n = n # Anzahl der Variablen + self.m = m # Anzahl der Gleichungen + + # wird ein privater Schlüssel als Dateipfad übergeben, so wird dieser + # geladen ansonsten werden mit `generate_keys` die Schlüssel erstellt + 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(finite_field, n, m) + + def generate_keys(self, F, n, m): + """ + erstelle Privaten und öffentlichen Schlüssel aus gegebenem endlichen + Körper `F`, Anzahl der Variablen `n` und Anzahl der Gleichungen `m` + """ + + # oil and vinegar variables: + v = n - m + oil = m + + # Zwei affine Abbildungen (T ist Identität) + # - S ist (n x n) und T ist (m x m) + S = AffineTransformation( + random_invertible_matrix(F, n), random_vector(F, n)) + T = AffineTransformation(matrix.identity(F, m), vector(F, m)) + + # Generiere geheimes Polynom `Pr` + # - P ist (n x m) + alpha = [] + beta = [] + gamma = [] + Pr = [] + + # x[0] .. x[n-1] als Variablen benutzbar machen + x = vector(self.ring.gens()) + + # für jedes Polynom Pr[i] + for i in xrange(0, oil): + # Zufällige Matrix Representation jedes Polynoms + # - Alpha (Wert) ist der Konstante Anteil + # - Beta (Vektor) ist der lineare Anteil + # - Gamma (Matrix) ist der gemischte und quadratische Anteil + alpha.append(random_value(F)) + beta.append(random_vector(F, n)) + gamma.append(random_matrix(F, n - m, n)) + + '''Baue Polynom folgender Form''' + # poly = ((gamma[i] * x) * x) + (beta * x) + alpha[i] + + # Konstanter Anteil + poly = alpha[i] + + # Essigvariablen: + for j in xrange(0, v): + for k in xrange(0, n): + poly += gamma[i][j, k] * x[j] * x[k] + + # Essig- & Ölvariablen + for k in xrange(0, n): + poly += beta[i][k] * x[k] + + Pr.append(poly) + + # erstelle den geheimen und öffentlichen Schlüssel + self.private_key = PrivateKey(S, Pr, T) + self.public_key = self.private_key.generate_public_key() + + def invert_MQ(self, msg): + vinegar_len = self.n - self.m + x = list(self.ring.gens()) + F = self.finite_field + + # wichtig ist, dass private_key.Pr `UOV`-Gestalt hat + Pr = cp.copy(self.private_key.Pr) + + sig = [] + + """ Linearisierung """ + # x[1] bis x[n-m] werden zufällig aus F gewählt + # Diese bilden den ersten Teil der Lösung + if vinegar_len == 1: + x[0] = 3 + sig.append(x[0]) + else: + for i in xrange(0, vinegar_len): + x[i] = random_value(F) + sig.append(x[i]) + + print("Zufällig belegte Werte", x) + + # Durch einsetzen der belegten Variablen wird P linearisiert + for i in xrange(0, len(Pr)): + Pr[i] = Pr[i](x) + + # Die gelösten Variablen werden entfernt + x = x[vinegar_len:] + + """ Gleichungssystem lösen """ + # Um mit sagemath das lineare Gleichungssystem zu loesen + # wird diese Form angestrebt: A*x - b = 0 + A = matrix(F, len(x)) # Koeffizenten Matrix + b = vector(F, len(x)) + + # A[i] ist die Liste der Koeffizienten der neuen Monome von Pr[i] + # b[i] ist der konstante Anteil von Pr[i] + for i in xrange(0, len(x)): + for j in xrange(0, len(x)): + A[i, j] = Pr[i].monomial_coefficient(x[j]) + + b[i] = Pr[i].constant_coefficient() + + # Die Nachricht muss von den Konstanten abgezogen werden + 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 "Pr(sig) = msg" wird zurückgegeben + return vector(F, sig) diff --git a/keys/bsp_uov.priv b/keys/bsp_uov.priv index b58b64b7dff26ab5dfef0d56e179a060c63bb4f9..9888565e6ce5c97005eb2b57c3a08dfa7b77ac05 100644 Binary files a/keys/bsp_uov.priv and b/keys/bsp_uov.priv differ diff --git a/keys/bsp_uov2.priv b/keys/bsp_uov2.priv deleted file mode 100644 index 9888565e6ce5c97005eb2b57c3a08dfa7b77ac05..0000000000000000000000000000000000000000 Binary files a/keys/bsp_uov2.priv and /dev/null differ diff --git a/keys/bsp_uov_old.priv b/keys/bsp_uov_old.priv new file mode 100644 index 0000000000000000000000000000000000000000..b58b64b7dff26ab5dfef0d56e179a060c63bb4f9 Binary files /dev/null and b/keys/bsp_uov_old.priv differ