X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=pygost%2Fgost28147.py;h=74f5e877add7e7945d824d73e2cf237d09a54bb1;hb=31b08d5a78505f0ae1a144e58d023d84eda2cc6e;hp=585d0e7f6866a2215664e06246d591b8664c4e25;hpb=5f31b66fe7bfc9e872108cef21fd82800c09b87d;p=pygost.git diff --git a/pygost/gost28147.py b/pygost/gost28147.py index 585d0e7..74f5e87 100644 --- a/pygost/gost28147.py +++ b/pygost/gost28147.py @@ -1,11 +1,10 @@ # coding: utf-8 # PyGOST -- Pure Python GOST cryptographic functions library -# Copyright (C) 2015-2018 Sergey Matveev +# Copyright (C) 2015-2020 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 28147-89 block cipher +"""GOST 28147-89 block cipher This is implementation of :rfc:`5830` ECB, CNT, CFB and :rfc:`4357` CBC modes of operation. N1, N2, K names are taken according to @@ -29,7 +28,7 @@ from pygost.gost3413 import pad_size from pygost.gost3413 import unpad2 from pygost.utils import hexdec from pygost.utils import strxor -from pygost.utils import xrange # pylint: disable=redefined-builtin +from pygost.utils import xrange KEYSIZE = 32 @@ -52,9 +51,9 @@ SEQ_DECRYPT = ( ) # S-box parameters -DEFAULT_SBOX = "Gost28147_CryptoProParamSetA" +DEFAULT_SBOX = "id-Gost28147-89-CryptoPro-A-ParamSet" SBOXES = { - "Gost2814789_TestParamSet": ( + "id-Gost28147-89-TestParamSet": ( (4, 2, 15, 5, 9, 1, 0, 8, 14, 3, 11, 12, 13, 7, 10, 6), (12, 9, 15, 14, 8, 1, 3, 10, 2, 7, 4, 13, 6, 0, 11, 5), (13, 8, 14, 12, 7, 3, 9, 10, 1, 5, 2, 4, 6, 15, 0, 11), @@ -64,7 +63,7 @@ SBOXES = { (9, 11, 12, 0, 3, 6, 7, 5, 4, 8, 14, 15, 1, 10, 2, 13), (12, 6, 5, 2, 11, 0, 9, 13, 3, 14, 7, 10, 15, 4, 1, 8), ), - "Gost28147_CryptoProParamSetA": ( + "id-Gost28147-89-CryptoPro-A-ParamSet": ( (9, 6, 3, 2, 8, 11, 1, 7, 10, 4, 14, 15, 12, 0, 13, 5), (3, 7, 14, 9, 8, 10, 15, 0, 5, 2, 6, 12, 11, 4, 13, 1), (14, 4, 6, 2, 11, 3, 13, 8, 12, 15, 5, 10, 0, 7, 1, 9), @@ -74,7 +73,7 @@ SBOXES = { (1, 13, 2, 9, 7, 10, 6, 0, 8, 12, 4, 5, 15, 3, 11, 14), (11, 10, 15, 5, 0, 12, 14, 8, 6, 2, 3, 9, 1, 7, 13, 4), ), - "Gost28147_CryptoProParamSetB": ( + "id-Gost28147-89-CryptoPro-B-ParamSet": ( (8, 4, 11, 1, 3, 5, 0, 9, 2, 14, 10, 12, 13, 6, 7, 15), (0, 1, 2, 10, 4, 13, 5, 12, 9, 7, 3, 15, 11, 8, 6, 14), (14, 12, 0, 10, 9, 2, 13, 11, 7, 5, 8, 15, 3, 6, 1, 4), @@ -84,7 +83,7 @@ SBOXES = { (5, 2, 10, 11, 9, 1, 12, 3, 7, 4, 13, 0, 6, 15, 8, 14), (0, 4, 11, 14, 8, 3, 7, 1, 10, 2, 9, 6, 15, 13, 5, 12), ), - "Gost28147_CryptoProParamSetC": ( + "id-Gost28147-89-CryptoPro-C-ParamSet": ( (1, 11, 12, 2, 9, 13, 0, 15, 4, 5, 8, 14, 10, 7, 6, 3), (0, 1, 7, 13, 11, 4, 5, 2, 8, 14, 15, 12, 9, 10, 6, 3), (8, 2, 5, 0, 4, 9, 15, 10, 3, 7, 12, 13, 6, 14, 1, 11), @@ -94,7 +93,7 @@ SBOXES = { (10, 9, 6, 8, 13, 14, 2, 0, 15, 3, 5, 11, 4, 1, 12, 7), (7, 4, 0, 5, 10, 2, 15, 14, 12, 6, 1, 11, 13, 9, 3, 8), ), - "Gost28147_CryptoProParamSetD": ( + "id-Gost28147-89-CryptoPro-D-ParamSet": ( (15, 12, 2, 10, 6, 4, 5, 0, 7, 9, 14, 13, 1, 11, 8, 3), (11, 6, 3, 4, 12, 15, 14, 2, 7, 13, 8, 0, 5, 10, 9, 1), (1, 12, 11, 0, 15, 14, 6, 5, 10, 13, 4, 8, 9, 3, 7, 2), @@ -104,7 +103,17 @@ SBOXES = { (3, 0, 6, 15, 1, 14, 9, 2, 13, 8, 12, 4, 11, 10, 5, 7), (1, 10, 6, 8, 15, 11, 0, 4, 12, 3, 5, 9, 7, 13, 2, 14), ), - "GostR3411_94_TestParamSet": ( + "id-tc26-gost-28147-param-Z": ( + (12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1), + (6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15), + (11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0), + (12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11), + (7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12), + (5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0), + (8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7), + (1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2), + ), + "id-GostR3411-94-TestParamSet": ( (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3), (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9), (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11), @@ -114,7 +123,7 @@ SBOXES = { (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12), (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12), ), - "GostR3411_94_CryptoProParamSet": ( + "id-GostR3411-94-CryptoProParamSet": ( (10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15), (5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8), (7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13), @@ -124,16 +133,6 @@ SBOXES = { (13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11), (1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12), ), - "Gost28147_tc26_ParamZ": ( - (12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1), - (6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15), - (11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0), - (12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11), - (7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12), - (5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0), - (8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7), - (1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2), - ), "EACParamSet": ( (11, 4, 8, 10, 9, 7, 0, 3, 1, 6, 2, 15, 14, 5, 12, 13), (1, 7, 14, 9, 11, 3, 15, 12, 0, 5, 4, 6, 13, 10, 8, 2), @@ -145,11 +144,11 @@ SBOXES = { (7, 14, 12, 13, 9, 4, 8, 15, 10, 2, 6, 0, 3, 11, 5, 1), ), } -SBOXES["AppliedCryptography"] = SBOXES["GostR3411_94_TestParamSet"] +SBOXES["AppliedCryptography"] = SBOXES["id-GostR3411-94-TestParamSet"] def _K(s, _in): - """ S-box substitution + """S-box substitution :param s: S-box :param _in: 32-bit word @@ -168,7 +167,7 @@ def _K(s, _in): def block2ns(data): - """ Convert block to N1 and N2 integers + """Convert block to N1 and N2 integers """ data = bytearray(data) return ( @@ -178,24 +177,17 @@ def block2ns(data): def ns2block(ns): - """ Convert N1 and N2 integers to 8-byte block + """Convert N1 and N2 integers to 8-byte block """ n1, n2 = ns return bytes(bytearray(( - (n2 >> 0) & 255, (n2 >> 8) & 255, (n2 >> 16) & 255, (n2 >> 24) & 255, - (n1 >> 0) & 255, (n1 >> 8) & 255, (n1 >> 16) & 255, (n1 >> 24) & 255, + (n2 >> 0) & 0xFF, (n2 >> 8) & 0xFF, (n2 >> 16) & 0xFF, (n2 >> 24) & 0xFF, + (n1 >> 0) & 0xFF, (n1 >> 8) & 0xFF, (n1 >> 16) & 0xFF, (n1 >> 24) & 0xFF, ))) -def addmod(x, y, mod=2 ** 32): - """ Modulo adding of two integers - """ - r = x + y - return r if r < mod else r - mod - - def _shift11(x): - """ 11-bit cyclic shift + """11-bit cyclic shift """ return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1)) @@ -216,7 +208,7 @@ def validate_sbox(sbox): def xcrypt(seq, sbox, key, ns): - """ Perform full-round single-block operation + """Perform full-round single-block operation :param seq: sequence of K_i S-box applying (either encrypt or decrypt) :param sbox: S-box parameters to use @@ -237,24 +229,24 @@ def xcrypt(seq, sbox, key, ns): ] n1, n2 = ns for i in seq: - n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1 + n1, n2 = _shift11(_K(s, (n1 + x[i]) % (2 ** 32))) ^ n2, n1 return n1, n2 def encrypt(sbox, key, ns): - """ Encrypt single block + """Encrypt single block """ return xcrypt(SEQ_ENCRYPT, sbox, key, ns) def decrypt(sbox, key, ns): - """ Decrypt single block + """Decrypt single block """ return xcrypt(SEQ_DECRYPT, sbox, key, ns) def ecb(key, data, action, sbox=DEFAULT_SBOX): - """ ECB mode of operation + """ECB mode of operation :param bytes key: encryption key :param data: plaintext @@ -281,8 +273,8 @@ ecb_encrypt = partial(ecb, action=encrypt) ecb_decrypt = partial(ecb, action=decrypt) -def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX): - """ CBC encryption mode of operation +def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX, mesh=False): + """CBC encryption mode of operation :param bytes key: encryption key :param bytes data: plaintext @@ -291,6 +283,7 @@ def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX): :type bool pad: perform ISO/IEC 7816-4 padding :param sbox: S-box parameters to use :type sbox: str, SBOXES'es key + :param bool mesh: enable key meshing :returns: ciphertext :rtype: bytes @@ -307,20 +300,23 @@ def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX): raise ValueError("Data is not blocksize aligned") ciphertext = [iv] for i in xrange(0, len(data), BLOCKSIZE): + if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0: + key, _ = meshing(key, iv, sbox=sbox) ciphertext.append(ns2block(encrypt(sbox, key, block2ns( strxor(ciphertext[-1], data[i:i + BLOCKSIZE]) )))) return b"".join(ciphertext) -def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX): - """ CBC decryption mode of operation +def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX, mesh=False): + """CBC decryption mode of operation :param bytes key: encryption key :param bytes data: ciphertext :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption :param sbox: S-box parameters to use :type sbox: str, SBOXES'es key + :param bool mesh: enable key meshing :returns: plaintext :rtype: bytes """ @@ -330,8 +326,15 @@ def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX): raise ValueError("Data is not blocksize aligned") if len(data) < 2 * BLOCKSIZE: raise ValueError("There is no either data, or IV in ciphertext") + iv = data[:BLOCKSIZE] plaintext = [] for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE): + if ( + mesh and + (i - BLOCKSIZE) >= MESH_MAX_DATA and + (i - BLOCKSIZE) % MESH_MAX_DATA == 0 + ): + key, _ = meshing(key, iv, sbox=sbox) plaintext.append(strxor( ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))), data[i - BLOCKSIZE:i], @@ -342,7 +345,7 @@ def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX): def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX): - """ Counter mode of operation + """Counter mode of operation :param bytes key: encryption key :param bytes data: plaintext @@ -363,8 +366,8 @@ def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX): n2, n1 = encrypt(sbox, key, block2ns(iv)) gamma = [] for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE): - n1 = addmod(n1, C2, 2 ** 32) - n2 = addmod(n2, C1, 2 ** 32 - 1) + n1 = (n1 + C2) % (2 ** 32) + n2 = (n2 + C1) % (2 ** 32 - 1) gamma.append(ns2block(encrypt(sbox, key, (n1, n2)))) return strxor(b"".join(gamma), data) @@ -382,7 +385,7 @@ def meshing(key, iv, sbox=DEFAULT_SBOX): def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False): - """ CFB encryption mode of operation + """CFB encryption mode of operation :param bytes key: encryption key :param bytes data: plaintext @@ -416,7 +419,7 @@ def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False): def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False): - """ CFB decryption mode of operation + """CFB decryption mode of operation :param bytes key: encryption key :param bytes data: plaintext