From 0564924d1ff1e08d86a5755206c97d3dac7c8acd Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Wed, 23 Mar 2022 15:33:04 +0300 Subject: [PATCH] Streaming 34.11-2012 --- news.texi | 5 ++++ pygost/__init__.py | 2 +- pygost/gost34112012.py | 52 +++++++++++++++++++++++++++--------------- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/news.texi b/news.texi index eeacd55..ed7795d 100644 --- a/news.texi +++ b/news.texi @@ -3,6 +3,11 @@ @table @strong +@anchor{Release 5.11} +@item 5.11 +@code{gost34112012}'s @code{update()}/@code{digest()} methods are +streaming now -- they do not store the whole data in memory. + @anchor{Release 5.10} @item 5.10 Added ISO 10126 @code{pygost.gost3413.(un)pad_iso10126} padding support. diff --git a/pygost/__init__.py b/pygost/__init__.py index 38d8dbe..54bee02 100644 --- a/pygost/__init__.py +++ b/pygost/__init__.py @@ -3,4 +3,4 @@ PyGOST is free software: see the file COPYING for copying conditions. """ -__version__ = "5.10" +__version__ = "5.11" diff --git a/pygost/gost34112012.py b/pygost/gost34112012.py index f680eee..f1a7acc 100644 --- a/pygost/gost34112012.py +++ b/pygost/gost34112012.py @@ -245,44 +245,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("