]> Cypherpunks.ru repositories - pygost.git/commitdiff
CTR-ACPKM, OMAC-ACPKM-Master modes of operation
authorSergey Matveev <stargrave@stargrave.org>
Tue, 28 Jul 2020 15:45:42 +0000 (18:45 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Thu, 30 Jul 2020 15:31:43 +0000 (18:31 +0300)
README
VERSION
install.texi
news.texi
pygost/__init__.py
pygost/gost3412.py
pygost/gost3413.py
pygost/stubs/pygost/gost3412.pyi
pygost/stubs/pygost/gost3413.pyi
pygost/test_gost3413.py
www.texi

diff --git a/README b/README
index 24b28082b2cb63276fdb07e47b78c343c062335a..7fde53f0942259199c043b7383eaa02a7d2fff53 100644 (file)
--- a/README
+++ b/README
@@ -23,7 +23,8 @@ GOST is GOvernment STandard of Russian Federation (and Soviet Union).
 * GOST R 34.12-2015 64-bit block cipher Магма (Magma)
 * GOST R 34.13-2015 padding methods and block cipher modes of operation
   (ECB, CTR, OFB, CBC, CFB, MAC)
-* MGM AEAD mode for 64 and 128 bit ciphers
+* MGM AEAD mode for 64 and 128 bit ciphers (Р 1323565.1.026–2019)
+* CTR-ACPKM, OMAC-ACPKM-Master modes of operation (Р 1323565.1.017-2018)
 * PEP247-compatible hash/MAC functions
 
 Known problems: low performance and non time-constant calculations.
diff --git a/VERSION b/VERSION
index ef216a53f54ef766b2415c07b489089de9c3914f..86a9588adcd59c02ae1aa9f450f2afb4fbb0006d 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.8
+4.9
index 737e6361083d27ad7b949c92feb82b9992fe081c..a3979132ba8c399a2bfcc70d32c7a02c92a56262 100644 (file)
@@ -1,7 +1,7 @@
 @node Download
 @unnumbered Download
 
-@set VERSION 4.8
+@set VERSION 4.9
 
 No additional dependencies except Python 2.7/3.x interpreter are required.
 
index cccd4f3601c2e72f78d968967cecf7de8297b023..d1c27b6266b3bf3fe0a16aa58de6e3132904e33e 100644 (file)
--- a/news.texi
+++ b/news.texi
@@ -3,6 +3,13 @@
 
 @table @strong
 
+@anchor{Release 4.9}
+@item 4.9
+    @itemize
+    @item CTR-ACPKM mode of operation
+    @item OMAC-ACPKM-Master moder of operation
+    @end itemize
+
 @anchor{Release 4.8}
 @item 4.8
 MGM AEAD mode for 64 and 128 bit ciphers.
index 488683d9899eb82efeed3e345c0c55317d3061cc..c2d5ea8ebbc1af588d590aec82abd58c3cd7ab37 100644 (file)
@@ -3,4 +3,4 @@
 PyGOST is free software: see the file COPYING for copying conditions.
 """
 
-__version__ = "4.8"
+__version__ = "4.9"
index 450cc7af80495d2eb5f23a101fce695569bc8444..296e4fbea72a794ca625c71663ce38b223b0d9e6 100644 (file)
@@ -26,6 +26,8 @@ from pygost.utils import strxor
 from pygost.utils import xrange
 
 
+KEY_SIZE = 32
+
 LC = bytearray((
     148, 32, 133, 16, 194, 192, 1, 251, 1, 192, 194, 16, 133, 32, 148, 1,
 ))
@@ -123,6 +125,8 @@ def lp(blk):
 class GOST3412Kuznechik(object):
     """GOST 34.12-2015 128-bit block cipher Кузнечик (Kuznechik)
     """
+    blocksize = 16
+
     def __init__(self, key):
         """
         :param key: encryption/decryption key
@@ -156,6 +160,8 @@ class GOST3412Kuznechik(object):
 class GOST3412Magma(object):
     """GOST 34.12-2015 64-bit block cipher Магма (Magma)
     """
+    blocksize = 8
+
     def __init__(self, key):
         """
         :param key: encryption/decryption key
index 3a539d2e4b1f9097e78774ea3cf263df648a9d5a..495a3f9e343826ed430616f88b63c4ead6ca5b60 100644 (file)
@@ -24,6 +24,9 @@ from pygost.utils import strxor
 from pygost.utils import xrange
 
 
+KEY_SIZE = 32
+
+
 def pad_size(data_size, blocksize):
     """Calculate required pad size to full up blocksize
     """
@@ -75,7 +78,7 @@ def ecb_encrypt(encrypter, bs, pt):
     """ECB encryption mode of operation
 
     :param encrypter: encrypting function, that takes block as an input
-    :param int bs: cipher's blocksize
+    :param int bs: cipher's blocksize, bytes
     :param bytes pt: already padded plaintext
     """
     if not pt or len(pt) % bs != 0:
@@ -90,7 +93,7 @@ def ecb_decrypt(decrypter, bs, ct):
     """ECB decryption mode of operation
 
     :param decrypter: Decrypting function, that takes block as an input
-    :param int bs: cipher's blocksize
+    :param int bs: cipher's blocksize, bytes
     :param bytes ct: ciphertext
     """
     if not ct or len(ct) % bs != 0:
@@ -101,11 +104,23 @@ def ecb_decrypt(decrypter, bs, ct):
     return b"".join(pt)
 
 
-def ctr(encrypter, bs, data, iv):
+def acpkm(encrypter, bs):
+    """Perform ACPKM key derivation
+
+    :param encrypter: encrypting function, that takes block as an input
+    :param int bs: cipher's blocksize, bytes
+    """
+    return b"".join([
+        encrypter(bytes(bytearray(range(d, d + bs))))
+        for d in range(0x80, 0x80 + bs * (KEY_SIZE // bs), bs)
+    ])
+
+
+def ctr(encrypter, bs, data, iv, _acpkm=None):
     """Counter mode of operation
 
     :param encrypter: encrypting function, that takes block as an input
-    :param int bs: cipher's blocksize
+    :param int bs: cipher's blocksize, bytes
     :param bytes data: plaintext/ciphertext
     :param bytes iv: half blocksize-sized initialization vector
 
@@ -113,19 +128,48 @@ def ctr(encrypter, bs, data, iv):
     """
     if len(iv) != bs // 2:
         raise ValueError("Invalid IV size")
+    if len(data) > bs * (1 << (8 * (bs // 2 - 1))):
+        raise ValueError("Too big data")
     stream = []
     ctr_value = 0
+    ctr_max_value = 1 << (8 * (bs // 2))
+    if _acpkm is not None:
+        acpkm_algo_class, acpkm_section_size_in_bs = _acpkm
+        acpkm_section_size_in_bs //= bs
     for _ in xrange(0, len(data) + pad_size(len(data), bs), bs):
+        if (
+                _acpkm is not None and
+                ctr_value != 0 and
+                ctr_value % acpkm_section_size_in_bs == 0
+        ):
+            encrypter = acpkm_algo_class(acpkm(encrypter, bs)).encrypt
         stream.append(encrypter(iv + long2bytes(ctr_value, bs // 2)))
-        ctr_value += 1
+        ctr_value = (ctr_value + 1) % ctr_max_value
     return strxor(b"".join(stream), data)
 
 
+def ctr_acpkm(algo_class, encrypter, section_size, bs, data, iv):
+    """CTR-ACPKM mode of operation
+
+    :param algo_class: pygost.gost3412's algorithm class
+    :param encrypter: encrypting function, that takes block as an input
+    :param int section_size: ACPKM'es section size (N), in bytes
+    :param int bs: cipher's blocksize, bytes
+    :param bytes data: plaintext/ciphertext
+    :param bytes iv: half blocksize-sized initialization vector
+
+    For decryption you use the same function again.
+    """
+    if section_size % bs != 0:
+        raise ValueError("section_size must be multiple of bs")
+    return ctr(encrypter, bs, data, iv, _acpkm=(algo_class, section_size))
+
+
 def ofb(encrypter, bs, data, iv):
     """OFB mode of operation
 
     :param encrypter: encrypting function, that takes block as an input
-    :param int bs: cipher's blocksize
+    :param int bs: cipher's blocksize, bytes
     :param bytes data: plaintext/ciphertext
     :param bytes iv: blocksize-sized initialization vector
 
@@ -145,7 +189,7 @@ def cbc_encrypt(encrypter, bs, pt, iv):
     """CBC encryption mode of operation
 
     :param encrypter: encrypting function, that takes block as an input
-    :param int bs: cipher's blocksize
+    :param int bs: cipher's blocksize, bytes
     :param bytes pt: already padded plaintext
     :param bytes iv: blocksize-sized initialization vector
     """
@@ -165,7 +209,7 @@ def cbc_decrypt(decrypter, bs, ct, iv):
     """CBC decryption mode of operation
 
     :param decrypter: Decrypting function, that takes block as an input
-    :param int bs: cipher's blocksize
+    :param int bs: cipher's blocksize, bytes
     :param bytes ct: ciphertext
     :param bytes iv: blocksize-sized initialization vector
     """
@@ -186,7 +230,7 @@ def cfb_encrypt(encrypter, bs, pt, iv):
     """CFB encryption mode of operation
 
     :param encrypter: encrypting function, that takes block as an input
-    :param int bs: cipher's blocksize
+    :param int bs: cipher's blocksize, bytes
     :param bytes pt: plaintext
     :param bytes iv: blocksize-sized initialization vector
     """
@@ -204,7 +248,7 @@ def cfb_decrypt(encrypter, bs, ct, iv):
     """CFB decryption mode of operation
 
     :param encrypter: encrypting function, that takes block as an input
-    :param int bs: cipher's blocksize
+    :param int bs: cipher's blocksize, bytes
     :param bytes ct: ciphertext
     :param bytes iv: blocksize-sized initialization vector
     """
@@ -224,8 +268,12 @@ def _mac_shift(bs, data, xor_lsb=0):
     return long2bytes(num, bs)[-bs:]
 
 
+Rb64 = 0b11011
+Rb128 = 0b10000111
+
+
 def _mac_ks(encrypter, bs):
-    Rb = 0b10000111 if bs == 16 else 0b11011
+    Rb = Rb128 if bs == 16 else Rb64
     _l = encrypter(bs * b'\x00')
     k1 = _mac_shift(bs, _l, Rb) if bytearray(_l)[0] & 0x80 > 0 else _mac_shift(bs, _l)
     k2 = _mac_shift(bs, k1, Rb) if bytearray(k1)[0] & 0x80 > 0 else _mac_shift(bs, k1)
@@ -236,7 +284,7 @@ def mac(encrypter, bs, data):
     """MAC (known here as CMAC, OMAC1) mode of operation
 
     :param encrypter: encrypting function, that takes block as an input
-    :param int bs: cipher's blocksize
+    :param int bs: cipher's blocksize, bytes
     :param bytes data: data to authenticate
 
     Implementation is based on PyCrypto's CMAC one, that is in public domain.
@@ -254,3 +302,66 @@ def mac(encrypter, bs, data):
         strxor(pad3(tail, bs), prev),
         k1 if len(tail) == bs else k2,
     ))
+
+
+def acpkm_master(algo_class, encrypter, key_section_size, bs, keymat_len):
+    """ACPKM-Master key derivation
+
+    :param algo_class: pygost.gost3412's algorithm class
+    :param encrypter: encrypting function, that takes block as an input
+    :param int key_section_size: ACPKM'es key section size (T*), in bytes
+    :param int bs: cipher's blocksize, bytes
+    :param int keymat_len: length of key material to produce
+    """
+    return ctr_acpkm(
+        algo_class,
+        encrypter,
+        key_section_size,
+        bs,
+        data=b"\x00" * keymat_len,
+        iv=b"\xFF" * (bs // 2),
+    )
+
+
+def mac_acpkm_master(algo_class, encrypter, key_section_size, section_size, bs, data):
+    """OMAC-ACPKM-Master
+
+    :param algo_class: pygost.gost3412's algorithm class
+    :param encrypter: encrypting function, that takes block as an input
+    :param int key_section_size: ACPKM'es key section size (T*), in bytes
+    :param int section_size: ACPKM'es section size (N), in bytes
+    :param int bs: cipher's blocksize, bytes
+    :param bytes data: data to authenticate
+    """
+    if len(data) % bs == 0:
+        tail_offset = len(data) - bs
+    else:
+        tail_offset = len(data) - (len(data) % bs)
+    prev = bs * b'\x00'
+    sections = len(data) // section_size
+    if len(data) % section_size != 0:
+        sections += 1
+    keymats = acpkm_master(
+        algo_class,
+        encrypter,
+        key_section_size,
+        bs,
+        (KEY_SIZE + bs) * sections,
+    )
+    for i in xrange(0, tail_offset, bs):
+        if i % section_size == 0:
+            keymat, keymats = keymats[:KEY_SIZE + bs], keymats[KEY_SIZE + bs:]
+            key, k1 = keymat[:KEY_SIZE], keymat[KEY_SIZE:]
+            encrypter = algo_class(key).encrypt
+        prev = encrypter(strxor(data[i:i + bs], prev))
+    tail = data[tail_offset:]
+    if len(tail) == bs:
+        key, k1 = keymats[:KEY_SIZE], keymats[KEY_SIZE:]
+        encrypter = algo_class(key).encrypt
+    k2 = long2bytes(bytes2long(k1) << 1, size=bs)
+    if bytearray(k1)[0] & 0x80 != 0:
+        k2 = strxor(k2, long2bytes(Rb128 if bs == 16 else Rb64, size=bs))
+    return encrypter(strxor(
+        strxor(pad3(tail, bs), prev),
+        k1 if len(tail) == bs else k2,
+    ))
index 1648cf4f85f955b6ac770b4f8c46d219ecb33c0e..ef278b7fe63ee1536489e4f6bbda098d6538b61f 100644 (file)
@@ -1,4 +1,6 @@
 class GOST3412Kuznechik(object):
+    blocksize = ...  # type: int
+
     def __init__(self, key: bytes) -> None: ...
 
     def encrypt(self, blk: bytes) -> bytes: ...
@@ -7,6 +9,8 @@ class GOST3412Kuznechik(object):
 
 
 class GOST3412Magma(object):
+    blocksize = ...  # type: int
+
     def __init__(self, key: bytes) -> None: ...
 
     def encrypt(self, blk: bytes) -> bytes: ...
index 43ef9ee524dd2a7c56af416b26740473741393c9..d9d9c96b462f8f893888167f4cc5318bae263e4a 100644 (file)
@@ -22,9 +22,22 @@ def ecb_encrypt(encrypter: Callable[[bytes], bytes], bs: int, pt: bytes) -> byte
 def ecb_decrypt(decrypter: Callable[[bytes], bytes], bs: int, ct: bytes) -> bytes: ...
 
 
+def acpkm(encrypter: Callable[[bytes], bytes], bs: int) -> bytes: ...
+
+
 def ctr(encrypter: Callable[[bytes], bytes], bs: int, data: bytes, iv: bytes) -> bytes: ...
 
 
+def ctr_acpkm(
+        algo_class: object,
+        encrypter: Callable[[bytes], bytes],
+        section_size: int,
+        bs: int,
+        data: bytes,
+        iv: bytes,
+) -> bytes: ...
+
+
 def ofb(encrypter: Callable[[bytes], bytes], bs: int, data: bytes, iv: bytes) -> bytes: ...
 
 
@@ -41,3 +54,22 @@ def cfb_decrypt(encrypter: Callable[[bytes], bytes], bs: int, ct: bytes, iv: byt
 
 
 def mac(encrypter: Callable[[bytes], bytes], bs: int, data: bytes) -> bytes: ...
+
+
+def acpkm_master(
+        algo_class: object,
+        encrypter: Callable[[bytes], bytes],
+        key_section_size: int,
+        bs: int,
+        keymat_len: int,
+) -> bytes: ...
+
+
+def mac_acpkm_master(
+        algo_class: object,
+        encrypter: Callable[[bytes], bytes],
+        key_section_size: int,
+        section_size: int,
+        bs: int,
+        data: bytes,
+) -> bytes: ...
index 984bb76d17f00138a6e8551b5d2a6615fce65141..6bf2f885ec74b68d8ccc6bf9c9f1408bd0b83f89 100644 (file)
@@ -21,14 +21,19 @@ from unittest import TestCase
 from pygost.gost3412 import GOST3412Kuznechik
 from pygost.gost3412 import GOST3412Magma
 from pygost.gost3413 import _mac_ks
+from pygost.gost3413 import acpkm
+from pygost.gost3413 import acpkm_master
 from pygost.gost3413 import cbc_decrypt
 from pygost.gost3413 import cbc_encrypt
 from pygost.gost3413 import cfb_decrypt
 from pygost.gost3413 import cfb_encrypt
 from pygost.gost3413 import ctr
+from pygost.gost3413 import ctr_acpkm
 from pygost.gost3413 import ecb_decrypt
 from pygost.gost3413 import ecb_encrypt
+from pygost.gost3413 import KEY_SIZE
 from pygost.gost3413 import mac
+from pygost.gost3413 import mac_acpkm_master
 from pygost.gost3413 import ofb
 from pygost.gost3413 import pad2
 from pygost.gost3413 import unpad2
@@ -343,3 +348,233 @@ class GOST3412MagmaModesTest(TestCase):
             data = urandom(randint(0, 16 * 2))
             ciph = GOST3412Magma(urandom(32))
             mac(ciph.encrypt, 8, data)
+
+
+class TestVectorACPKM(TestCase):
+    """Test vectors from Р 1323565.1.017-2018
+    """
+    key = hexdec("8899AABBCCDDEEFF0011223344556677FEDCBA98765432100123456789ABCDEF")
+
+    def test_magma_ctr_acpkm(self):
+        key = acpkm(GOST3412Magma(self.key).encrypt, 8)
+        self.assertSequenceEqual(key, hexdec("863EA017842C3D372B18A85A28E2317D74BEFC107720DE0C9E8AB974ABD00CA0"))
+        key = acpkm(GOST3412Magma(key).encrypt, 8)
+        self.assertSequenceEqual(key, hexdec("49A5E2677DE555982B8AD5E826652D17EEC847BF5B3997A81CF7FE7F1187BD27"))
+        key = acpkm(GOST3412Magma(key).encrypt, 8)
+        self.assertSequenceEqual(key, hexdec("3256BF3F97B5667426A9FB1C5EAABE41893CCDD5A868F9B63B0AA90720FA43C4"))
+
+    def test_magma_ctr(self):
+        encrypter = GOST3412Magma(self.key).encrypt
+        plaintext = hexdec("""
+11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88
+00 11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A
+11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00
+22 33 44 55 66 77 88 99
+        """.replace("\n", "").replace(" ", ""))
+        iv = hexdec("12345678")
+        ciphertext = hexdec("""
+2A B8 1D EE EB 1E 4C AB 68 E1 04 C4 BD 6B 94 EA
+C7 2C 67 AF 6C 2E 5B 6B 0E AF B6 17 70 F1 B3 2E
+A1 AE 71 14 9E ED 13 82 AB D4 67 18 06 72 EC 6F
+84 A2 F1 5B 3F CA 72 C1
+        """.replace("\n", "").replace(" ", ""))
+        self.assertSequenceEqual(
+            ctr_acpkm(GOST3412Magma, encrypter, bs=8, section_size=16, data=plaintext, iv=iv),
+            ciphertext,
+        )
+        self.assertSequenceEqual(
+            ctr_acpkm(GOST3412Magma, encrypter, bs=8, section_size=16, data=ciphertext, iv=iv),
+            plaintext,
+        )
+
+    def test_kuznechik_ctr_acpkm(self):
+        key = acpkm(GOST3412Kuznechik(self.key).encrypt, 16)
+        self.assertSequenceEqual(key, hexdec("2666ED40AE687811745CA0B448F57A7B390ADB5780307E8E9659AC403AE60C60"))
+        key = acpkm(GOST3412Kuznechik(key).encrypt, 16)
+        self.assertSequenceEqual(key, hexdec("BB3DD5402E999B7A3DEBB0DB45448EC530F07365DFEE3ABA8415F77AC8F34CE8"))
+        key = acpkm(GOST3412Kuznechik(key).encrypt, 16)
+        self.assertSequenceEqual(key, hexdec("23362FD553CAD2178299A5B5A2D4722E3BB83C730A8BF57CE2DD004017F8C565"))
+
+    def test_kuznechik_ctr(self):
+        encrypter = GOST3412Kuznechik(self.key).encrypt
+        iv = hexdec("1234567890ABCEF0")
+        plaintext = hexdec("""
+11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88
+00 11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A
+11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00
+22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 11
+33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 11 22
+44 55 66 77 88 99 AA BB CC EE FF 0A 00 11 22 33
+55 66 77 88 99 AA BB CC EE FF 0A 00 11 22 33 44
+        """.replace("\n", "").replace(" ", ""))
+        ciphertext = hexdec("""
+F1 95 D8 BE C1 0E D1 DB D5 7B 5F A2 40 BD A1 B8
+85 EE E7 33 F6 A1 3E 5D F3 3C E4 B3 3C 45 DE E4
+4B CE EB 8F 64 6F 4C 55 00 17 06 27 5E 85 E8 00
+58 7C 4D F5 68 D0 94 39 3E 48 34 AF D0 80 50 46
+CF 30 F5 76 86 AE EC E1 1C FC 6C 31 6B 8A 89 6E
+DF FD 07 EC 81 36 36 46 0C 4F 3B 74 34 23 16 3E
+64 09 A9 C2 82 FA C8 D4 69 D2 21 E7 FB D6 DE 5D
+        """.replace("\n", "").replace(" ", ""))
+        self.assertSequenceEqual(
+            ctr_acpkm(
+                GOST3412Kuznechik,
+                encrypter,
+                bs=16,
+                section_size=32,
+                data=plaintext,
+                iv=iv,
+            ),
+            ciphertext,
+        )
+        self.assertSequenceEqual(
+            ctr_acpkm(
+                GOST3412Kuznechik,
+                encrypter,
+                bs=16,
+                section_size=32,
+                data=ciphertext,
+                iv=iv,
+            ),
+            plaintext,
+        )
+
+    def test_magma_omac_1_5_blocks(self):
+        encrypter = GOST3412Magma(self.key).encrypt
+        key_section_size = 640 // 8
+        self.assertSequenceEqual(
+            acpkm_master(
+                GOST3412Magma,
+                encrypter,
+                key_section_size=key_section_size,
+                bs=8,
+                keymat_len=KEY_SIZE + 8,
+            ),
+            hexdec("0DF2F5273DA328932AC49D81D36B2558A50DBF9BBCAC74A614B2CCB2F1CBCD8A70638E3DE8B3571E"),
+        )
+        text = hexdec("1122334455667700FFEEDDCC")
+        self.assertSequenceEqual(
+            mac_acpkm_master(
+                GOST3412Magma,
+                encrypter,
+                key_section_size,
+                section_size=16,
+                bs=8,
+                data=text,
+            ),
+            hexdec("A0540E3730ACBCF3"),
+        )
+
+    def test_magma_omac_5_blocks(self):
+        encrypter = GOST3412Magma(self.key).encrypt
+        key_section_size = 640 // 8
+        self.assertSequenceEqual(
+            acpkm_master(
+                GOST3412Magma,
+                encrypter,
+                key_section_size=key_section_size,
+                bs=8,
+                keymat_len=3 * (KEY_SIZE + 8),
+            ),
+            hexdec("""
+0D F2 F5 27 3D A3 28 93 2A C4 9D 81 D3 6B 25 58
+A5 0D BF 9B BC AC 74 A6 14 B2 CC B2 F1 CB CD 8A
+70 63 8E 3D E8 B3 57 1E 8D 38 26 D5 5E 63 A1 67
+E2 40 66 40 54 7B 9F 1F 5F 2B 43 61 2A AE AF DA
+18 0B AC 86 04 DF A6 FE 53 C2 CE 27 0E 9C 9F 52
+68 D0 FD BF E1 A3 BD D9 BE 5B 96 D0 A1 20 23 48
+6E F1 71 0F 92 4A E0 31 30 52 CB 5F CA 0B 79 1E
+1B AB E8 57 6D 0F E3 A8
+            """.replace("\n", "").replace(" ", "")),
+        )
+        text = hexdec("""
+11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88
+00 11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A
+11 22 33 44 55 66 77 88
+        """.replace("\n", "").replace(" ", ""))
+        self.assertSequenceEqual(
+            mac_acpkm_master(
+                GOST3412Magma,
+                encrypter,
+                key_section_size,
+                section_size=16,
+                bs=8,
+                data=text,
+            ),
+            hexdec("34008DAD5496BB8E"),
+        )
+
+    def test_kuznechik_omac_1_5_blocks(self):
+        encrypter = GOST3412Kuznechik(self.key).encrypt
+        key_section_size = 768 // 8
+        self.assertSequenceEqual(
+            acpkm_master(
+                GOST3412Kuznechik,
+                encrypter,
+                key_section_size=key_section_size,
+                bs=16,
+                keymat_len=KEY_SIZE + 16,
+            ),
+            hexdec("""
+0C AB F1 F2 EF BC 4A C1 60 48 DF 1A 24 C6 05 B2
+C0 D1 67 3D 75 86 A8 EC 0D D4 2C 45 A4 F9 5B AE
+0F 2E 26 17 E4 71 48 68 0F C3 E6 17 8D F2 C1 37
+            """.replace("\n", "").replace(" ", ""))
+        )
+        text = hexdec("""
+11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88
+00 11 22 33 44 55 66 77
+        """.replace("\n", "").replace(" ", ""))
+        self.assertSequenceEqual(
+            mac_acpkm_master(
+                GOST3412Kuznechik,
+                encrypter,
+                key_section_size,
+                section_size=32,
+                bs=16,
+                data=text,
+            ),
+            hexdec("B5367F47B62B995EEB2A648C5843145E"),
+        )
+
+    def test_kuznechik_omac_5_blocks(self):
+        encrypter = GOST3412Kuznechik(self.key).encrypt
+        key_section_size = 768 // 8
+        self.assertSequenceEqual(
+            acpkm_master(
+                GOST3412Kuznechik,
+                encrypter,
+                key_section_size=key_section_size,
+                bs=16,
+                keymat_len=3 * (KEY_SIZE + 16),
+            ),
+            hexdec("""
+0C AB F1 F2 EF BC 4A C1 60 48 DF 1A 24 C6 05 B2
+C0 D1 67 3D 75 86 A8 EC 0D D4 2C 45 A4 F9 5B AE
+0F 2E 26 17 E4 71 48 68 0F C3 E6 17 8D F2 C1 37
+C9 DD A8 9C FF A4 91 FE AD D9 B3 EA B7 03 BB 31
+BC 7E 92 7F 04 94 72 9F 51 B4 9D 3D F9 C9 46 08
+00 FB BC F5 ED EE 61 0E A0 2F 01 09 3C 7B C7 42
+D7 D6 27 15 01 B1 77 77 52 63 C2 A3 49 5A 83 18
+A8 1C 79 A0 4F 29 66 0E A3 FD A8 74 C6 30 79 9E
+14 2C 57 79 14 FE A9 0D 3B C2 50 2E 83 36 85 D9
+            """.replace("\n", "").replace(" ", "")),
+        )
+        text = hexdec("""
+11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88
+00 11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A
+11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00
+22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 11
+33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 11 22
+        """.replace("\n", "").replace(" ", ""))
+        self.assertSequenceEqual(
+            mac_acpkm_master(
+                GOST3412Kuznechik,
+                encrypter,
+                key_section_size,
+                section_size=32,
+                bs=16,
+                data=text,
+            ),
+            hexdec("FBB8DCEE45BEA67C35F58C5700898E5D"),
+        )
index 013780b332a76a620d44978a221077e211d5697f..a9d5c7bc517290d1e62a3b59fdf45e3da2493246 100644 (file)
--- a/www.texi
+++ b/www.texi
@@ -55,7 +55,8 @@ Currently supported algorithms are:
 @item GOST R 34.12-2015 64-bit block cipher Магма (Magma)
 @item GOST R 34.13-2015 padding methods and block cipher modes of operation
       (ECB, CTR, OFB, CBC, CFB, MAC)
-@item MGM AEAD mode for 64 and 128 bit ciphers
+@item MGM AEAD mode for 64 and 128 bit ciphers (Р 1323565.1.026–2019)
+@item CTR-ACPKM, OMAC-ACPKM-Master modes of operation (Р 1323565.1.017-2018)
 @item PEP247-compatible hash/MAC functions
 @end itemize