]> Cypherpunks.ru repositories - pygost.git/blobdiff - pygost/gost34112012.py
Simpler and slightly faster 512-bit-addition code
[pygost.git] / pygost / gost34112012.py
index f680eeeac0d83cd69fc1564f2ef655302a655212..48d7ae2e7bb22185804ab63e0c201d1f7f22a6e0 100644 (file)
@@ -184,16 +184,10 @@ 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):
@@ -245,44 +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
         """
-        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:]