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

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. 

22 

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 

30 

31 

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 

49 

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 

62 

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 

96 

97 self.HSMCertificateV2ElementSGXQuote.return_value = "quote_elem" 

98 self.HSMCertificateV2ElementSGXAttestationKey.return_value = "attkey_elem" 

99 self.HSMCertificateV2ElementX509.side_effect = ["cert0_elem", "cert1_elem"] 

100 

101 get_hsm.return_value = self.hsm 

102 get_ud_value_for_attestation.return_value = "some-random-value" 

103 

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] 

111 

112 do_attestation(self.options) 

113 

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") 

161 

162 def test_no_output_path(self, *args): 

163 self.setupMocks(*args[:-1]) 

164 self.options.output_file_path = None 

165 

166 with self.assertRaises(AdminError) as e: 

167 do_attestation(self.options) 

168 self.assertIn("output file", str(e.exception)) 

169 

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() 

179 

180 def test_adm_err_get_attestation(self, *args): 

181 self.setupMocks(*args[:-1]) 

182 

183 self.hsm.get_powhsm_attestation.side_effect = RuntimeError("an error") 

184 

185 with self.assertRaises(RuntimeError) as e: 

186 do_attestation(self.options) 

187 self.assertIn("an error", str(e.exception)) 

188 

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() 

199 

200 def test_adm_err_envelope_parsing(self, *args): 

201 self.setupMocks(*args[:-1]) 

202 

203 self.SgxEnvelope.side_effect = ValueError("an error") 

204 

205 with self.assertRaises(AdminError) as e: 

206 do_attestation(self.options) 

207 self.assertIn("envelope parse error", str(e.exception)) 

208 

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()