100 lines
3.0 KiB
Python
100 lines
3.0 KiB
Python
# coding: utf-8
|
|
# PyGOST -- Pure Python GOST cryptographic functions library
|
|
# Copyright (C) 2015-2023 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, 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
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# 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 28147-89 MAC
|
|
"""
|
|
|
|
from copy import copy
|
|
|
|
from pygost.gost28147 import block2ns
|
|
from pygost.gost28147 import BLOCKSIZE
|
|
from pygost.gost28147 import DEFAULT_SBOX
|
|
from pygost.gost28147 import ns2block
|
|
from pygost.gost28147 import validate_iv
|
|
from pygost.gost28147 import validate_key
|
|
from pygost.gost28147 import validate_sbox
|
|
from pygost.gost28147 import xcrypt
|
|
from pygost.gost3413 import pad1
|
|
from pygost.iface import PEP247
|
|
from pygost.utils import strxor
|
|
from pygost.utils import xrange
|
|
|
|
digest_size = 8
|
|
SEQ_MAC = (
|
|
0, 1, 2, 3, 4, 5, 6, 7,
|
|
0, 1, 2, 3, 4, 5, 6, 7,
|
|
)
|
|
|
|
|
|
class MAC(PEP247):
|
|
"""GOST 28147-89 MAC mode of operation
|
|
|
|
>>> m = MAC(key=key)
|
|
>>> m.update("some data")
|
|
>>> m.update("another data")
|
|
>>> m.hexdigest()[:8]
|
|
'a687a08b'
|
|
"""
|
|
digest_size = digest_size
|
|
|
|
def __init__(self, key, data=b"", iv=8 * b"\x00", sbox=DEFAULT_SBOX):
|
|
"""
|
|
:param key: authentication key
|
|
:type key: bytes, 32 bytes
|
|
:param iv: initialization vector
|
|
:type iv: bytes, BLOCKSIZE length
|
|
:param sbox: S-box parameters to use
|
|
:type sbox: str, SBOXES'es key
|
|
"""
|
|
validate_key(key)
|
|
validate_iv(iv)
|
|
validate_sbox(sbox)
|
|
self.key = key
|
|
self.data = data
|
|
self.iv = iv
|
|
self.sbox = sbox
|
|
|
|
def copy(self):
|
|
return MAC(self.key, copy(self.data), self.iv, self.sbox)
|
|
|
|
def update(self, data):
|
|
"""Append data that has to be authenticated
|
|
"""
|
|
self.data += data
|
|
|
|
def digest(self):
|
|
"""Get MAC tag of supplied data
|
|
|
|
You have to provide at least single byte of data.
|
|
If you want to produce tag length of 3 bytes, then
|
|
``digest()[:3]``.
|
|
"""
|
|
if not self.data:
|
|
raise ValueError("No data processed")
|
|
data = pad1(self.data, BLOCKSIZE)
|
|
prev = block2ns(self.iv)[::-1]
|
|
for i in xrange(0, len(data), BLOCKSIZE):
|
|
prev = xcrypt(
|
|
SEQ_MAC, self.sbox, self.key, block2ns(strxor(
|
|
data[i:i + BLOCKSIZE],
|
|
ns2block(prev),
|
|
)),
|
|
)[::-1]
|
|
return ns2block(prev)
|
|
|
|
|
|
def new(key, data=b"", iv=8 * b"\x00", sbox=DEFAULT_SBOX):
|
|
return MAC(key, data, iv, sbox)
|