Coverage for tests/admin/test_x509_utils.py: 100%
117 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-10-30 06:22 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-10-30 06:22 +0000
1# The MIT License (MIT)
2#
3# Copyright (c) 2021 RSK Labs Ltd
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy of
6# this software and associated documentation files (the "Software"), to deal in
7# the Software without restriction, including without limitation the rights to
8# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9# of the Software, and to permit persons to whom the Software is furnished to do
10# so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
24from unittest import TestCase
25from unittest.mock import patch, Mock
26from parameterized import parameterized
27from admin.x509_utils import split_pem_certificates, get_intel_pcs_x509_crl
28import logging
30logging.disable(logging.CRITICAL)
33class TestSplitPemCertificates(TestCase):
34 def test_splits_ok(self):
35 test_certs = """
36-----BEGIN CERTIFICATE-----
37something
38-----END CERTIFICATE-----
39-----BEGIN CERTIFICATE-----somethingelse-----END CERTIFICATE-----
40-----BEGIN CERTIFICATE-----whatis
41thisstuff
42-----END CERTIFICATE-----
43"""
44 certs = split_pem_certificates(test_certs)
45 self.assertEqual(3, len(certs))
46 self.assertEqual(
47 "-----BEGIN CERTIFICATE-----something-----END CERTIFICATE-----",
48 certs[0].strip().replace("\n", ""))
49 self.assertEqual(
50 "-----BEGIN CERTIFICATE-----somethingelse-----END CERTIFICATE-----",
51 certs[1].strip().replace("\n", ""))
52 self.assertEqual(
53 "-----BEGIN CERTIFICATE-----whatisthisstuff-----END CERTIFICATE-----",
54 certs[2].strip().replace("\n", ""))
56 def test_nocerts(self):
57 self.assertEqual([], split_pem_certificates("not a certificate in sight"))
59 def test_certs_pref_suf(self):
60 self.assertEqual(
61 ["-----BEGIN CERTIFICATE-----"
62 "something-----END CERTIFICATE-----"],
63 split_pem_certificates(
64 "prefix\n\n\n-----BEGIN CERTIFICATE-----"
65 "something-----END CERTIFICATE-----\n\nsuffix\n\r\tmorestuff"
66 ))
69@patch("admin.x509_utils.url_unquote")
70@patch("admin.x509_utils.split_pem_certificates")
71@patch("admin.x509_utils.x509.load_der_x509_crl")
72@patch("admin.x509_utils.x509.load_pem_x509_crl")
73@patch("admin.x509_utils.requests")
74class TestGetIntelPcsX509CRL(TestCase):
75 def test_ok_pem(self, requests, load_pem, load_der, split, unquote):
76 res = Mock()
77 requests.get.return_value = res
79 res.status_code = 200
80 res.content = "the-crl-content"
81 res.headers = {
82 "Content-Type": "application/x-pem-file"
83 }
84 load_pem.return_value = "the-parsed-certificate"
86 self.assertEqual({
87 "crl": "the-parsed-certificate",
88 "issuer_chain": None,
89 "warning": None,
90 }, get_intel_pcs_x509_crl("the-crl-url"))
92 load_pem.assert_called_with("the-crl-content")
93 load_der.assert_not_called()
94 split.assert_not_called()
95 unquote.assert_not_called()
97 @parameterized.expand([
98 ("header 1", "application/pkix-crl"),
99 ("header 2", "application/x-x509-ca-cert"),
100 ])
101 def test_ok_der(self, requests, load_pem, load_der, split, unquote, _, ctype):
102 res = Mock()
103 requests.get.return_value = res
105 res.status_code = 200
106 res.content = "the-crl-content"
107 res.headers = {
108 "Content-Type": ctype,
109 }
110 load_der.return_value = "the-parsed-certificate"
112 self.assertEqual({
113 "crl": "the-parsed-certificate",
114 "issuer_chain": None,
115 "warning": None,
116 }, get_intel_pcs_x509_crl("the-crl-url"))
118 load_der.assert_called_with("the-crl-content")
119 load_pem.assert_not_called()
120 split.assert_not_called()
121 unquote.assert_not_called()
123 def test_ok_warning(self, requests, load_pem, load_der, split, unquote):
124 res = Mock()
125 requests.get.return_value = res
127 res.status_code = 200
128 res.content = "the-crl-content"
129 res.headers = {
130 "Content-Type": "application/x-pem-file",
131 "warning": "this-is-a-warning",
132 }
133 load_pem.return_value = "the-parsed-certificate"
135 self.assertEqual({
136 "crl": "the-parsed-certificate",
137 "issuer_chain": None,
138 "warning": "Getting the-crl-url: this-is-a-warning",
139 }, get_intel_pcs_x509_crl("the-crl-url"))
141 load_pem.assert_called_with("the-crl-content")
142 load_der.assert_not_called()
143 split.assert_not_called()
144 unquote.assert_not_called()
146 @patch("admin.x509_utils.x509.load_pem_x509_certificate")
147 def test_ok_issuer_chain(self, loadcer, requests, load_pem, load_der, split, unquote):
148 res = Mock()
149 requests.get.return_value = res
151 res.status_code = 200
152 res.content = "the-crl-content"
153 res.headers = {
154 "Content-Type": "application/x-x509-ca-cert",
155 "SGX-PCK-CRL-Issuer-Chain": "chain0-chain1-chain2",
156 }
157 load_der.return_value = "the-parsed-certificate"
158 loadcer.side_effect = lambda s: f"parsed-cert-{s.decode()}"
159 split.side_effect = lambda s: s.split(",")
160 unquote.side_effect = lambda s: s.replace("-", ",")
162 self.assertEqual({
163 "crl": "the-parsed-certificate",
164 "issuer_chain": [
165 "parsed-cert-chain0",
166 "parsed-cert-chain1",
167 "parsed-cert-chain2",
168 ],
169 "warning": None,
170 }, get_intel_pcs_x509_crl("the-crl-url"))
172 load_der.assert_called_with("the-crl-content")
173 load_pem.assert_not_called()
174 unquote.assert_called_with("chain0-chain1-chain2")
175 split.assert_called_with("chain0,chain1,chain2")
176 self.assertEqual(3, loadcer.call_count)
178 def test_error_response(self, requests, load_pem, load_der, split, unquote):
179 res = Mock()
180 requests.get.return_value = res
182 res.status_code = 404
184 with self.assertRaises(RuntimeError) as e:
185 get_intel_pcs_x509_crl("the-crl-url")
186 self.assertIn("Error fetching", str(e.exception))
188 load_pem.assert_not_called()
189 load_der.assert_not_called()
190 split.assert_not_called()
191 unquote.assert_not_called()
193 def test_error_unknown_ctype(self, requests, load_pem, load_der, split, unquote):
194 res = Mock()
195 requests.get.return_value = res
197 res.status_code = 200
198 res.headers = {
199 "Content-Type": "not-known"
200 }
202 with self.assertRaises(RuntimeError) as e:
203 get_intel_pcs_x509_crl("the-crl-url")
204 self.assertIn("While", str(e.exception))
205 self.assertIn("Unknown", str(e.exception))
207 load_pem.assert_not_called()
208 load_der.assert_not_called()
209 split.assert_not_called()
210 unquote.assert_not_called()
212 @parameterized.expand([
213 ("header 1", "application/x-pem-file", "pem"),
214 ("header 2", "application/pkix-crl", "der"),
215 ("header 3", "application/x-x509-ca-cert", "der"),
216 ])
217 def test_error_parsing(self, requests, load_pem, load_der,
218 split, unquote, _, ctype, errct):
219 res = Mock()
220 requests.get.return_value = res
222 res.status_code = 200
223 res.content = "some-content"
224 res.headers = {
225 "Content-Type": ctype,
226 }
228 load_pem.side_effect = ValueError("pem parsing issue")
229 load_der.side_effect = ValueError("der parsing issue")
231 with self.assertRaises(RuntimeError) as e:
232 get_intel_pcs_x509_crl("the-crl-url")
233 self.assertIn("While", str(e.exception))
234 self.assertIn(f"{errct} parsing", str(e.exception))
236 split.assert_not_called()
237 unquote.assert_not_called()