]> Cypherpunks.ru repositories - pygost.git/blob - pygost/wrap.py
e7d7f7cf007ae682d4f27fdc73238787f7f859a7
[pygost.git] / pygost / wrap.py
1 # coding: utf-8
2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2018 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, either version 3 of the License, or
8 # (at your option) any later version.
9 #
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.
14 #
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/>.
17 """Key wrap.
18
19 :rfc:`4357` key wrapping (28147-89 and CryptoPro).
20 """
21
22 from struct import pack
23 from struct import unpack
24
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
29
30
31 def wrap_gost(ukm, kek, cek):
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).digest()[:4]
44     cek_enc = ecb_encrypt(kek, cek)
45     return ukm + cek_enc + cek_mac
46
47
48 def unwrap_gost(kek, data):
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)
62     if MAC(kek, data=cek, iv=ukm).digest()[:4] != cek_mac:
63         raise ValueError("Invalid MAC")
64     return cek
65
66
67 def wrap_cryptopro(ukm, kek, cek):
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)
80
81
82 def unwrap_cryptopro(kek, data):
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(diversify(kek, bytearray(data[:8])), data)
95
96
97 def diversify(kek, ukm):
98     out = kek
99     for i in range(8):
100         s1, s2 = 0, 0
101         for j in range(8):
102             k, = unpack("<i", out[j * 4:j * 4 + 4])
103             if (ukm[i] >> j) & 1:
104                 s1 += k
105             else:
106                 s2 += k
107         iv = pack("<I", s1 % 2 ** 32) + pack("<I", s2 % 2 ** 32)
108         out = cfb_encrypt(out, out, iv=iv)
109     return out