From 4277661e11e63906d923827e5c124877f1dc0c8d Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Tue, 28 Jul 2020 18:45:42 +0300 Subject: [PATCH] CTR-ACPKM, OMAC-ACPKM-Master modes of operation --- README | 3 +- VERSION | 2 +- install.texi | 2 +- news.texi | 7 + pygost/__init__.py | 2 +- pygost/gost3412.py | 6 + pygost/gost3413.py | 135 ++++++++++++++++-- pygost/stubs/pygost/gost3412.pyi | 4 + pygost/stubs/pygost/gost3413.pyi | 32 +++++ pygost/test_gost3413.py | 235 +++++++++++++++++++++++++++++++ www.texi | 3 +- 11 files changed, 414 insertions(+), 17 deletions(-) diff --git a/README b/README index 24b2808..7fde53f 100644 --- 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 ef216a5..86a9588 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.8 +4.9 diff --git a/install.texi b/install.texi index 737e636..a397913 100644 --- a/install.texi +++ b/install.texi @@ -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. diff --git a/news.texi b/news.texi index cccd4f3..d1c27b6 100644 --- 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. diff --git a/pygost/__init__.py b/pygost/__init__.py index 488683d..c2d5ea8 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__ = "4.8" +__version__ = "4.9" diff --git a/pygost/gost3412.py b/pygost/gost3412.py index 450cc7a..296e4fb 100644 --- a/pygost/gost3412.py +++ b/pygost/gost3412.py @@ -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 diff --git a/pygost/gost3413.py b/pygost/gost3413.py index 3a539d2..495a3f9 100644 --- a/pygost/gost3413.py +++ b/pygost/gost3413.py @@ -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, + )) diff --git a/pygost/stubs/pygost/gost3412.pyi b/pygost/stubs/pygost/gost3412.pyi index 1648cf4..ef278b7 100644 --- a/pygost/stubs/pygost/gost3412.pyi +++ b/pygost/stubs/pygost/gost3412.pyi @@ -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: ... diff --git a/pygost/stubs/pygost/gost3413.pyi b/pygost/stubs/pygost/gost3413.pyi index 43ef9ee..d9d9c96 100644 --- a/pygost/stubs/pygost/gost3413.pyi +++ b/pygost/stubs/pygost/gost3413.pyi @@ -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: ... diff --git a/pygost/test_gost3413.py b/pygost/test_gost3413.py index 984bb76..6bf2f88 100644 --- a/pygost/test_gost3413.py +++ b/pygost/test_gost3413.py @@ -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"), + ) diff --git a/www.texi b/www.texi index 013780b..a9d5c7b 100644 --- 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 -- 2.44.0