2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2018 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/>.
18 from base64 import b64decode
19 from hmac import new as hmac_new
20 from unittest import skipIf
21 from unittest import TestCase
23 from pygost.gost28147 import cfb_decrypt
24 from pygost.gost34112012512 import GOST34112012512
25 from pygost.gost34112012512 import pbkdf2 as gost34112012_pbkdf2
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
37 pyderasn_exists = False
39 pyderasn_exists = True
42 @skipIf(not pyderasn_exists, "PyDERASN dependency is required")
43 class TestPFX(TestCase):
44 """PFX test vectors from "Транспортный ключевой контейнер" (R50.1.112-2016.pdf)
46 pfx_raw = b64decode("""
47 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
79 password = u'Пароль для PFX'
81 def test_shrouded_key_bag(self):
82 private_key_info_expected = b64decode(b"""
83 MGYCAQAwHwYIKoUDBwEBAQEwEwYHKoUDAgIjAQYIKoUDBwEBAgIEQEYbRu86z+1JFKDcPDN9UbTG
84 G2ki9enTqos4KpUU0j9IDpl1UXiaA1YDIwUjlAp+81GkLmyt8Fw6Gt/X5JZySAY=
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"]),
92 self.assertSequenceEqual(tail, b"")
93 outer_safe_contents = octet_string_safe_contents["safeContents"]
95 octet_string_safe_contents, tail = OctetStringSafeContents().decode(
96 bytes(outer_safe_contents[0]["bagValue"]),
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"]),
103 self.assertSequenceEqual(tail, b"")
104 pbes2_params, tail = PBES2Params().decode(
105 bytes(shrouded_key_bag["encryptionAlgorithm"]["parameters"]),
107 self.assertSequenceEqual(tail, b"")
108 pbkdf2_params, tail = PBKDF2Params().decode(
109 bytes(pbes2_params["keyDerivationFunc"]["parameters"]),
111 self.assertSequenceEqual(tail, b"")
112 enc_scheme_params, tail = Gost2814789Parameters().decode(
113 bytes(pbes2_params["encryptionScheme"]["parameters"]),
115 self.assertSequenceEqual(tail, b"")
117 key = gost34112012_pbkdf2(
118 password=self.password.encode("utf-8"),
119 salt=bytes(pbkdf2_params["salt"]["specified"]),
120 iterations=int(pbkdf2_params["iterationCount"]),
123 # key = hexdec("309dd0354c5603739403f2335e9e2055138f8b5c98b63009de0635eea1fd7ba8")
124 self.assertSequenceEqual(
127 bytes(shrouded_key_bag["encryptedData"]),
128 iv=bytes(enc_scheme_params["iv"]),
129 sbox="Gost28147_tc26_ParamZ",
131 private_key_info_expected,
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
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"]),
158 self.assertSequenceEqual(tail, b"")
159 outer_safe_contents = octet_string_safe_contents["safeContents"]
161 encrypted_data, tail = EncryptedData().decode(
162 bytes(outer_safe_contents[1]["bagValue"]),
164 self.assertSequenceEqual(tail, b"")
165 pbes2_params, _ = PBES2Params().decode(
166 bytes(encrypted_data["encryptedContentInfo"]["contentEncryptionAlgorithm"]["parameters"]),
168 self.assertSequenceEqual(tail, b"")
169 pbkdf2_params, tail = PBKDF2Params().decode(
170 bytes(pbes2_params["keyDerivationFunc"]["parameters"]),
172 self.assertSequenceEqual(tail, b"")
173 enc_scheme_params, tail = Gost2814789Parameters().decode(
174 bytes(pbes2_params["encryptionScheme"]["parameters"]),
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"]),
183 # key = hexdec("0e93d71339e7f53b79a0bc41f9109dd4fb60b30ae10736c1bb77b84c07681cfc")
184 self.assertSequenceEqual(
187 bytes(encrypted_data["encryptedContentInfo"]["encryptedContent"]),
188 iv=bytes(enc_scheme_params["iv"]),
189 sbox="Gost28147_tc26_ParamZ",
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"]),
200 self.assertSequenceEqual(tail, b"")
201 outer_safe_contents = octet_string_safe_contents["safeContents"]
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"]),
210 # mac_key = hexdec("cadbfbf3bceaa9b79f651508fac5abbeb4a13d0bd0e1876bd3c3efb2112128a5")
211 self.assertSequenceEqual(
214 msg=outer_safe_contents.encode(),
215 digestmod=GOST34112012512,
217 bytes(mac_data["mac"]["digest"]),