]> Cypherpunks.ru repositories - pygost.git/blob - pygost/wrap.py
a3c206ca4c52a02c61e35fb55806fed0d5a36ae3
[pygost.git] / pygost / wrap.py
1 # coding: utf-8
2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2020 Sergey Matveev <stargrave@stargrave.org>
4 #
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.
8 #
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.
13 #
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/>.
16 """Key wrap.
17
18 :rfc:`4357` key wrapping (28147-89 and CryptoPro).
19 """
20
21 from struct import pack
22 from struct import unpack
23
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
29
30
31 def wrap_gost(ukm, kek, cek, sbox=DEFAULT_SBOX):
32     """28147-89 key wrapping
33
34     :param ukm: UKM
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
40     :returns: wrapped key
41     :rtype: bytes, 44 bytes
42     """
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
46
47
48 def unwrap_gost(kek, data, sbox=DEFAULT_SBOX):
49     """28147-89 key unwrapping
50
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
56     :rtype: 32 bytes
57     """
58     if len(data) != 44:
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")
64     return cek
65
66
67 def wrap_cryptopro(ukm, kek, cek, sbox=DEFAULT_SBOX):
68     """CryptoPro key wrapping
69
70     :param ukm: UKM
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
76     :returns: wrapped key
77     :rtype: bytes, 44 bytes
78     """
79     return wrap_gost(ukm, diversify(kek, bytearray(ukm)), cek, sbox=sbox)
80
81
82 def unwrap_cryptopro(kek, data, sbox=DEFAULT_SBOX):
83     """CryptoPro key unwrapping
84
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
90     :rtype: 32 bytes
91     """
92     if len(data) < 8:
93         raise ValueError("Invalid data length")
94     return unwrap_gost(
95         diversify(kek, bytearray(data[:8]), sbox=sbox),
96         data,
97         sbox=sbox,
98     )
99
100
101 def diversify(kek, ukm, sbox=DEFAULT_SBOX):
102     out = kek
103     for i in range(8):
104         s1, s2 = 0, 0
105         for j in range(8):
106             k, = unpack("<i", out[j * 4:j * 4 + 4])
107             if (ukm[i] >> j) & 1:
108                 s1 += k
109             else:
110                 s2 += k
111         iv = pack("<I", s1 % 2 ** 32) + pack("<I", s2 % 2 ** 32)
112         out = cfb_encrypt(out, out, iv=iv, sbox=sbox)
113     return out