]> Cypherpunks.ru repositories - pygost.git/blob - pygost/wrap.py
pygost.wrap.* Sbox specifying
[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 DEFAULT_SBOX
27 from pygost.gost28147 import ecb_decrypt
28 from pygost.gost28147 import ecb_encrypt
29 from pygost.gost28147_mac import MAC
30
31
32 def wrap_gost(ukm, kek, cek, sbox=DEFAULT_SBOX):
33     """28147-89 key wrapping
34
35     :param ukm: UKM
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
41     :returns: wrapped key
42     :rtype: bytes, 44 bytes
43     """
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
47
48
49 def unwrap_gost(kek, data, sbox=DEFAULT_SBOX):
50     """28147-89 key unwrapping
51
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
57     :rtype: 32 bytes
58     """
59     if len(data) != 44:
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")
65     return cek
66
67
68 def wrap_cryptopro(ukm, kek, cek, sbox=DEFAULT_SBOX):
69     """CryptoPro key wrapping
70
71     :param ukm: UKM
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
77     :returns: wrapped key
78     :rtype: bytes, 44 bytes
79     """
80     return wrap_gost(ukm, diversify(kek, bytearray(ukm)), cek, sbox=sbox)
81
82
83 def unwrap_cryptopro(kek, data, sbox=DEFAULT_SBOX):
84     """CryptoPro key unwrapping
85
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
91     :rtype: 32 bytes
92     """
93     if len(data) < 8:
94         raise ValueError("Invalid data length")
95     return unwrap_gost(
96         diversify(kek, bytearray(data[:8]), sbox=sbox),
97         data,
98         sbox=sbox,
99     )
100
101
102 def diversify(kek, ukm, sbox=DEFAULT_SBOX):
103     out = kek
104     for i in range(8):
105         s1, s2 = 0, 0
106         for j in range(8):
107             k, = unpack("<i", out[j * 4:j * 4 + 4])
108             if (ukm[i] >> j) & 1:
109                 s1 += k
110             else:
111                 s2 += k
112         iv = pack("<I", s1 % 2 ** 32) + pack("<I", s2 % 2 ** 32)
113         out = cfb_encrypt(out, out, iv=iv, sbox=sbox)
114     return out