]> Cypherpunks.ru repositories - pygost.git/blob - pygost/test_pfx.py
More TC26 ASN.1 test vectors
[pygost.git] / pygost / test_pfx.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
18 from base64 import b64decode
19 from hmac import new as hmac_new
20 from unittest import skipIf
21 from unittest import TestCase
22
23 from pygost.gost28147 import cfb_decrypt
24 from pygost.gost34112012512 import GOST34112012512
25 from pygost.gost34112012512 import pbkdf2 as gost34112012_pbkdf2
26
27
28 try:
29     from pygost.asn1schemas.cms import Gost2814789Parameters
30     from pygost.asn1schemas.pfx import EncryptedData
31     from pygost.asn1schemas.pfx import OctetStringSafeContents
32     from pygost.asn1schemas.pfx import PBES2Params
33     from pygost.asn1schemas.pfx import PBKDF2Params
34     from pygost.asn1schemas.pfx import PFX
35     from pygost.asn1schemas.pfx import PKCS8ShroudedKeyBag
36 except ImportError:
37     pyderasn_exists = False
38 else:
39     pyderasn_exists = True
40
41
42 @skipIf(not pyderasn_exists, "PyDERASN dependency is required")
43 class TestPFX(TestCase):
44     """PFX test vectors from "Транспортный ключевой контейнер" (R50.1.112-2016.pdf)
45     """
46     pfx_raw = b64decode("""
47 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
77 mwcl1xQV2G5/fgICB9A=
78     """)
79     password = u'Пароль для PFX'
80
81     def test_shrouded_key_bag(self):
82         private_key_info_expected = b64decode(b"""
83 MGYCAQAwHwYIKoUDBwEBAQEwEwYHKoUDAgIjAQYIKoUDBwEBAgIEQEYbRu86z+1JFKDcPDN9UbTG
84 G2ki9enTqos4KpUU0j9IDpl1UXiaA1YDIwUjlAp+81GkLmyt8Fw6Gt/X5JZySAY=
85         """)
86
87         pfx, tail = PFX().decode(self.pfx_raw)
88         self.assertSequenceEqual(tail, b"")
89         octet_string_safe_contents, tail = OctetStringSafeContents().decode(
90             bytes(pfx["authSafe"]["content"]),
91         )
92         self.assertSequenceEqual(tail, b"")
93         outer_safe_contents = octet_string_safe_contents["safeContents"]
94
95         octet_string_safe_contents, tail = OctetStringSafeContents().decode(
96             bytes(outer_safe_contents[0]["bagValue"]),
97         )
98         self.assertSequenceEqual(tail, b"")
99         safe_bag = octet_string_safe_contents["safeContents"][0]
100         shrouded_key_bag, tail = PKCS8ShroudedKeyBag().decode(
101             bytes(safe_bag["bagValue"]),
102         )
103         self.assertSequenceEqual(tail, b"")
104         pbes2_params, tail = PBES2Params().decode(
105             bytes(shrouded_key_bag["encryptionAlgorithm"]["parameters"]),
106         )
107         self.assertSequenceEqual(tail, b"")
108         pbkdf2_params, tail = PBKDF2Params().decode(
109             bytes(pbes2_params["keyDerivationFunc"]["parameters"]),
110         )
111         self.assertSequenceEqual(tail, b"")
112         enc_scheme_params, tail = Gost2814789Parameters().decode(
113             bytes(pbes2_params["encryptionScheme"]["parameters"]),
114         )
115         self.assertSequenceEqual(tail, b"")
116
117         key = gost34112012_pbkdf2(
118             password=self.password.encode("utf-8"),
119             salt=bytes(pbkdf2_params["salt"]["specified"]),
120             iterations=int(pbkdf2_params["iterationCount"]),
121             dklen=32,
122         )
123         # key = hexdec("309dd0354c5603739403f2335e9e2055138f8b5c98b63009de0635eea1fd7ba8")
124         self.assertSequenceEqual(
125             cfb_decrypt(
126                 key,
127                 bytes(shrouded_key_bag["encryptedData"]),
128                 iv=bytes(enc_scheme_params["iv"]),
129                 sbox="Gost28147_tc26_ParamZ",
130             ),
131             private_key_info_expected,
132         )
133
134     def test_encrypted_data(self):
135         cert_bag_expected = b64decode(b"""
136 MIIDSjCCA0YGCyqGSIb3DQEMCgEDoIIDHjCCAxoGCiqGSIb3DQEJFgGgggMKBIIDBjCCAwIwggKt
137 oAMCAQICEAHQaF8xH5bAAAAACycJAAEwDAYIKoUDBwEBAwIFADBgMQswCQYDVQQGEwJSVTEVMBMG
138 A1UEBwwM0JzQvtGB0LrQstCwMQ8wDQYDVQQKDAbQotCaMjYxKTAnBgNVBAMMIENBIGNlcnRpZmlj
139 YXRlIChQS0NTIzEyIGV4YW1wbGUpMB4XDTE1MDMyNzA3MjUwMFoXDTIwMDMyNzA3MjMwMFowZDEL
140 MAkGA1UEBhMCUlUxFTATBgNVBAcMDNCc0L7RgdC60LLQsDEPMA0GA1UECgwG0KLQmjI2MS0wKwYD
141 VQQDDCRUZXN0IGNlcnRpZmljYXRlIDEgKFBLQ1MjMTIgZXhhbXBsZSkwZjAfBggqhQMHAQEBATAT
142 BgcqhQMCAiMBBggqhQMHAQECAgNDAARA1xzymkpvr2dYJT8WTOX3Dt96/+hGsXNytUQpkWB5ImJM
143 4tg9AsC4RIUwV5H41MhG0uBRFweTzN6AsAdBvhTClYEJADI3MDkwMDAxo4IBKTCCASUwKwYDVR0Q
144 BCQwIoAPMjAxNTAzMjcwNzI1MDBagQ8yMDE2MDMyNzA3MjUwMFowDgYDVR0PAQH/BAQDAgTwMB0G
145 A1UdDgQWBBQhWOsRQ68yYN2Utg/owHoWcqsVbTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
146 AwQwDAYDVR0TAQH/BAIwADCBmQYDVR0jBIGRMIGOgBQmnc7Xh5ykb5t/BMwOkxA4drfEmqFkpGIw
147 YDELMAkGA1UEBhMCUlUxFTATBgNVBAcMDNCc0L7RgdC60LLQsDEPMA0GA1UECgwG0KLQmjI2MSkw
148 JwYDVQQDDCBDQSBjZXJ0aWZpY2F0ZSAoUEtDUyMxMiBleGFtcGxlKYIQAdBoXvL8TSAAAAALJwkA
149 ATAMBggqhQMHAQEDAgUAA0EA9oq0Vvk8kkgIwkp0x0J5eKtia4MNTiwKAm7jgnCZIx3O98BThaTX
150 3ZQhEo2RL9pTCPr6wFMheeJ+YdGMReXvsjEVMBMGCSqGSIb3DQEJFTEGBAQBAAAA
151         """)
152
153         pfx, tail = PFX().decode(self.pfx_raw)
154         self.assertSequenceEqual(tail, b"")
155         octet_string_safe_contents, tail = OctetStringSafeContents().decode(
156             bytes(pfx["authSafe"]["content"]),
157         )
158         self.assertSequenceEqual(tail, b"")
159         outer_safe_contents = octet_string_safe_contents["safeContents"]
160
161         encrypted_data, tail = EncryptedData().decode(
162             bytes(outer_safe_contents[1]["bagValue"]),
163         )
164         self.assertSequenceEqual(tail, b"")
165         pbes2_params, _ = PBES2Params().decode(
166             bytes(encrypted_data["encryptedContentInfo"]["contentEncryptionAlgorithm"]["parameters"]),
167         )
168         self.assertSequenceEqual(tail, b"")
169         pbkdf2_params, tail = PBKDF2Params().decode(
170             bytes(pbes2_params["keyDerivationFunc"]["parameters"]),
171         )
172         self.assertSequenceEqual(tail, b"")
173         enc_scheme_params, tail = Gost2814789Parameters().decode(
174             bytes(pbes2_params["encryptionScheme"]["parameters"]),
175         )
176         self.assertSequenceEqual(tail, b"")
177         key = gost34112012_pbkdf2(
178             password=self.password.encode("utf-8"),
179             salt=bytes(pbkdf2_params["salt"]["specified"]),
180             iterations=int(pbkdf2_params["iterationCount"]),
181             dklen=32,
182         )
183         # key = hexdec("0e93d71339e7f53b79a0bc41f9109dd4fb60b30ae10736c1bb77b84c07681cfc")
184         self.assertSequenceEqual(
185             cfb_decrypt(
186                 key,
187                 bytes(encrypted_data["encryptedContentInfo"]["encryptedContent"]),
188                 iv=bytes(enc_scheme_params["iv"]),
189                 sbox="Gost28147_tc26_ParamZ",
190             ),
191             cert_bag_expected,
192         )
193
194     def test_mac(self):
195         pfx, tail = PFX().decode(self.pfx_raw)
196         self.assertSequenceEqual(tail, b"")
197         octet_string_safe_contents, tail = OctetStringSafeContents().decode(
198             bytes(pfx["authSafe"]["content"]),
199         )
200         self.assertSequenceEqual(tail, b"")
201         outer_safe_contents = octet_string_safe_contents["safeContents"]
202
203         mac_data = pfx["macData"]
204         mac_key = gost34112012_pbkdf2(
205             password=self.password.encode('utf-8'),
206             salt=bytes(mac_data["macSalt"]),
207             iterations=int(mac_data["iterations"]),
208             dklen=96,
209         )[-32:]
210         # mac_key = hexdec("cadbfbf3bceaa9b79f651508fac5abbeb4a13d0bd0e1876bd3c3efb2112128a5")
211         self.assertSequenceEqual(
212             hmac_new(
213                 key=mac_key,
214                 msg=outer_safe_contents.encode(),
215                 digestmod=GOST34112012512,
216             ).digest(),
217             bytes(mac_data["mac"]["digest"]),
218         )