153 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			153 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # coding: utf-8
 | |
| # PyGOST -- Pure Python GOST cryptographic functions library
 | |
| # Copyright (C) 2015-2023 Sergey Matveev <stargrave@stargrave.org>
 | |
| #
 | |
| # 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 <http://www.gnu.org/licenses/>.
 | |
| """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), sbox=sbox),
 | |
|         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("<i", out[j * 4:j * 4 + 4])
 | |
|             if (ukm[i] >> j) & 1:
 | |
|                 s1 += k
 | |
|             else:
 | |
|                 s2 += k
 | |
|         iv = pack("<I", s1 % 2 ** 32) + pack("<I", s2 % 2 ** 32)
 | |
|         out = cfb_encrypt(out, out, iv=iv, sbox=sbox)
 | |
|     return out
 | |
| 
 | |
| 
 | |
| def kexp15(encrypter_key, encrypter_mac, bs, key, iv):
 | |
|     """KExp15 key exporting
 | |
| 
 | |
|     :param encrypter_key: encrypting function for key encryption,
 | |
|                           that takes block as an input
 | |
|     :param encrypter_mac: encrypting function for key authentication
 | |
|     :param int bs: cipher's blocksize, bytes
 | |
|     :param bytes key: key to export
 | |
|     :param bytes iv: half blocksize-sized initialization vector
 | |
|     """
 | |
|     key_mac = mac(encrypter_mac, bs, iv + key)
 | |
|     return ctr(encrypter_key, bs, key + key_mac, iv)
 | |
| 
 | |
| 
 | |
| def kimp15(encrypter_key, encrypter_mac, bs, kexp, iv):
 | |
|     """KImp15 key importing
 | |
| 
 | |
|     :param encrypter_key: encrypting function for key decryption,
 | |
|                           that takes block as an input
 | |
|     :param encrypter_mac: encrypting function for key authentication
 | |
|     :param int bs: cipher's blocksize, bytes
 | |
|     :param bytes kexp: key to import
 | |
|     :param bytes iv: half blocksize-sized initialization vector
 | |
|     """
 | |
|     key_and_key_mac = ctr(encrypter_key, bs, kexp, iv)
 | |
|     key, key_mac = key_and_key_mac[:-bs], key_and_key_mac[-bs:]
 | |
|     if not compare_digest(mac(encrypter_mac, bs, iv + key), key_mac):
 | |
|         raise ValueError("Invalid authentication tag")
 | |
|     return key
 |