X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=pygost%2Fgost3413.py;h=3a539d2e4b1f9097e78774ea3cf263df648a9d5a;hb=54735617392b3988c2e24b59e0c552d74736e320;hp=5fb73f8ccf9d30d12830d2626b1229d4f004de1f;hpb=c5a63fc144c59342e32304e4e86b2bad479a8187;p=pygost.git diff --git a/pygost/gost3413.py b/pygost/gost3413.py index 5fb73f8..3a539d2 100644 --- a/pygost/gost3413.py +++ b/pygost/gost3413.py @@ -1,11 +1,10 @@ # coding: utf-8 # PyGOST -- Pure Python GOST cryptographic functions library -# Copyright (C) 2015-2016 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 @@ -19,9 +18,14 @@ This module currently includes only padding methods. """ +from pygost.utils import bytes2long +from pygost.utils import long2bytes +from pygost.utils import strxor +from pygost.utils import xrange + def pad_size(data_size, blocksize): - """Calculate required pad size to full up BLOCKSIZE + """Calculate required pad size to full up blocksize """ if data_size < blocksize: return blocksize - data_size @@ -46,9 +50,207 @@ def pad2(data, blocksize): return data + b"\x80" + b"\x00" * pad_size(len(data) + 1, blocksize) +def unpad2(data, blocksize): + """Unpad method 2 + """ + last_block = bytearray(data[-blocksize:]) + pad_index = last_block.rfind(b"\x80") + if pad_index == -1: + raise ValueError("Invalid padding") + for c in last_block[pad_index + 1:]: + if c != 0: + raise ValueError("Invalid padding") + return data[:-(blocksize - pad_index)] + + def pad3(data, blocksize): """Padding method 3 """ if pad_size(len(data), blocksize) == 0: return data return pad2(data, blocksize) + + +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 bytes pt: already padded plaintext + """ + if not pt or len(pt) % bs != 0: + raise ValueError("Plaintext is not blocksize aligned") + ct = [] + for i in xrange(0, len(pt), bs): + ct.append(encrypter(pt[i:i + bs])) + return b"".join(ct) + + +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 bytes ct: ciphertext + """ + if not ct or len(ct) % bs != 0: + raise ValueError("Ciphertext is not blocksize aligned") + pt = [] + for i in xrange(0, len(ct), bs): + pt.append(decrypter(ct[i:i + bs])) + return b"".join(pt) + + +def ctr(encrypter, bs, data, iv): + """Counter mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize + :param bytes data: plaintext/ciphertext + :param bytes iv: half blocksize-sized initialization vector + + For decryption you use the same function again. + """ + if len(iv) != bs // 2: + raise ValueError("Invalid IV size") + stream = [] + ctr_value = 0 + for _ in xrange(0, len(data) + pad_size(len(data), bs), bs): + stream.append(encrypter(iv + long2bytes(ctr_value, bs // 2))) + ctr_value += 1 + return strxor(b"".join(stream), data) + + +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 bytes data: plaintext/ciphertext + :param bytes iv: blocksize-sized initialization vector + + For decryption you use the same function again. + """ + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + result = [] + for i in xrange(0, len(data) + pad_size(len(data), bs), bs): + r = r[1:] + [encrypter(r[0])] + result.append(strxor(r[-1], data[i:i + bs])) + return b"".join(result) + + +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 bytes pt: already padded plaintext + :param bytes iv: blocksize-sized initialization vector + """ + if not pt or len(pt) % bs != 0: + raise ValueError("Plaintext is not blocksize aligned") + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + ct = [] + for i in xrange(0, len(pt), bs): + ct.append(encrypter(strxor(r[0], pt[i:i + bs]))) + r = r[1:] + [ct[-1]] + return b"".join(ct) + + +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 bytes ct: ciphertext + :param bytes iv: blocksize-sized initialization vector + """ + if not ct or len(ct) % bs != 0: + raise ValueError("Ciphertext is not blocksize aligned") + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + pt = [] + for i in xrange(0, len(ct), bs): + blk = ct[i:i + bs] + pt.append(strxor(r[0], decrypter(blk))) + r = r[1:] + [blk] + return b"".join(pt) + + +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 bytes pt: plaintext + :param bytes iv: blocksize-sized initialization vector + """ + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + ct = [] + for i in xrange(0, len(pt) + pad_size(len(pt), bs), bs): + ct.append(strxor(encrypter(r[0]), pt[i:i + bs])) + r = r[1:] + [ct[-1]] + return b"".join(ct) + + +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 bytes ct: ciphertext + :param bytes iv: blocksize-sized initialization vector + """ + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + pt = [] + for i in xrange(0, len(ct) + pad_size(len(ct), bs), bs): + blk = ct[i:i + bs] + pt.append(strxor(encrypter(r[0]), blk)) + r = r[1:] + [blk] + return b"".join(pt) + + +def _mac_shift(bs, data, xor_lsb=0): + num = (bytes2long(data) << 1) ^ xor_lsb + return long2bytes(num, bs)[-bs:] + + +def _mac_ks(encrypter, bs): + Rb = 0b10000111 if bs == 16 else 0b11011 + _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) + return k1, k2 + + +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 bytes data: data to authenticate + + Implementation is based on PyCrypto's CMAC one, that is in public domain. + """ + k1, k2 = _mac_ks(encrypter, bs) + if len(data) % bs == 0: + tail_offset = len(data) - bs + else: + tail_offset = len(data) - (len(data) % bs) + prev = bs * b'\x00' + for i in xrange(0, tail_offset, bs): + prev = encrypter(strxor(data[i:i + bs], prev)) + tail = data[tail_offset:] + return encrypter(strxor( + strxor(pad3(tail, bs), prev), + k1 if len(tail) == bs else k2, + ))