2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2019 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, either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 :rfc:`4357` key wrapping (28147-89 and CryptoPro).
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
32 def wrap_gost(ukm, kek, cek, sbox=DEFAULT_SBOX):
33 """28147-89 key wrapping
36 :type ukm: bytes, 8 bytes
37 :param kek: key encryption key
38 :type kek: bytes, 32 bytes
39 :param cek: content encryption key
40 :type cek: bytes, 32 bytes
42 :rtype: bytes, 44 bytes
44 cek_mac = MAC(kek, data=cek, iv=ukm, sbox=sbox).digest()[:4]
45 cek_enc = ecb_encrypt(kek, cek, sbox=sbox)
46 return ukm + cek_enc + cek_mac
49 def unwrap_gost(kek, data, sbox=DEFAULT_SBOX):
50 """28147-89 key unwrapping
52 :param kek: key encryption key
53 :type kek: bytes, 32 bytes
54 :param data: wrapped key
55 :type data: bytes, 44 bytes
56 :returns: unwrapped CEK
60 raise ValueError("Invalid data length")
61 ukm, cek_enc, cek_mac = data[:8], data[8:8 + 32], data[-4:]
62 cek = ecb_decrypt(kek, cek_enc, sbox=sbox)
63 if MAC(kek, data=cek, iv=ukm, sbox=sbox).digest()[:4] != cek_mac:
64 raise ValueError("Invalid MAC")
68 def wrap_cryptopro(ukm, kek, cek, sbox=DEFAULT_SBOX):
69 """CryptoPro key wrapping
72 :type ukm: bytes, 8 bytes
73 :param kek: key encryption key
74 :type kek: bytes, 32 bytes
75 :param cek: content encryption key
76 :type cek: bytes, 32 bytes
78 :rtype: bytes, 44 bytes
80 return wrap_gost(ukm, diversify(kek, bytearray(ukm)), cek, sbox=sbox)
83 def unwrap_cryptopro(kek, data, sbox=DEFAULT_SBOX):
84 """CryptoPro key unwrapping
86 :param kek: key encryption key
87 :type kek: bytes, 32 bytes
88 :param data: wrapped key
89 :type data: bytes, 44 bytes
90 :returns: unwrapped CEK
94 raise ValueError("Invalid data length")
96 diversify(kek, bytearray(data[:8]), sbox=sbox),
102 def diversify(kek, ukm, sbox=DEFAULT_SBOX):
107 k, = unpack("<i", out[j * 4:j * 4 + 4])
108 if (ukm[i] >> j) & 1:
112 iv = pack("<I", s1 % 2 ** 32) + pack("<I", s2 % 2 ** 32)
113 out = cfb_encrypt(out, out, iv=iv, sbox=sbox)