Coverage for tests/admin/test_sgx_attestation.py: 100%
117 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +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.
23import ecdsa
24from types import SimpleNamespace
25from unittest import TestCase
26from unittest.mock import Mock, patch, call
27from parameterized import parameterized
28from admin.sgx_attestation import do_attestation
29from admin.misc import AdminError
32@patch("sys.stdout")
33@patch("admin.sgx_attestation.HSMCertificateV2ElementX509")
34@patch("admin.sgx_attestation.HSMCertificateV2ElementSGXAttestationKey")
35@patch("admin.sgx_attestation.HSMCertificateV2ElementSGXQuote")
36@patch("admin.sgx_attestation.HSMCertificateV2")
37@patch("admin.sgx_attestation.SgxEnvelope")
38@patch("admin.sgx_attestation.do_unlock")
39@patch("admin.sgx_attestation.get_ud_value_for_attestation")
40@patch("admin.sgx_attestation.get_hsm")
41class TestSgxAttestation(TestCase):
42 def setUp(self):
43 options = SimpleNamespace()
44 options.output_file_path = "an-output-file"
45 options.no_unlock = False
46 options.verbose = "is-verbose"
47 options.attestation_ud_source = "an-ud-source"
48 self.options = options
50 def setupMocks(self, get_hsm, get_ud_value_for_attestation, do_unlock,
51 SgxEnvelope, HSMCertificateV2, HSMCertificateV2ElementSGXQuote,
52 HSMCertificateV2ElementSGXAttestationKey, HSMCertificateV2ElementX509):
53 self.get_hsm = get_hsm
54 self.get_ud_value_for_attestation = get_ud_value_for_attestation
55 self.do_unlock = do_unlock
56 self.SgxEnvelope = SgxEnvelope
57 self.HSMCertificateV2 = HSMCertificateV2
58 self.HSMCertificateV2ElementSGXQuote = HSMCertificateV2ElementSGXQuote
59 self.HSMCertificateV2ElementSGXAttestationKey = \
60 HSMCertificateV2ElementSGXAttestationKey
61 self.HSMCertificateV2ElementX509 = HSMCertificateV2ElementX509
63 self.hsm = Mock()
64 self.hsm.get_powhsm_attestation.return_value = {
65 "envelope": "11"*32,
66 "message": "22"*32,
67 }
68 quote = SimpleNamespace(**{"get_raw_data": lambda: b"quote-raw-data"})
69 sig = SimpleNamespace(**{"r": b"a"*32, "s": b"a"*32})
70 self.att_key = ecdsa.SigningKey.generate(curve=ecdsa.NIST256p).\
71 get_verifying_key()
72 att_key_str = self.att_key.to_string()
73 attkey = SimpleNamespace(**{"x": att_key_str[:32], "y": att_key_str[32:]})
74 qesig = SimpleNamespace(**{"r": b"c"*32, "s": b"c"*32})
75 qad = SimpleNamespace(**{
76 "signature": sig,
77 "attestation_key": attkey,
78 "qe_report_body": SimpleNamespace(**{
79 "get_raw_data": lambda: b"qerb-raw-data"}),
80 "qe_report_body_signature": qesig,
81 })
82 qead = SimpleNamespace(**{
83 "data": b"qead-data",
84 })
85 qecd = SimpleNamespace(**{
86 "certs": ["qecd-cert-0", "qecd-cert-1"],
87 })
88 envelope = SimpleNamespace(**{
89 "quote": quote,
90 "quote_auth_data": qad,
91 "qe_auth_data": qead,
92 "qe_cert_data": qecd,
93 "custom_message": b"a-custom-message",
94 })
95 self.SgxEnvelope.return_value = envelope
97 self.HSMCertificateV2ElementSGXQuote.return_value = "quote_elem"
98 self.HSMCertificateV2ElementSGXAttestationKey.return_value = "attkey_elem"
99 self.HSMCertificateV2ElementX509.side_effect = ["cert0_elem", "cert1_elem"]
101 get_hsm.return_value = self.hsm
102 get_ud_value_for_attestation.return_value = "some-random-value"
104 @parameterized.expand([
105 ("unlock", False),
106 ("no_unlock", True),
107 ])
108 def test_ok(self, *args):
109 self.setupMocks(*args[:-3])
110 self.options.no_unlock = args[-1]
112 do_attestation(self.options)
114 self.get_ud_value_for_attestation.assert_called_with("an-ud-source")
115 if self.options.no_unlock:
116 self.do_unlock.assert_not_called()
117 else:
118 self.do_unlock.assert_called_with(self.options, label=False)
119 self.get_hsm.assert_called_with("is-verbose")
120 self.hsm.get_powhsm_attestation.assert_called_with("some-random-value")
121 self.hsm.disconnect.assert_called()
122 self.SgxEnvelope.assert_called_with(
123 bytes.fromhex("11"*32),
124 bytes.fromhex("22"*32),
125 )
126 self.HSMCertificateV2ElementSGXQuote.assert_called_with({
127 "name": "quote",
128 "message": b"quote-raw-data".hex(),
129 "custom_data": b"a-custom-message".hex(),
130 "signature": "30440220"+"61"*32+"0220"+"61"*32,
131 "signed_by": "attestation",
132 })
133 self.HSMCertificateV2ElementSGXAttestationKey.assert_called_with({
134 "name": "attestation",
135 "message": b"qerb-raw-data".hex(),
136 "key": self.att_key.to_string("uncompressed").hex(),
137 "auth_data": b"qead-data".hex(),
138 "signature": "30440220"+"63"*32+"0220"+"63"*32,
139 "signed_by": "quoting_enclave",
140 })
141 self.HSMCertificateV2ElementX509.assert_has_calls([
142 call({
143 "name": "quoting_enclave",
144 "message": "qecd-cert-0",
145 "signed_by": "platform_ca",
146 }),
147 call({
148 "name": "platform_ca",
149 "message": "qecd-cert-1",
150 "signed_by": "sgx_root",
151 })
152 ])
153 cert = self.HSMCertificateV2.return_value
154 cert.add_element.assert_has_calls([
155 call("quote_elem"),
156 call("attkey_elem"),
157 call("cert0_elem"),
158 call("cert1_elem")
159 ])
160 cert.save_to_jsonfile.assert_called_with("an-output-file")
162 def test_no_output_path(self, *args):
163 self.setupMocks(*args[:-1])
164 self.options.output_file_path = None
166 with self.assertRaises(AdminError) as e:
167 do_attestation(self.options)
168 self.assertIn("output file", str(e.exception))
170 self.get_ud_value_for_attestation.assert_not_called()
171 self.get_hsm.assert_not_called()
172 self.hsm.get_powhsm_attestation.assert_not_called()
173 self.do_unlock.assert_not_called()
174 self.SgxEnvelope.assert_not_called()
175 self.HSMCertificateV2.assert_not_called()
176 self.HSMCertificateV2ElementSGXQuote.assert_not_called()
177 self.HSMCertificateV2ElementSGXAttestationKey.assert_not_called()
178 self.HSMCertificateV2ElementX509.assert_not_called()
180 def test_adm_err_get_attestation(self, *args):
181 self.setupMocks(*args[:-1])
183 self.hsm.get_powhsm_attestation.side_effect = RuntimeError("an error")
185 with self.assertRaises(RuntimeError) as e:
186 do_attestation(self.options)
187 self.assertIn("an error", str(e.exception))
189 self.get_ud_value_for_attestation.assert_called_with("an-ud-source")
190 self.do_unlock.assert_called_with(self.options, label=False)
191 self.get_hsm.assert_called_with("is-verbose")
192 self.hsm.get_powhsm_attestation.assert_called_with("some-random-value")
193 self.hsm.disconnect.assert_not_called()
194 self.SgxEnvelope.assert_not_called()
195 self.HSMCertificateV2.assert_not_called()
196 self.HSMCertificateV2ElementSGXQuote.assert_not_called()
197 self.HSMCertificateV2ElementSGXAttestationKey.assert_not_called()
198 self.HSMCertificateV2ElementX509.assert_not_called()
200 def test_adm_err_envelope_parsing(self, *args):
201 self.setupMocks(*args[:-1])
203 self.SgxEnvelope.side_effect = ValueError("an error")
205 with self.assertRaises(AdminError) as e:
206 do_attestation(self.options)
207 self.assertIn("envelope parse error", str(e.exception))
209 self.get_ud_value_for_attestation.assert_called_with("an-ud-source")
210 self.do_unlock.assert_called_with(self.options, label=False)
211 self.get_hsm.assert_called_with("is-verbose")
212 self.hsm.get_powhsm_attestation.assert_called_with("some-random-value")
213 self.hsm.disconnect.assert_called()
214 self.SgxEnvelope.assert_called_with(
215 bytes.fromhex("11"*32),
216 bytes.fromhex("22"*32),
217 )
218 self.HSMCertificateV2.assert_not_called()
219 self.HSMCertificateV2ElementSGXQuote.assert_not_called()
220 self.HSMCertificateV2ElementSGXAttestationKey.assert_not_called()
221 self.HSMCertificateV2ElementX509.assert_not_called()