2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2020 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 struct import pack
22 from struct import unpack
24 from pygost.gost28147 import cfb_encrypt
25 from pygost.gost28147 import DEFAULT_SBOX
26 from pygost.gost28147 import ecb_decrypt
27 from pygost.gost28147 import ecb_encrypt
28 from pygost.gost28147_mac import MAC
31 def wrap_gost(ukm, kek, cek, sbox=DEFAULT_SBOX):
32 """28147-89 key wrapping
35 :type ukm: bytes, 8 bytes
36 :param kek: key encryption key
37 :type kek: bytes, 32 bytes
38 :param cek: content encryption key
39 :type cek: bytes, 32 bytes
41 :rtype: bytes, 44 bytes
43 cek_mac = MAC(kek, data=cek, iv=ukm, sbox=sbox).digest()[:4]
44 cek_enc = ecb_encrypt(kek, cek, sbox=sbox)
45 return ukm + cek_enc + cek_mac
48 def unwrap_gost(kek, data, sbox=DEFAULT_SBOX):
49 """28147-89 key unwrapping
51 :param kek: key encryption key
52 :type kek: bytes, 32 bytes
53 :param data: wrapped key
54 :type data: bytes, 44 bytes
55 :returns: unwrapped CEK
59 raise ValueError("Invalid data length")
60 ukm, cek_enc, cek_mac = data[:8], data[8:8 + 32], data[-4:]
61 cek = ecb_decrypt(kek, cek_enc, sbox=sbox)
62 if MAC(kek, data=cek, iv=ukm, sbox=sbox).digest()[:4] != cek_mac:
63 raise ValueError("Invalid MAC")
67 def wrap_cryptopro(ukm, kek, cek, sbox=DEFAULT_SBOX):
68 """CryptoPro key wrapping
71 :type ukm: bytes, 8 bytes
72 :param kek: key encryption key
73 :type kek: bytes, 32 bytes
74 :param cek: content encryption key
75 :type cek: bytes, 32 bytes
77 :rtype: bytes, 44 bytes
79 return wrap_gost(ukm, diversify(kek, bytearray(ukm)), cek, sbox=sbox)
82 def unwrap_cryptopro(kek, data, sbox=DEFAULT_SBOX):
83 """CryptoPro key unwrapping
85 :param kek: key encryption key
86 :type kek: bytes, 32 bytes
87 :param data: wrapped key
88 :type data: bytes, 44 bytes
89 :returns: unwrapped CEK
93 raise ValueError("Invalid data length")
95 diversify(kek, bytearray(data[:8]), sbox=sbox),
101 def diversify(kek, ukm, sbox=DEFAULT_SBOX):
106 k, = unpack("<i", out[j * 4:j * 4 + 4])
107 if (ukm[i] >> j) & 1:
111 iv = pack("<I", s1 % 2 ** 32) + pack("<I", s2 % 2 ** 32)
112 out = cfb_encrypt(out, out, iv=iv, sbox=sbox)