summaryrefslogtreecommitdiff
path: root/pygost/gost28147_mac.py
blob: 64712c6da5090e5d9e9cf86e9c26a82fcec92459 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# coding: utf-8
# PyGOST -- Pure Python GOST cryptographic functions library
# Copyright (C) 2015-2020 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)