2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2021 Sergey Matveev <stargrave@stargrave.org>
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, version 3 of the License.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 :rfc:`4357` key wrapping (28147-89 and CryptoPro).
21 from hmac import compare_digest
22 from struct import pack
23 from struct import unpack
25 from pygost.gost28147 import cfb_encrypt
26 from pygost.gost28147 import DEFAULT_SBOX
27 from pygost.gost28147 import ecb_decrypt
28 from pygost.gost28147 import ecb_encrypt
29 from pygost.gost28147_mac import MAC
30 from pygost.gost3413 import ctr
31 from pygost.gost3413 import mac
34 def wrap_gost(ukm, kek, cek, sbox=DEFAULT_SBOX):
35 """28147-89 key wrapping
38 :type ukm: bytes, 8 bytes
39 :param kek: key encryption key
40 :type kek: bytes, 32 bytes
41 :param cek: content encryption key
42 :type cek: bytes, 32 bytes
44 :rtype: bytes, 44 bytes
46 cek_mac = MAC(kek, data=cek, iv=ukm, sbox=sbox).digest()[:4]
47 cek_enc = ecb_encrypt(kek, cek, sbox=sbox)
48 return ukm + cek_enc + cek_mac
51 def unwrap_gost(kek, data, sbox=DEFAULT_SBOX):
52 """28147-89 key unwrapping
54 :param kek: key encryption key
55 :type kek: bytes, 32 bytes
56 :param data: wrapped key
57 :type data: bytes, 44 bytes
58 :returns: unwrapped CEK
62 raise ValueError("Invalid data length")
63 ukm, cek_enc, cek_mac = data[:8], data[8:8 + 32], data[-4:]
64 cek = ecb_decrypt(kek, cek_enc, sbox=sbox)
65 if MAC(kek, data=cek, iv=ukm, sbox=sbox).digest()[:4] != cek_mac:
66 raise ValueError("Invalid MAC")
70 def wrap_cryptopro(ukm, kek, cek, sbox=DEFAULT_SBOX):
71 """CryptoPro key wrapping
74 :type ukm: bytes, 8 bytes
75 :param kek: key encryption key
76 :type kek: bytes, 32 bytes
77 :param cek: content encryption key
78 :type cek: bytes, 32 bytes
80 :rtype: bytes, 44 bytes
82 return wrap_gost(ukm, diversify(kek, bytearray(ukm)), cek, sbox=sbox)
85 def unwrap_cryptopro(kek, data, sbox=DEFAULT_SBOX):
86 """CryptoPro key unwrapping
88 :param kek: key encryption key
89 :type kek: bytes, 32 bytes
90 :param data: wrapped key
91 :type data: bytes, 44 bytes
92 :returns: unwrapped CEK
96 raise ValueError("Invalid data length")
98 diversify(kek, bytearray(data[:8]), sbox=sbox),
104 def diversify(kek, ukm, sbox=DEFAULT_SBOX):
109 k, = unpack("<i", out[j * 4:j * 4 + 4])
110 if (ukm[i] >> j) & 1:
114 iv = pack("<I", s1 % 2 ** 32) + pack("<I", s2 % 2 ** 32)
115 out = cfb_encrypt(out, out, iv=iv, sbox=sbox)
119 def kexp15(encrypter_key, encrypter_mac, bs, key, iv):
120 """KExp15 key exporting
122 :param encrypter_key: encrypting function for key encryption,
123 that takes block as an input
124 :param encrypter_mac: encrypting function for key authentication
125 :param int bs: cipher's blocksize, bytes
126 :param bytes key: key to export
127 :param bytes iv: half blocksize-sized initialization vector
129 key_mac = mac(encrypter_mac, bs, iv + key)
130 return ctr(encrypter_key, bs, key + key_mac, iv)
133 def kimp15(encrypter_key, encrypter_mac, bs, kexp, iv):
134 """KImp15 key importing
136 :param encrypter_key: encrypting function for key decryption,
137 that takes block as an input
138 :param encrypter_mac: encrypting function for key authentication
139 :param int bs: cipher's blocksize, bytes
140 :param bytes kexp: key to import
141 :param bytes iv: half blocksize-sized initialization vector
143 key_and_key_mac = ctr(encrypter_key, bs, kexp, iv)
144 key, key_mac = key_and_key_mac[:-bs], key_and_key_mac[-bs:]
145 if not compare_digest(mac(encrypter_mac, bs, iv + key), key_mac):
146 raise ValueError("Invalid authentication tag")