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