2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2016 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 ecb_decrypt
27 from pygost.gost28147 import ecb_encrypt
28 from pygost.gost28147_mac import MAC
31 def wrap_gost(ukm, kek, cek):
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).digest()[:4]
44 cek_enc = ecb_encrypt(kek, cek)
45 return ukm + cek_enc + cek_mac
48 def unwrap_gost(kek, data):
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 :return: 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)
62 if MAC(kek, data=cek, iv=ukm).digest()[:4] != cek_mac:
63 raise ValueError("Invalid MAC")
67 def wrap_cryptopro(ukm, kek, cek):
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)
82 def unwrap_cryptopro(kek, data):
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 :return: unwrapped CEK
93 raise ValueError("Invalid data length")
94 return unwrap_gost(diversify(kek, bytearray(data[:8])), data)
97 def diversify(kek, ukm):
102 k, = unpack("<i", out[j * 4:j * 4 + 4])
103 if (ukm[i] >> j) & 1:
107 iv = pack("<I", s1 % 2 ** 32) + pack("<I", s2 % 2 ** 32)
108 out = cfb_encrypt(out, out, iv=iv)