diff --git a/middleware/admin/certificate.py b/middleware/admin/certificate.py index 1b86f791..1321a0b2 100644 --- a/middleware/admin/certificate.py +++ b/middleware/admin/certificate.py @@ -24,3 +24,10 @@ from .certificate_v2 import HSMCertificateV2, HSMCertificateV2ElementSGXQuote, \ HSMCertificateV2ElementSGXAttestationKey, \ HSMCertificateV2ElementX509 + + +# Assign version mapping to the parent class +HSMCertificate.VERSION_MAPPING = { + 1: HSMCertificate, + 2: HSMCertificateV2, +} diff --git a/middleware/admin/certificate_v1.py b/middleware/admin/certificate_v1.py index a37802ed..414fe8f3 100644 --- a/middleware/admin/certificate_v1.py +++ b/middleware/admin/certificate_v1.py @@ -125,18 +125,25 @@ class HSMCertificate: VERSION = 1 # Only supported version ROOT_ELEMENT = "root" ELEMENT_BASE_CLASS = HSMCertificateElement + ELEMENT_FACTORY = HSMCertificateElement - @staticmethod - def from_jsonfile(path): + @classmethod + def from_jsonfile(kls, path): try: with open(path, "r") as file: certificate_map = json.loads(file.read()) if type(certificate_map) != dict: raise ValueError( - "JSON file must contain an object as a top level element") + "Certificate file must contain an object as a top level element") - return HSMCertificate(certificate_map) + version = certificate_map.get("version") + if version not in kls.VERSION_MAPPING: + raise ValueError("Invalid or unsupported HSM certificate " + f"version {version} (supported versions are " + f"{", ".join(kls.VERSION_MAPPING.keys())})") + + return kls.VERSION_MAPPING[version](certificate_map) except (ValueError, json.JSONDecodeError) as e: raise ValueError('Unable to read HSM certificate from "%s": %s' % (path, str(e))) @@ -190,8 +197,8 @@ def validate_and_get_values(self, raw_root_pubkey_hex): def add_element(self, element): if not isinstance(element, self.ELEMENT_BASE_CLASS): - raise ValueError( - f"Expected an HSMCertificateElement but got a {type(element)}") + raise ValueError(f"Expected an {self.ELEMENT_BASE_CLASS.__name__} " + "but got a {type(element)}") self._elements[element.name] = element def clear_targets(self): @@ -214,11 +221,10 @@ def save_to_jsonfile(self, path): file.write("%s\n" % json.dumps(self.to_dict(), indent=2)) def _parse(self, certificate_map): - if "version" not in certificate_map or certificate_map["version"] != self.VERSION: - raise ValueError( - "Invalid or unsupported HSM certificate version " - f"(current version is {self.VERSION})" - ) + version = certificate_map.get("version") + if version != self.VERSION: + raise ValueError("Invalid or unexpected HSM certificate version " + f"{version} (expected {self.VERSION})") if "targets" not in certificate_map or type(certificate_map["targets"]) != list: raise ValueError("Missing or invalid targets") @@ -229,7 +235,7 @@ def _parse(self, certificate_map): raise ValueError("Missing elements") for item in certificate_map["elements"]: - element = HSMCertificateElement(item) + element = self.ELEMENT_FACTORY(item) self._elements[item["name"]] = element # Sanity: check each target has a path to the root authority diff --git a/middleware/admin/certificate_v2.py b/middleware/admin/certificate_v2.py index 962fa3a0..a7b58f7c 100644 --- a/middleware/admin/certificate_v2.py +++ b/middleware/admin/certificate_v2.py @@ -20,76 +20,176 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import base64 from .certificate_v1 import HSMCertificate +from .utils import is_nonempty_hex_string class HSMCertificateV2Element: - pass + def __init__(self): + raise RuntimeError("Cannot instantiate an " + "abstract HSMCertificateV2Element") + + @classmethod + def from_dict(kls, element_map): + if element_map.get("type") not in kls.TYPE_MAPPING: + raise ValueError("Invalid or missing element type for " + f"element {element_map.get("name")}") + + return kls.TYPE_MAPPING[element_map["type"]](element_map) + + def _init_with_map(self, element_map): + if "name" not in element_map: + raise ValueError("Missing name for HSM certificate element") + + self._name = element_map["name"] + + if "signed_by" not in element_map: + raise ValueError("Missing certifier for HSM certificate element") + self._signed_by = element_map["signed_by"] + + @property + def name(self): + return self._name + + @property + def signed_by(self): + return self._signed_by class HSMCertificateV2ElementSGXQuote(HSMCertificateV2Element): - def __init__(self, name, message, custom_data, signature, signed_by): - self.name = name - self.message = message - self.custom_data = custom_data - self.signature = signature - self.signed_by = signed_by + def __init__(self, element_map): + self._init_with_map(element_map) + + def _init_with_map(self, element_map): + super()._init_with_map(element_map) + + if not is_nonempty_hex_string(element_map.get("message")): + raise ValueError(f"Invalid message for HSM certificate element {self.name}") + self._message = bytes.fromhex(element_map["message"]) + + if not is_nonempty_hex_string(element_map.get("custom_data")): + raise ValueError("Invalid custom data for HSM certificate " + f"element {self.name}") + self._custom_data = bytes.fromhex(element_map["custom_data"]) + + if not is_nonempty_hex_string(element_map.get("signature")): + raise ValueError("Invalid signature for HSM certificate element {self.name}") + self._signature = bytes.fromhex(element_map["signature"]) + + @property + def message(self): + return self._message.hex() + + @property + def custom_data(self): + return self._custom_data.hex() + + @property + def signature(self): + return self._signature.hex() def to_dict(self): return { "name": self.name, "type": "sgx_quote", - "message": self.message.hex(), - "custom_data": self.custom_data.hex(), - "signature": self.signature.hex(), + "message": self.message, + "custom_data": self.custom_data, + "signature": self.signature, "signed_by": self.signed_by, } class HSMCertificateV2ElementSGXAttestationKey(HSMCertificateV2Element): - def __init__(self, name, message, key, auth_data, signature, signed_by): - self.name = name - self.message = message - self.key = key - self.auth_data = auth_data - self.signature = signature - self.signed_by = signed_by + def __init__(self, element_map): + self._init_with_map(element_map) + + def _init_with_map(self, element_map): + super()._init_with_map(element_map) + + if not is_nonempty_hex_string(element_map.get("message")): + raise ValueError(f"Invalid message for HSM certificate element {self.name}") + self._message = bytes.fromhex(element_map["message"]) + + if not is_nonempty_hex_string(element_map.get("key")): + raise ValueError(f"Invalid key for HSM certificate element {self.name}") + self._key = bytes.fromhex(element_map["key"]) + + if not is_nonempty_hex_string(element_map.get("auth_data")): + raise ValueError(f"Invalid auth data for HSM certificate element {self.name}") + self._auth_data = bytes.fromhex(element_map["auth_data"]) + + if not is_nonempty_hex_string(element_map.get("signature")): + raise ValueError(f"Invalid signature for HSM certificate element {self.name}") + self._signature = bytes.fromhex(element_map["signature"]) + + @property + def message(self): + return self._message.hex() + + @property + def key(self): + return self._key.hex() + + @property + def auth_data(self): + return self._auth_data.hex() + + @property + def signature(self): + return self._signature.hex() def to_dict(self): return { "name": self.name, "type": "sgx_attestation_key", - "message": self.message.hex(), - "key": self.key.hex(), - "auth_data": self.auth_data.hex(), - "signature": self.signature.hex(), + "message": self.message, + "key": self.key, + "auth_data": self.auth_data, + "signature": self.signature, "signed_by": self.signed_by, } class HSMCertificateV2ElementX509(HSMCertificateV2Element): - def __init__(self, name, message, signed_by): - self.name = name - self.message = message - self.signed_by = signed_by + def __init__(self, element_map): + self._init_with_map(element_map) + + def _init_with_map(self, element_map): + super()._init_with_map(element_map) + + try: + self._message = base64.b64decode(element_map.get("message")) + except Exception: + raise ValueError(f"Invalid message for HSM certificate element {self.name}") + + @property + def message(self): + return base64.b64encode(self._message).decode("ASCII") def to_dict(self): return { "name": self.name, "type": "x509_pem", - "message": self.message.decode('ASCII'), + "message": self.message, "signed_by": self.signed_by, } +# Element type mappings +HSMCertificateV2Element.TYPE_MAPPING = { + "sgx_quote": HSMCertificateV2ElementSGXQuote, + "sgx_attestation_key": HSMCertificateV2ElementSGXAttestationKey, + "x509_pem": HSMCertificateV2ElementX509, +} + + class HSMCertificateV2(HSMCertificate): VERSION = 2 + ROOT_ELEMENT = "sgx_root" ELEMENT_BASE_CLASS = HSMCertificateV2Element + ELEMENT_FACTORY = HSMCertificateV2Element.from_dict def validate_and_get_values(self, raw_root_pubkey_hex): # TODO pass - - def _parse(self, certificate_map): - # TODO - pass diff --git a/middleware/admin/sgx_attestation.py b/middleware/admin/sgx_attestation.py index 364df5b2..8267e678 100644 --- a/middleware/admin/sgx_attestation.py +++ b/middleware/admin/sgx_attestation.py @@ -98,34 +98,34 @@ def do_attestation(options): att_cert = HSMCertificateV2() att_cert.add_element( - HSMCertificateV2ElementSGXQuote( - name="quote", - message=envelope.quote.get_raw_data(), - custom_data=envelope.custom_message, - signature=quote_signature, - signed_by="attestation", - )) + HSMCertificateV2ElementSGXQuote({ + "name": "quote", + "message": envelope.quote.get_raw_data().hex(), + "custom_data": envelope.custom_message.hex(), + "signature": quote_signature.hex(), + "signed_by": "attestation", + })) att_cert.add_element( - HSMCertificateV2ElementSGXAttestationKey( - name="attestation", - message=envelope.quote_auth_data.qe_report_body.get_raw_data(), - key=att_key.to_string("uncompressed"), - auth_data=envelope.qe_auth_data.data, - signature=qe_rb_signature, - signed_by="quoting_enclave", - )) + HSMCertificateV2ElementSGXAttestationKey({ + "name": "attestation", + "message": envelope.quote_auth_data.qe_report_body.get_raw_data().hex(), + "key": att_key.to_string("uncompressed").hex(), + "auth_data": envelope.qe_auth_data.data.hex(), + "signature": qe_rb_signature.hex(), + "signed_by": "quoting_enclave", + })) att_cert.add_element( - HSMCertificateV2ElementX509( - name="quoting_enclave", - message=envelope.qe_cert_data.certs[0], - signed_by="platform_ca", - )) + HSMCertificateV2ElementX509({ + "name": "quoting_enclave", + "message": envelope.qe_cert_data.certs[0], + "signed_by": "platform_ca", + })) att_cert.add_element( - HSMCertificateV2ElementX509( - name="platform_ca", - message=envelope.qe_cert_data.certs[1], - signed_by="sgx_root", - )) + HSMCertificateV2ElementX509({ + "name": "platform_ca", + "message": envelope.qe_cert_data.certs[1], + "signed_by": "sgx_root", + })) att_cert.add_target("quote") att_cert.save_to_jsonfile(options.output_file_path) diff --git a/middleware/tests/admin/test_certificate_v1.py b/middleware/tests/admin/test_certificate_v1.py index f7503bba..8235850a 100644 --- a/middleware/tests/admin/test_certificate_v1.py +++ b/middleware/tests/admin/test_certificate_v1.py @@ -26,7 +26,7 @@ from unittest import TestCase from unittest.mock import call, patch, mock_open -from admin.certificate_v1 import HSMCertificate, HSMCertificateElement +from admin.certificate import HSMCertificate, HSMCertificateElement class TestHSMCertificate(TestCase): @@ -155,7 +155,7 @@ def test_create_certificate_missing_elements(self): "targets": ["attestation", "device"] }) - @patch('admin.certificate_v1.HSMCertificateElement') + @patch('admin.certificate_v1.HSMCertificate.ELEMENT_FACTORY') def test_create_certificate_invalid_element(self, certElementMock): certElementMock.side_effect = ValueError() with self.assertRaises(ValueError): diff --git a/middleware/tests/admin/test_certificate_v2.py b/middleware/tests/admin/test_certificate_v2.py index f4a16da6..ada5c485 100644 --- a/middleware/tests/admin/test_certificate_v2.py +++ b/middleware/tests/admin/test_certificate_v2.py @@ -22,9 +22,11 @@ from unittest import TestCase from admin.certificate_v1 import HSMCertificate -from admin.certificate_v2 import HSMCertificateV2, HSMCertificateV2ElementSGXQuote, \ +from admin.certificate_v2 import HSMCertificateV2, HSMCertificateV2Element, \ + HSMCertificateV2ElementSGXQuote, \ HSMCertificateV2ElementSGXAttestationKey, \ HSMCertificateV2ElementX509 +from .test_certificate_v2_resources import TEST_CERTIFICATE class TestHSMCertificateV2(TestCase): @@ -35,16 +37,61 @@ def test_create_empty_certificate_ok(self): cert = HSMCertificateV2() self.assertEqual({'version': 2, 'targets': [], 'elements': []}, cert.to_dict()) + def test_parse_identity(self): + cert = HSMCertificateV2(TEST_CERTIFICATE) + self.assertEqual(TEST_CERTIFICATE, cert.to_dict()) + + +class TestHSMCertificateV2Element(TestCase): + def test_from_dict_unknown_type(self): + with self.assertRaises(ValueError) as e: + HSMCertificateV2Element.from_dict({ + "name": "a-strange-name", + "type": "an-unknown-type", + "some": "other", + "random": "attributes", + }) + self.assertIn("a-strange-name", str(e.exception)) + + def test_from_dict_no_name(self): + with self.assertRaises(ValueError) as e: + HSMCertificateV2Element.from_dict({ + "type": "sgx_quote", + "signed_by": "a-signer", + "some": "other", + "random": "attributes", + }) + self.assertIn("Missing name", str(e.exception)) + + def test_from_dict_no_signed_by(self): + with self.assertRaises(ValueError) as e: + HSMCertificateV2Element.from_dict({ + "name": "a name", + "type": "sgx_quote", + "some": "other", + "random": "attributes", + }) + self.assertIn("Missing certifier", str(e.exception)) + class TestHSMCertificateV2ElementSGXQuote(TestCase): + def setUp(self): + self.elem = HSMCertificateV2ElementSGXQuote({ + "name": "thename", + "message": "aabbcc", + "custom_data": "ddeeff", + "signature": "112233", + "signed_by": "whosigned", + }) + + def test_props(self): + self.assertEqual("thename", self.elem.name) + self.assertEqual("whosigned", self.elem.signed_by) + self.assertEqual("aabbcc", self.elem.message) + self.assertEqual("ddeeff", self.elem.custom_data) + self.assertEqual("112233", self.elem.signature) + def test_dict_ok(self): - elem = HSMCertificateV2ElementSGXQuote( - "thename", - bytes.fromhex("aabbcc"), - bytes.fromhex("ddeeff"), - bytes.fromhex("112233"), - "whosigned" - ) self.assertEqual({ "name": "thename", "type": "sgx_quote", @@ -52,19 +99,71 @@ def test_dict_ok(self): "custom_data": "ddeeff", "signature": "112233", "signed_by": "whosigned", - }, elem.to_dict()) + }, self.elem.to_dict()) + + def test_parse_identity(self): + source = TEST_CERTIFICATE["elements"][0] + elem = HSMCertificateV2Element.from_dict(source) + self.assertTrue(isinstance(elem, HSMCertificateV2ElementSGXQuote)) + self.assertEqual(source, elem.to_dict()) + + def test_from_dict_invalid_message(self): + with self.assertRaises(ValueError) as e: + HSMCertificateV2Element.from_dict({ + "name": "quote", + "type": "sgx_quote", + "message": "not-hex", + "custom_data": "112233", + "signature": "445566778899", + "signed_by": "attestation" + }) + self.assertIn("Invalid message", str(e.exception)) + + def test_from_dict_invalid_custom_data(self): + with self.assertRaises(ValueError) as e: + HSMCertificateV2Element.from_dict({ + "name": "quote", + "type": "sgx_quote", + "message": "aabbccdd", + "custom_data": "not-hex", + "signature": "445566778899", + "signed_by": "attestation" + }) + self.assertIn("Invalid custom data", str(e.exception)) + + def test_from_dict_invalid_signature(self): + with self.assertRaises(ValueError) as e: + HSMCertificateV2Element.from_dict({ + "name": "quote", + "type": "sgx_quote", + "message": "aabbccdd", + "custom_data": "112233", + "signature": "not-hex", + "signed_by": "attestation" + }) + self.assertIn("Invalid signature", str(e.exception)) class TestHSMCertificateV2ElementSGXAttestationKey(TestCase): + def setUp(self): + self.elem = HSMCertificateV2ElementSGXAttestationKey({ + "name": "thename", + "message": "aabbcc", + "key": "ddeeff", + "auth_data": "112233", + "signature": "44556677", + "signed_by": "whosigned", + }) + + def test_props(self): + self.assertEqual("thename", self.elem.name) + self.assertEqual("whosigned", self.elem.signed_by) + self.assertEqual("aabbcc", self.elem.message) + self.assertEqual("ddeeff", self.elem.key) + self.assertEqual("112233", self.elem.auth_data) + self.assertEqual("44556677", self.elem.signature) + def test_dict_ok(self): - elem = HSMCertificateV2ElementSGXAttestationKey( - "thename", - bytes.fromhex("aabbcc"), - bytes.fromhex("ddeeff"), - bytes.fromhex("112233"), - bytes.fromhex("44556677"), - "whosigned" - ) self.assertEqual({ "name": "thename", "type": "sgx_attestation_key", @@ -73,19 +172,100 @@ def test_dict_ok(self): "auth_data": "112233", "signature": "44556677", "signed_by": "whosigned", - }, elem.to_dict()) + }, self.elem.to_dict()) + + def test_parse_identity(self): + source = TEST_CERTIFICATE["elements"][1] + elem = HSMCertificateV2Element.from_dict(source) + self.assertTrue(isinstance(elem, HSMCertificateV2ElementSGXAttestationKey)) + self.assertEqual(source, elem.to_dict()) + + def test_from_dict_invalid_message(self): + with self.assertRaises(ValueError) as e: + HSMCertificateV2Element.from_dict({ + "name": "attestation", + "type": "sgx_attestation_key", + "message": "not-hex", + "key": "eeff", + "auth_data": "112233", + "signature": "44556677", + "signed_by": "quoting_enclave" + }) + self.assertIn("Invalid message", str(e.exception)) + + def test_from_dict_invalid_key(self): + with self.assertRaises(ValueError) as e: + HSMCertificateV2Element.from_dict({ + "name": "attestation", + "type": "sgx_attestation_key", + "message": "aabbccdd", + "key": "not-hex", + "auth_data": "112233", + "signature": "44556677", + "signed_by": "quoting_enclave" + }) + self.assertIn("Invalid key", str(e.exception)) + + def test_from_dict_invalid_auth_data(self): + with self.assertRaises(ValueError) as e: + HSMCertificateV2Element.from_dict({ + "name": "attestation", + "type": "sgx_attestation_key", + "message": "aabbccdd", + "key": "eeff", + "auth_data": "not-hex", + "signature": "44556677", + "signed_by": "quoting_enclave" + }) + self.assertIn("Invalid auth data", str(e.exception)) + + def test_from_dict_invalid_signature(self): + with self.assertRaises(ValueError) as e: + HSMCertificateV2Element.from_dict({ + "name": "attestation", + "type": "sgx_attestation_key", + "message": "aabbccdd", + "key": "eeff", + "auth_data": "112233", + "signature": "not-hex", + "signed_by": "quoting_enclave" + }) + self.assertIn("Invalid signature", str(e.exception)) class TestHSMCertificateV2ElementX509(TestCase): + def setUp(self): + self.elem = HSMCertificateV2ElementX509({ + "name": "thename", + "message": "dGhpcyBpcyBhbiBhc2NpaSBtZXNzYWdl", + "signed_by": "whosigned", + }) + + def test_props(self): + self.assertEqual("thename", self.elem.name) + self.assertEqual("whosigned", self.elem.signed_by) + self.assertEqual("dGhpcyBpcyBhbiBhc2NpaSBtZXNzYWdl", self.elem.message) + def test_dict_ok(self): - elem = HSMCertificateV2ElementX509( - "thename", - b"this is an ascii message", - "whosigned" - ) self.assertEqual({ "name": "thename", "type": "x509_pem", - "message": "this is an ascii message", + "message": "dGhpcyBpcyBhbiBhc2NpaSBtZXNzYWdl", "signed_by": "whosigned", - }, elem.to_dict()) + }, self.elem.to_dict()) + + def test_parse_identity(self): + source = TEST_CERTIFICATE["elements"][3] + elem = HSMCertificateV2Element.from_dict(source) + self.assertTrue(isinstance(elem, HSMCertificateV2ElementX509)) + self.assertEqual(source, elem.to_dict()) + + def test_from_dict_invalid_message(self): + with self.assertRaises(ValueError) as e: + HSMCertificateV2Element.from_dict({ + "name": "quoting_enclave", + "type": "x509_pem", + "message": "not-base-64", + "signed_by": "platform_ca" + }) + self.assertIn("Invalid message", str(e.exception)) diff --git a/middleware/tests/admin/test_certificate_v2_resources.py b/middleware/tests/admin/test_certificate_v2_resources.py new file mode 100644 index 00000000..4717248a --- /dev/null +++ b/middleware/tests/admin/test_certificate_v2_resources.py @@ -0,0 +1,63 @@ +# The MIT License (MIT) +# +# Copyright (c) 2021 RSK Labs Ltd +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is furnished to do +# so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import json + +TEST_CERTIFICATE = json.loads(""" +{ + "version": 2, + "targets": [ + "quote" + ], + "elements": [ + { + "name": "quote", + "type": "sgx_quote", + "message": "03000200000000000a000f00939a7233f79c4ca9940a0db3957f0607ceae3549bc7273eb34d562f4564fc182000000000e0e100fffff01000000000000000000010000000000000000000000000000000000000000000000000000000000000005000000000000000700000000000000d32688d3c1f3dfcc8b0b36eac7c89d49af331800bd56248044166fa6699442c10000000000000000000000000000000000000000000000000000000000000000718c2f1a0efbd513e016fafd6cf62a624442f2d83708d4b33ab5a8d8c1cd4dd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000640001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e95bb875c1a728071f70ad8c9d03f1744c19acb0580921e611ac9104f7701d00000000000000000000000000000000000000000000000000000000000000000", + "custom_data": "504f5748534d3a352e343a3a736778f36f7bc09aab50c0886a442b2d04b18186720bda7a753643066cd0bc0a4191800c4d091913d39750dc8975adbdd261bd10c1c2e110faa47cfbe30e740895552bbdcb3c17c7aee714cec8ad900341bfd987b452280220dcbd6e7191f67ea4209b00000000000000000000000000000000", + "signature": "3046022100e52b03a7bd6b5dd9feeeb375bd597730d2872643b47aff4dd641c5c3a2b8016e022100bbd227f67e7c23bbddeb4f8fddee031a2b961501d1c28dda082669d7ac861e6c", + "signed_by": "attestation" + }, + { + "name": "attestation", + "type": "sgx_attestation_key", + "message": "0e0e100fffff0100000000000000000000000000000000000000000000000000000000000000000000000000000000001500000000000000e70000000000000096b347a64e5a045e27369c26e6dcda51fd7c850e9b3a3a79e718f43261dee1e400000000000000000000000000000000000000000000000000000000000000008c4f5775d796503e96137f77c68a829a0056ac8ded70140b081b094490c57bff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001fe721d0322954821589237fd27efb8fef1acb3ecd6b0352c31271550fc70f940000000000000000000000000000000000000000000000000000000000000000", + "key": "04a024cb34c90ea6a8f9f2181c9020cbcc7c073e69981733c8deed6f6c451822aa08376350ff7da01f842bb40c631cbb711f8b6f7a4fae398320a3884774d250ad", + "auth_data": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "signature": "304502201f14d532274c4385fc0019ca2a21e53e17143cb62377ca4fcdd97fa9fef8fb2502210095d4ee272cf3c512e36779de67dc7814982f1160d981d138a32b265e928a0562", + "signed_by": "quoting_enclave" + }, + { + "name": "quoting_enclave", + "type": "x509_pem", + "message": "MIIE8zCCBJigAwIBAgIUfr2dlwN42DBUA9CXIkBlGP2vV3AwCgYIKoZIzj0EAwIwcDEiMCAGA1UEAwwZSW50ZWwgU0dYIFBDSyBQbGF0Zm9ybSBDQTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMwHhcNMjQwMzIzMDQ0NjIxWhcNMzEwMzIzMDQ0NjIxWjBwMSIwIAYDVQQDDBlJbnRlbCBTR1ggUENLIENlcnRpZmljYXRlMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKl7RDNlsZKkEtAcW7SfCX1JegbvGq4O0rRUt0z/G6fZJsNlpmRwTB4DYkrgkm1t+9RpLwxFX9/kghxiDQm0jqmjggMOMIIDCjAfBgNVHSMEGDAWgBSVb13NvRvh6UBJydT0M84BVwveVDBrBgNVHR8EZDBiMGCgXqBchlpodHRwczovL2FwaS50cnVzdGVkc2VydmljZXMuaW50ZWwuY29tL3NneC9jZXJ0aWZpY2F0aW9uL3Y0L3Bja2NybD9jYT1wbGF0Zm9ybSZlbmNvZGluZz1kZXIwHQYDVR0OBBYEFALKV5DF16KnEbSW5QM9ecDqBZaHMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMIICOwYJKoZIhvhNAQ0BBIICLDCCAigwHgYKKoZIhvhNAQ0BAQQQttJXuiQVwqM4s74g+HxfKTCCAWUGCiqGSIb4TQENAQIwggFVMBAGCyqGSIb4TQENAQIBAgEOMBAGCyqGSIb4TQENAQICAgEOMBAGCyqGSIb4TQENAQIDAgEDMBAGCyqGSIb4TQENAQIEAgEDMBEGCyqGSIb4TQENAQIFAgIA/zARBgsqhkiG+E0BDQECBgICAP8wEAYLKoZIhvhNAQ0BAgcCAQEwEAYLKoZIhvhNAQ0BAggCAQAwEAYLKoZIhvhNAQ0BAgkCAQAwEAYLKoZIhvhNAQ0BAgoCAQAwEAYLKoZIhvhNAQ0BAgsCAQAwEAYLKoZIhvhNAQ0BAgwCAQAwEAYLKoZIhvhNAQ0BAg0CAQAwEAYLKoZIhvhNAQ0BAg4CAQAwEAYLKoZIhvhNAQ0BAg8CAQAwEAYLKoZIhvhNAQ0BAhACAQAwEAYLKoZIhvhNAQ0BAhECAQ0wHwYLKoZIhvhNAQ0BAhIEEA4OAwP//wEAAAAAAAAAAAAwEAYKKoZIhvhNAQ0BAwQCAAAwFAYKKoZIhvhNAQ0BBAQGAGBqAAAAMA8GCiqGSIb4TQENAQUKAQEwHgYKKoZIhvhNAQ0BBgQQDVe/DXUVE4gemtgO5uBpvDBEBgoqhkiG+E0BDQEHMDYwEAYLKoZIhvhNAQ0BBwEBAf8wEAYLKoZIhvhNAQ0BBwIBAQAwEAYLKoZIhvhNAQ0BBwMBAQAwCgYIKoZIzj0EAwIDSQAwRgIhAJFgf78HggTBtvQPXZJx/3Fm71vCOmt82pce91M2ZAI0AiEAiZMPBbZZmvR2v+1mrs76JeglDQ+pK/SLN94l4+jM5DA=", + "signed_by": "platform_ca" + }, + { + "name": "platform_ca", + "type": "x509_pem", + "message": "MIICljCCAj2gAwIBAgIVAJVvXc29G+HpQEnJ1PQzzgFXC95UMAoGCCqGSM49BAMCMGgxGjAYBgNVBAMMEUludGVsIFNHWCBSb290IENBMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYDVQQGEwJVUzAeFw0xODA1MjExMDUwMTBaFw0zMzA1MjExMDUwMTBaMHAxIjAgBgNVBAMMGUludGVsIFNHWCBQQ0sgUGxhdGZvcm0gQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENSB/7t21lXSO2Cuzpxw74eJB72EyDGgW5rXCtx2tVTLq6hKk6z+UiRZCnqR7psOvgqFeSxlmTlJleTmi2WYz3qOBuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUlW9dzb0b4elAScnU9DPOAVcL3lQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0EAwIDRwAwRAIgXsVki0w+i6VYGW3UF/22uaXe0YJDj1UenA+TjD1ai5cCICYb1SAmD5xkfTVpvo4UoyiSYxrDWLmUR4CI9NKyfPN+", + "signed_by": "sgx_root" + } + ] +} +""") diff --git a/middleware/tests/admin/test_sgx_attestation.py b/middleware/tests/admin/test_sgx_attestation.py index 4b60fadb..78d1e8de 100644 --- a/middleware/tests/admin/test_sgx_attestation.py +++ b/middleware/tests/admin/test_sgx_attestation.py @@ -65,7 +65,7 @@ def setupMocks(self, get_hsm, get_ud_value_for_attestation, do_unlock, "envelope": "11"*32, "message": "22"*32, } - quote = SimpleNamespace(**{"get_raw_data": lambda: "quote-raw-data"}) + quote = SimpleNamespace(**{"get_raw_data": lambda: b"quote-raw-data"}) sig = SimpleNamespace(**{"r": b"a"*32, "s": b"a"*32}) self.att_key = ecdsa.SigningKey.generate(curve=ecdsa.NIST256p).\ get_verifying_key() @@ -76,11 +76,11 @@ def setupMocks(self, get_hsm, get_ud_value_for_attestation, do_unlock, "signature": sig, "attestation_key": attkey, "qe_report_body": SimpleNamespace(**{ - "get_raw_data": lambda: "qerb-raw-data"}), + "get_raw_data": lambda: b"qerb-raw-data"}), "qe_report_body_signature": qesig, }) qead = SimpleNamespace(**{ - "data": "qead-data", + "data": b"qead-data", }) qecd = SimpleNamespace(**{ "certs": ["qecd-cert-0", "qecd-cert-1"], @@ -90,7 +90,7 @@ def setupMocks(self, get_hsm, get_ud_value_for_attestation, do_unlock, "quote_auth_data": qad, "qe_auth_data": qead, "qe_cert_data": qecd, - "custom_message": "a-custom-message", + "custom_message": b"a-custom-message", }) self.SgxEnvelope.return_value = envelope @@ -123,32 +123,32 @@ def test_ok(self, *args): bytes.fromhex("11"*32), bytes.fromhex("22"*32), ) - self.HSMCertificateV2ElementSGXQuote.assert_called_with( - name="quote", - message="quote-raw-data", - custom_data="a-custom-message", - signature=bytes.fromhex("30440220"+"61"*32+"0220"+"61"*32), - signed_by="attestation", - ) - self.HSMCertificateV2ElementSGXAttestationKey.assert_called_with( - name="attestation", - message="qerb-raw-data", - key=self.att_key.to_string("uncompressed"), - auth_data="qead-data", - signature=bytes.fromhex("30440220"+"63"*32+"0220"+"63"*32), - signed_by="quoting_enclave", - ) + self.HSMCertificateV2ElementSGXQuote.assert_called_with({ + "name": "quote", + "message": b"quote-raw-data".hex(), + "custom_data": b"a-custom-message".hex(), + "signature": "30440220"+"61"*32+"0220"+"61"*32, + "signed_by": "attestation", + }) + self.HSMCertificateV2ElementSGXAttestationKey.assert_called_with({ + "name": "attestation", + "message": b"qerb-raw-data".hex(), + "key": self.att_key.to_string("uncompressed").hex(), + "auth_data": b"qead-data".hex(), + "signature": "30440220"+"63"*32+"0220"+"63"*32, + "signed_by": "quoting_enclave", + }) self.HSMCertificateV2ElementX509.assert_has_calls([ - call( - name="quoting_enclave", - message="qecd-cert-0", - signed_by="platform_ca", - ), - call( - name="platform_ca", - message="qecd-cert-1", - signed_by="sgx_root", - ) + call({ + "name": "quoting_enclave", + "message": "qecd-cert-0", + "signed_by": "platform_ca", + }), + call({ + "name": "platform_ca", + "message": "qecd-cert-1", + "signed_by": "sgx_root", + }) ]) cert = self.HSMCertificateV2.return_value cert.add_element.assert_has_calls([ diff --git a/setup.cfg b/setup.cfg index da90c29b..36a420ae 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,6 +26,7 @@ per-file-ignores = __init__.py:F401, middleware/admin/certificate.py:F401, middleware/tests/sgx/test_envelope.py:E122, + middleware/tests/admin/test_certificate_v2_resources.py:E501, show-source = False statistics = True