From 19c99c04d18558ec0f2a0c56c95d49e4a92aca04 Mon Sep 17 00:00:00 2001 From: Maximilian Stauss <max.stauss@gmail.com> Date: Sat, 2 Sep 2017 16:54:38 +0200 Subject: [PATCH] add UOV --- classes/UnbalancedOilAndVinegar.py | 141 +++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 classes/UnbalancedOilAndVinegar.py diff --git a/classes/UnbalancedOilAndVinegar.py b/classes/UnbalancedOilAndVinegar.py new file mode 100644 index 0000000..a243b9e --- /dev/null +++ b/classes/UnbalancedOilAndVinegar.py @@ -0,0 +1,141 @@ +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 UnbalancedOilAndVinegar(SignatureScheme): + """Unalanced Oil and Vinegar Implementation""" + + def __init__(self, finite_field, ring, 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.P) + + sig = [] + + """ Linearisierung """ + # x[1] bis x[n-m] werden zufällig aus F gewählt + # Diese bilden den ersten Teil der Lösung + for i in xrange(0, vinegar_len): + x[i] = random_value(F) + sig.append(x[i]) + + # Durch einsetzen der belegten Variablen wird P linearisiert + for i in xrange(0, len(P)): + 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 + for x in A.solve_right(b): + sig.append(x) + + # Die Lösung von "P(sig) = msg" wird zurückgegeben + return vector(F, sig) -- GitLab