def encrypt(self, data: bytearray) -> bytearray: """ Plaintext encryption in CTR mode. Args: data: Plaintext data to be encrypted (as a byte object). Returns: Ciphertext data (as a byte object). Raises: GOSTCipherError('GOSTCipherError: invalid plaintext data'): In case where the plaintext data is not byte object. """ result = bytearray() gamma = bytearray() data = super().encrypt(data) for i in range(self._get_num_block(data)): gamma = self._cipher_obj.encrypt(self._counter) self._counter = self._inc_ctr(self._counter) result = result + add_xor(self._get_block(data, i), gamma) if len(data) % self.block_size != 0: gamma = self._cipher_obj.encrypt(self._counter) self._counter = self._inc_ctr(self._counter) result = result + add_xor( data[self.block_size * self._get_num_block(data)::], gamma) return result
def encrypt(self, data: bytearray) -> bytearray: """ Plaintext encryption in CTR mode. Parameters - data: plaintext data to be encrypted (as a byte object). Return: ciphertext data (as a byte object). Exception - GOSTCipherError('invalid plaintext data'): in case where the plaintext data is not byte object. """ if not isinstance(data, (bytes, bytearray)): self.clear() raise GOSTCipherError('invalid plaintext data') gamma = bytearray() result = bytearray() for i in range(get_num_block(data, self.block_size)): gamma = self._cipher_obj.encrypt(self._counter) self._counter = self._inc_ctr(self._counter) result = result + add_xor( data[self.block_size * i:self.block_size + (self.block_size * i)], gamma) if len(data) % self.block_size != 0: gamma = self._cipher_obj.encrypt(self._counter) self._counter = self._inc_ctr(self._counter) result = result + add_xor( data[self.block_size * get_num_block(data, self.block_size)::], gamma) return result
def __init__(self, key: bytearray): """ Initialize the ciphering object. Args: key: Encryption key. """ self._cipher_c: List[bytearray] = [] self._cipher_iter_key = [] self._cipher_get_c() self.oid = ObjectIdentifier('1.2.643.7.1.1.5.2') key_1 = key[:_KEY_SIZE // 2] key_2 = key[_KEY_SIZE // 2:] internal = bytearray(_KEY_SIZE // 2) self._cipher_iter_key.append(key_1) self._cipher_iter_key.append(key_2) for i in range(4): for j in range(8): internal = add_xor(key_1, self._cipher_c[i * 8 + j]) internal = GOST34122015Kuznechik._cipher_s(internal) internal = GOST34122015Kuznechik._cipher_l(internal) key_1, key_2 = [add_xor(internal, key_2), key_1] self._cipher_iter_key.append(key_1) self._cipher_iter_key.append(key_2) key_1 = bytearray(self.key_size // 2) key_2 = bytearray(self.key_size // 2) key = bytearray(self.key_size)
def update(self, data: bytearray) -> None: """ Update the MAC object with the bytes-like object. Parameters - data: The data from which to get the MAC (as a byte object). Repeated calls are equivalent to a single call with the concatenation of all the arguments: 'm.update(a)'; 'm.update(b)' is equivalent to 'm.update(a+b)'. Exception - GOSTCipherError('invalid text data'): in case where the text data is not byte object. """ if not isinstance(data, (bytes, bytearray)): self.clear() raise GOSTCipherError('invalid text data') data = set_pad_mode_3(data, self.block_size) block = bytearray() prev_block = self._cur_mac for i in range(0, get_num_block(data, self.block_size) - 1): block = (self._cipher_obj.encrypt( add_xor( prev_block, data[self.block_size * i:self.block_size + (self.block_size * i)]))) prev_block = block block = (self._cipher_obj.encrypt( add_xor(prev_block, data[len(data) - self.block_size:len(data)]))) self._cur_mac = block self._prev_mac = prev_block self._buff = data[self.block_size * (get_num_block(data, self.block_size) - 1):]
def _hash_e(self, k: bytearray, data: bytearray) -> bytearray: internal = add_xor(k, data) for i in range(12): internal = self._hash_s(internal) internal = self._hash_p(internal) internal = self._hash_l(internal) k = self._hash_get_key(k, i) internal = add_xor(internal, k) return internal
def _hash_g(self, hash_h: bytearray, hash_n: bytearray, data: bytearray) -> bytearray: k = add_xor(hash_n, hash_h) k = self._hash_s(k) k = self._hash_p(k) k = self._hash_l(k) internal = self._hash_e(k, data) internal = add_xor(internal, hash_h) result = add_xor(internal, data) return result
def mac_final(self) -> bytearray: """Return the final value of the MAC.""" if get_pad_size(self._buff, self.block_size) == 0: final_key = self._key_1 else: final_key = self._key_2 self._buff = set_pad_mode_3(self._buff, self.block_size) result = bytearray() result = self._cipher_obj.encrypt( add_xor(add_xor(self._prev_mac, self._buff), final_key)) return result
def _f(self) -> bytearray: _t = self._u_first() internal = self._u_first() for _ in range(1, self._iterations): internal = self._u_iter(internal) _t = add_xor(_t, internal) return _t
def decrypt(self, data: bytearray) -> bytearray: """ Ciphertext decryption in CBC mode. Parameters - data: ciphertext data to be decrypted (as a byte object). Return: plaintext data (as a byte object). Exception - GOSTCipherError('invalid ciphertext data'): in case where the ciphertext data is not byte object. """ if not isinstance(data, (bytes, bytearray)): self.clear() raise GOSTCipherError('invalid ciphertext data') result = bytearray() for i in range(get_num_block(data, self.block_size)): cipher_blk = (add_xor( self._init_vect[0:self.block_size], self._cipher_obj.decrypt( data[self.block_size * i:self.block_size + (self.block_size * i)]))) result = result + cipher_blk self._init_vect[0:len(self._init_vect) - self.block_size] = ( self._init_vect[self.block_size:len(self._init_vect)]) self._init_vect[len(self._init_vect) - self.block_size:len(self._init_vect)] = ( data[self.block_size * i:self.block_size + (self.block_size * i)]) return result
def encrypt(self, data: bytearray) -> bytearray: """ Plaintext encryption in OFB mode. Args: data: Plaintext data to be encrypted (as a byte object). Returns: Ciphertext data (as a byte object). Raises: GOSTCipherError('GOSTCipherError: invalid plaintext data'): In case where the plaintext data is not byte object. """ result = bytearray() gamma = bytearray() data = super().encrypt(data) for i in range(self._get_num_block(data)): gamma = self._get_gamma() cipher_block = self._get_block(data, i) result = result + add_xor(gamma, cipher_block) self._set_init_vect(gamma[0:self.block_size]) if len(data) % self.block_size != 0: result = result + self._final_cipher(data) return result
def _hash_get_key(self, k: bytearray, i: int) -> bytearray: key = bytearray(_BLOCK_SIZE) key = add_xor(k, _C[i]) key = self._hash_s(key) key = self._hash_p(key) key = self._hash_l(key) return key
def encrypt(self, block: bytearray) -> bytearray: """ Encrypting a block of plaintext. Args: block: The block of plaintext to be encrypted (the block size is 16 bytes). Returns: The block of ciphertext. """ block = bytearray(block) for i in range(9): block = add_xor(self._cipher_iter_key[i], block) block = GOST34122015Kuznechik._cipher_s(block) block = GOST34122015Kuznechik._cipher_l(block) block = add_xor(self._cipher_iter_key[9], block) return block
def update(self, data: bytearray) -> None: """ Update the MAC object with the bytes-like object. Args: data: The data from which to get the MAC (as a byte object). Repeated calls are equivalent to a single call with the concatenation of all the arguments: 'm.update(a)'; 'm.update(b)' is equivalent to 'm.update(a+b)'. Raises: GOSTCipherError('GOSTCipherError: invalid text data'): In case where the text data is not byte object. """ if not isinstance(data, (bytes, bytearray)): self.clear() raise GOSTCipherError('GOSTCipherError: invalid text data') data = self._iter_buf + data block = bytearray() prev_block = self._cur_mac for i in range(0, self._get_num_block(data) - 1): block = self._cipher_obj.encrypt( add_xor(prev_block, self._get_block(data, i))) prev_block = block if self._get_pad_size(data) == 0: block = (self._cipher_obj.encrypt( add_xor(prev_block, data[len(data) - self.block_size:len(data)]))) self._cur_mac = block self._prev_mac = prev_block self._iter_buf = bytearray(b'') else: begin_data = len(data) - 2 * self.block_size + self._get_pad_size( data) end_data = len(data) - self.block_size + self._get_pad_size(data) block = (self._cipher_obj.encrypt( add_xor(prev_block, data[begin_data:end_data]))) self._cur_mac = block self._prev_mac = block self._iter_buf = data[len(data) - self.block_size + self._get_pad_size(data):] self._fin_buff = data[self.block_size * (self._get_num_block(data) - 1):]
def _cipher_g_iter(cipher_k: bytearray, cipher_a: bytearray) -> tuple: cipher_k = bytearray(cipher_k) cipher_a = bytearray(cipher_a) a_0 = bytearray(4) a_1 = bytearray(4) cipher_g = bytearray(4) a_1 = cipher_a[0:4] a_0 = cipher_a[4:_BLOCK_SIZE_MAGMA] cipher_g = GOST34122015Magma._cipher_g(cipher_k, a_0) cipher_g = add_xor(a_1, cipher_g) return a_0, a_1, cipher_g
def _get_mac_key(self, value_r): value_b = b'' if self.block_size == 16: value_b = _B_128 elif self.block_size == 8: value_b = _B_64 if msb(value_r) == 0: int_value = bytearray_to_int(value_r) << 1 key_1 = int_to_bytearray(int_value, self.block_size) else: int_value = bytearray_to_int(value_r) << 1 key_1 = add_xor(int_to_bytearray(int_value, self.block_size), value_b) if msb(key_1) == 0: int_value = bytearray_to_int(key_1) << 1 key_2 = int_to_bytearray(int_value, self.block_size) else: int_value = bytearray_to_int(key_1) << 1 key_2 = add_xor(int_to_bytearray(int_value, self.block_size), value_b) return [key_1, key_2]
def encrypt(self, data: bytearray) -> bytearray: """ Plaintext encryption in OFB mode. Parameters :data: Plaintext data to be encrypted (as a byte object). Return: ciphertext data (as a byte object). Exception: - GOSTCipherError('invalid plaintext data') - in case where the plaintext data is not byte object. """ if not isinstance(data, (bytes, bytearray)): self.clear() raise GOSTCipherError('invalid plaintext data') gamma = bytearray() result = bytearray() for i in range(get_num_block(data, self.block_size)): gamma = self._cipher_obj.encrypt( self._init_vect[0:self.block_size]) cipher_blk = add_xor( gamma, data[self.block_size * i:self.block_size + (self.block_size * i)]) result = result + cipher_blk self._init_vect[0:len(self._init_vect) - self.block_size] = ( self._init_vect[self.block_size:len(self._init_vect)]) self._init_vect[len(self._init_vect) - self.block_size:len(self._init_vect)] = ( gamma[0:self.block_size]) if len(data) % self.block_size != 0: gamma = self._cipher_obj.encrypt( self._init_vect[0:self.block_size]) cipher_blk = add_xor( gamma, data[self.block_size * get_num_block(data, self.block_size)::]) result = result + cipher_blk return result
def digest(self) -> bytearray: """ Return the HMAC message authentication code. This method is called after calling the 'update ()' method. Return: HMAC message authentication code as a byte object. """ fin_hasher_obj = GOST34112012(self._hasher_obj.name, data=b'') fin_hasher_obj.update( add_xor(self._key, _O_PAD) + self._hasher_obj.digest()) result = fin_hasher_obj.digest() fin_hasher_obj.reset() return result
def update(self, data: bytearray) -> None: """ Update the HMAC object with the bytes-like object. Parameters - data: the message for which want to calculate the authentication code. Repeated calls are equivalent to a single call with the concatenation of all the arguments: 'm.update(a)'; 'm.update(b)' is equivalent to 'm.update(a+b)'. Exception - GOSTHMACError('invalid data value'): in case where the data is not byte object. """ if not isinstance(data, (bytes, bytearray)): raise GOSTHMACError('invalid data value') self._counter = self._counter + 1 if self._counter == 1: self._hasher_obj.update(add_xor(self._key, _I_PAD) + data) elif self._counter != 1: self._hasher_obj.update(data)
def decrypt(self, data: bytearray) -> bytearray: """ Ciphertext decryption in CBC mode. Args: data: Ciphertext data to be decrypted (as a byte object). Returns: Plaintext data (as a byte object). Raises: GOSTCipherError('GOSTCipherError: invalid ciphertext data'): In case where the ciphertext data is not byte object. """ result = bytearray() data = GOST34132015CipherPadding.decrypt(self, data) for i in range(self._get_num_block(data)): internal = self._cipher_obj.decrypt(self._get_block(data, i)) cipher_block = add_xor(self._init_vect[0:self.block_size], internal) result = result + cipher_block self._set_init_vect(self._get_block(data, i)) return result
def _final_cipher(self, data): gamma = self._get_gamma() cipher_block = self._get_final_block(data) return add_xor(gamma, cipher_block)