]> Cypherpunks.ru repositories - pygost.git/blobdiff - pygost/gost34112012.py
Simpler and slightly faster 512-bit-addition code
[pygost.git] / pygost / gost34112012.py
index de4ef2e07863cc165d14a1f5884ba22831a795e7..48d7ae2e7bb22185804ab63e0c201d1f7f22a6e0 100644 (file)
@@ -1,11 +1,10 @@
 # coding: utf-8
 # PyGOST -- Pure Python GOST cryptographic functions library
-# Copyright (C) 2015-2016 Sergey Matveev <stargrave@stargrave.org>
+# Copyright (C) 2015-2022 Sergey Matveev <stargrave@stargrave.org>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# the Free Software Foundation, version 3 of the License.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -14,7 +13,7 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
-""" GOST R 34.11-2012 (Streebog) hash function common files
+"""GOST R 34.11-2012 (Streebog) hash function common files
 
 This is implementation of :rfc:`6986`. Most function and variable names are
 taken according to specification's terminology.
@@ -26,7 +25,6 @@ from struct import unpack
 
 from pygost.iface import PEP247
 from pygost.utils import hexdec
-from pygost.utils import hexenc
 from pygost.utils import strxor
 from pygost.utils import xrange
 
@@ -60,22 +58,22 @@ Pi = bytearray((
 ))
 
 A = [unpack(">Q", hexdec(s))[0] for s in (
-   "8e20faa72ba0b470", "47107ddd9b505a38", "ad08b0e0c3282d1c", "d8045870ef14980e",
-   "6c022c38f90a4c07", "3601161cf205268d", "1b8e0b0e798c13c8", "83478b07b2468764",
-   "a011d380818e8f40", "5086e740ce47c920", "2843fd2067adea10", "14aff010bdd87508",
-   "0ad97808d06cb404", "05e23c0468365a02", "8c711e02341b2d01", "46b60f011a83988e",
-   "90dab52a387ae76f", "486dd4151c3dfdb9", "24b86a840e90f0d2", "125c354207487869",
-   "092e94218d243cba", "8a174a9ec8121e5d", "4585254f64090fa0", "accc9ca9328a8950",
-   "9d4df05d5f661451", "c0a878a0a1330aa6", "60543c50de970553", "302a1e286fc58ca7",
-   "18150f14b9ec46dd", "0c84890ad27623e0", "0642ca05693b9f70", "0321658cba93c138",
-   "86275df09ce8aaa8", "439da0784e745554", "afc0503c273aa42a", "d960281e9d1d5215",
-   "e230140fc0802984", "71180a8960409a42", "b60c05ca30204d21", "5b068c651810a89e",
-   "456c34887a3805b9", "ac361a443d1c8cd2", "561b0d22900e4669", "2b838811480723ba",
-   "9bcf4486248d9f5d", "c3e9224312c8c1a0", "effa11af0964ee50", "f97d86d98a327728",
-   "e4fa2054a80b329c", "727d102a548b194e", "39b008152acb8227", "9258048415eb419d",
-   "492c024284fbaec0", "aa16012142f35760", "550b8e9e21f7a530", "a48b474f9ef5dc18",
-   "70a6a56e2440598e", "3853dc371220a247", "1ca76e95091051ad", "0edd37c48a08a6d8",
-   "07e095624504536c", "8d70c431ac02a736", "c83862965601dd1b", "641c314b2b8ee083",
+    "8e20faa72ba0b470", "47107ddd9b505a38", "ad08b0e0c3282d1c", "d8045870ef14980e",
+    "6c022c38f90a4c07", "3601161cf205268d", "1b8e0b0e798c13c8", "83478b07b2468764",
+    "a011d380818e8f40", "5086e740ce47c920", "2843fd2067adea10", "14aff010bdd87508",
+    "0ad97808d06cb404", "05e23c0468365a02", "8c711e02341b2d01", "46b60f011a83988e",
+    "90dab52a387ae76f", "486dd4151c3dfdb9", "24b86a840e90f0d2", "125c354207487869",
+    "092e94218d243cba", "8a174a9ec8121e5d", "4585254f64090fa0", "accc9ca9328a8950",
+    "9d4df05d5f661451", "c0a878a0a1330aa6", "60543c50de970553", "302a1e286fc58ca7",
+    "18150f14b9ec46dd", "0c84890ad27623e0", "0642ca05693b9f70", "0321658cba93c138",
+    "86275df09ce8aaa8", "439da0784e745554", "afc0503c273aa42a", "d960281e9d1d5215",
+    "e230140fc0802984", "71180a8960409a42", "b60c05ca30204d21", "5b068c651810a89e",
+    "456c34887a3805b9", "ac361a443d1c8cd2", "561b0d22900e4669", "2b838811480723ba",
+    "9bcf4486248d9f5d", "c3e9224312c8c1a0", "effa11af0964ee50", "f97d86d98a327728",
+    "e4fa2054a80b329c", "727d102a548b194e", "39b008152acb8227", "9258048415eb419d",
+    "492c024284fbaec0", "aa16012142f35760", "550b8e9e21f7a530", "a48b474f9ef5dc18",
+    "70a6a56e2440598e", "3853dc371220a247", "1ca76e95091051ad", "0edd37c48a08a6d8",
+    "07e095624504536c", "8d70c431ac02a736", "c83862965601dd1b", "641c314b2b8ee083",
 )]
 
 Tau = (
@@ -165,17 +163,31 @@ C = [hexdec("".join(s))[::-1] for s in (
 )]
 
 
+def _lcache():
+    cache = []
+    for byteN in xrange(8):
+        cache.append([0 for _ in xrange(256)])
+    for byteN in xrange(8):
+        for byteVal in xrange(256):
+            res64 = 0
+            val = byteVal
+            for bitN in xrange(8):
+                if val & 0x80 > 0:
+                    res64 ^= A[(7 - byteN) * 8 + bitN]
+                val <<= 1
+            cache[byteN][byteVal] = res64
+    return cache
+
+
+# Trade memory for CPU for part of L() calculations
+LCache = _lcache()
+
+
 def add512bit(a, b):
-    """ Add two 512 integers
-    """
-    a = bytearray(a)
-    b = bytearray(b)
-    cb = 0
-    res = bytearray(64)
-    for i in range(64):
-        cb = a[i] + b[i] + (cb >> 8)
-        res[i] = cb & 0xff
-    return res
+    a = int.from_bytes(a, "little")
+    b = int.from_bytes(b, "little")
+    r = (a + b) % (1 << 512)
+    return r.to_bytes(512 // 8, "little")
 
 
 def g(n, hsh, msg):
@@ -204,18 +216,15 @@ def PS(data):
 def L(data):
     res = []
     for i in range(8):
-        val = unpack("<Q", data[i * 8:i * 8 + 8])[0]
         res64 = 0
-        for j in range(BLOCKSIZE):
-            if val & 0x8000000000000000:
-                res64 ^= A[j]
-            val <<= 1
+        for j in range(8):
+            res64 ^= LCache[j][data[8 * i + j]]
         res.append(pack("<Q", res64))
     return b"".join(res)
 
 
 class GOST34112012(PEP247):
-    """ GOST 34.11-2012 big-endian hash
+    """GOST 34.11-2012 big-endian hash
 
     >>> m = GOST34112012(digest_size=32)
     >>> m.update("foo")
@@ -230,45 +239,60 @@ class GOST34112012(PEP247):
         :param digest_size: hash digest size to compute
         :type digest_size: 32 or 64 bytes
         """
-        self.data = data
         self._digest_size = digest_size
+        self.hsh = BLOCKSIZE * (b"\x01" if digest_size == 32 else b"\x00")
+        self.chk = bytearray(BLOCKSIZE * b"\x00")
+        self.n = 0
+        self.buf = b""
+        self.update(data)
 
     def copy(self):
-        return GOST34112012(copy(self.data), self.digest_size)
+        obj = GOST34112012()
+        obj._digest_size = self._digest_size
+        obj.hsh = self.hsh
+        obj.chk = copy(self.chk)
+        obj.n = self.n
+        obj.buf = self.buf
+        return obj
 
     @property
     def digest_size(self):
         return self._digest_size
 
+    def _update_block(self, block):
+        self.hsh = g(self.n, self.hsh, block)
+        self.chk = add512bit(self.chk, block)
+        self.n += 512
+
     def update(self, data):
-        """ Append data that has to be hashed
+        """Update state with the new data
         """
-        self.data += data
+        if len(self.buf) > 0:
+            self.buf += data[:BLOCKSIZE - len(self.buf)]
+            data = data[BLOCKSIZE - len(self.buf):]
+            if len(self.buf) == BLOCKSIZE:
+                self._update_block(self.buf)
+                self.buf = b""
+        while len(data) >= BLOCKSIZE:
+            self._update_block(data[:BLOCKSIZE])
+            data = data[BLOCKSIZE:]
+        self.buf += data
 
     def digest(self):
-        """ Get hash of the provided data
+        """Get hash of the provided data
         """
-        hsh = BLOCKSIZE * (b"\x01" if self.digest_size == 32 else b"\x00")
-        chk = bytearray(BLOCKSIZE * b"\x00")
-        n = 0
-        data = self.data
-        for i in xrange(0, len(data) // BLOCKSIZE * BLOCKSIZE, BLOCKSIZE):
-            block = data[i:i + BLOCKSIZE]
-            hsh = g(n, hsh, block)
-            chk = add512bit(chk, block)
-            n += 512
+        data = self.buf
 
         # Padding
-        padblock_size = len(data) * 8 - n
+        padblock_size = len(data) * 8
         data += b"\x01"
-        padlen = BLOCKSIZE - len(data) % BLOCKSIZE
+        padlen = BLOCKSIZE - len(data)
         if padlen != BLOCKSIZE:
             data += b"\x00" * padlen
 
-        hsh = g(n, hsh, data[-BLOCKSIZE:])
-        n += padblock_size
-        chk = add512bit(chk, data[-BLOCKSIZE:])
+        hsh = g(self.n, self.hsh, data)
+        n = self.n + padblock_size
+        chk = add512bit(self.chk, data)
         hsh = g(0, hsh, pack("<Q", n) + 56 * b"\x00")
         hsh = g(0, hsh, chk)
         return hsh[-self._digest_size:]
-