X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=pygost%2Fgost34112012.py;h=48d7ae2e7bb22185804ab63e0c201d1f7f22a6e0;hb=d7cf18d77f96e9a69cde610523e7a84efc8bc76a;hp=bdabcf388fc5a91e25ff57c900ce97a89e235af3;hpb=dacca17a69ca9a920bf69bc1dc47386bdd6d4def;p=pygost.git diff --git a/pygost/gost34112012.py b/pygost/gost34112012.py index bdabcf3..48d7ae2 100644 --- a/pygost/gost34112012.py +++ b/pygost/gost34112012.py @@ -1,11 +1,10 @@ # coding: utf-8 # PyGOST -- Pure Python GOST cryptographic functions library -# Copyright (C) 2015-2017 Sergey Matveev +# Copyright (C) 2015-2022 Sergey Matveev # # 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 . -""" 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. @@ -27,11 +26,10 @@ from struct import unpack from pygost.iface import PEP247 from pygost.utils import hexdec from pygost.utils import strxor -from pygost.utils import xrange # pylint: disable=redefined-builtin +from pygost.utils import xrange BLOCKSIZE = 64 -# pylint: disable=bad-whitespace,bad-continuation Pi = bytearray(( 252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, 218, 35, 197, 4, 77, 233, 119, 240, 219, 147, 46, @@ -88,7 +86,6 @@ Tau = ( 6, 14, 22, 30, 38, 46, 54, 62, 7, 15, 23, 31, 39, 47, 55, 63, ) -# pylint: disable=bad-whitespace,bad-continuation C = [hexdec("".join(s))[::-1] for s in ( ( @@ -166,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): @@ -205,18 +216,15 @@ def PS(data): def L(data): res = [] for i in range(8): - val = unpack(">> m = GOST34112012(digest_size=32) >>> m.update("foo") @@ -231,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 + """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("