]> Cypherpunks.ru repositories - pygost.git/blobdiff - pygost/gost34112012.py
3x faster 34.11-2012
[pygost.git] / pygost / gost34112012.py
index df4777fce69efd277ad942ef9a7b645595019ceb..2e43fe1cb6c04e3c2d80189948b55aea400b67f9 100644 (file)
@@ -1,6 +1,6 @@
 # coding: utf-8
 # PyGOST -- Pure Python GOST cryptographic functions library
-# Copyright (C) 2015-2020 Sergey Matveev <stargrave@stargrave.org>
+# Copyright (C) 2015-2021 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
@@ -13,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.
@@ -162,9 +162,31 @@ C = [hexdec("".join(s))[::-1] for s in (
     ),
 )]
 
+# Trade memory for CPU for part of L() calculations
+_Cache = None
+
+
+def _cache_ensure():
+    global _Cache
+    if _Cache is not None:
+        return
+    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
+    _Cache = cache
+
 
 def add512bit(a, b):
-    """ Add two 512 integers
+    """Add two 512 integers
     """
     a = bytearray(a)
     b = bytearray(b)
@@ -200,20 +222,18 @@ def PS(data):
 
 
 def L(data):
+    _cache_ensure()
     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 ^= _Cache[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")
@@ -239,12 +259,12 @@ class GOST34112012(PEP247):
         return self._digest_size
 
     def update(self, data):
-        """ Append data that has to be hashed
+        """Append data that has to be hashed
         """
         self.data += 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")