# coding: utf-8 # PyGOST -- Pure Python GOST cryptographic functions library # 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, 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 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """Key wrap. :rfc:`4357` key wrapping (28147-89 and CryptoPro). """ from hmac import compare_digest from struct import pack from struct import unpack from pygost.gost28147 import cfb_encrypt from pygost.gost28147 import DEFAULT_SBOX from pygost.gost28147 import ecb_decrypt from pygost.gost28147 import ecb_encrypt from pygost.gost28147_mac import MAC from pygost.gost3413 import ctr from pygost.gost3413 import mac def wrap_gost(ukm, kek, cek, sbox=DEFAULT_SBOX): """28147-89 key wrapping :param ukm: UKM :type ukm: bytes, 8 bytes :param kek: key encryption key :type kek: bytes, 32 bytes :param cek: content encryption key :type cek: bytes, 32 bytes :returns: wrapped key :rtype: bytes, 44 bytes """ cek_mac = MAC(kek, data=cek, iv=ukm, sbox=sbox).digest()[:4] cek_enc = ecb_encrypt(kek, cek, sbox=sbox) return ukm + cek_enc + cek_mac def unwrap_gost(kek, data, sbox=DEFAULT_SBOX): """28147-89 key unwrapping :param kek: key encryption key :type kek: bytes, 32 bytes :param data: wrapped key :type data: bytes, 44 bytes :returns: unwrapped CEK :rtype: 32 bytes """ if len(data) != 44: raise ValueError("Invalid data length") ukm, cek_enc, cek_mac = data[:8], data[8:8 + 32], data[-4:] cek = ecb_decrypt(kek, cek_enc, sbox=sbox) if MAC(kek, data=cek, iv=ukm, sbox=sbox).digest()[:4] != cek_mac: raise ValueError("Invalid MAC") return cek def wrap_cryptopro(ukm, kek, cek, sbox=DEFAULT_SBOX): """CryptoPro key wrapping :param ukm: UKM :type ukm: bytes, 8 bytes :param kek: key encryption key :type kek: bytes, 32 bytes :param cek: content encryption key :type cek: bytes, 32 bytes :returns: wrapped key :rtype: bytes, 44 bytes """ return wrap_gost(ukm, diversify(kek, bytearray(ukm)), cek, sbox=sbox) def unwrap_cryptopro(kek, data, sbox=DEFAULT_SBOX): """CryptoPro key unwrapping :param kek: key encryption key :type kek: bytes, 32 bytes :param data: wrapped key :type data: bytes, 44 bytes :returns: unwrapped CEK :rtype: 32 bytes """ if len(data) < 8: raise ValueError("Invalid data length") return unwrap_gost( diversify(kek, bytearray(data[:8]), sbox=sbox), data, sbox=sbox, ) def diversify(kek, ukm, sbox=DEFAULT_SBOX): out = kek for i in range(8): s1, s2 = 0, 0 for j in range(8): k, = unpack("> j) & 1: s1 += k else: s2 += k iv = pack("