From 402e60c6c50ca23cf9ad780dec9a8081bacee98a Mon Sep 17 00:00:00 2001 From: Italo Sampaio Date: Tue, 19 Nov 2024 20:13:56 -0300 Subject: [PATCH] Changes after code review - Split logic into smaller modules - Added unit tests --- middleware/mrenclave.py | 192 +----------------- .../sgx/sgxtypes/sgx_enclave_properties.py | 108 ++++++++++ middleware/sgx/sgxtypes/sgx_sigstruct.py | 137 +++++++++++++ middleware/tests/sgx/__init__.py | 21 ++ .../tests/sgx/test_enclave_properties.py | 106 ++++++++++ middleware/tests/sgx/test_mrenclave.py | 77 +++++++ middleware/tests/sgx/test_sigstruct.py | 132 ++++++++++++ 7 files changed, 582 insertions(+), 191 deletions(-) create mode 100644 middleware/sgx/sgxtypes/sgx_enclave_properties.py create mode 100644 middleware/sgx/sgxtypes/sgx_sigstruct.py create mode 100644 middleware/tests/sgx/__init__.py create mode 100644 middleware/tests/sgx/test_enclave_properties.py create mode 100644 middleware/tests/sgx/test_mrenclave.py create mode 100644 middleware/tests/sgx/test_sigstruct.py diff --git a/middleware/mrenclave.py b/middleware/mrenclave.py index 9de37fc3..d98e24b4 100644 --- a/middleware/mrenclave.py +++ b/middleware/mrenclave.py @@ -23,198 +23,8 @@ from argparse import ArgumentParser from admin.misc import AdminError from elftools.elf.elffile import ELFFile -from enum import IntEnum import json - - -class EnclaveSizeSettings: - - class _Offset(IntEnum): - NUM_HEAP_PAGES = 0 - NUM_STACK_PAGES = 8 - NUM_TCS = 16 - END_MARKER = 24 - - def __init__(self, data): - self.num_heap_pages = int.from_bytes( - data[self._Offset.NUM_HEAP_PAGES:self._Offset.NUM_STACK_PAGES], - byteorder="little" - ) - self.num_stack_pages = int.from_bytes( - data[self._Offset.NUM_STACK_PAGES:self._Offset.NUM_TCS], - byteorder="little" - ) - self.num_tcs = int.from_bytes( - data[self._Offset.NUM_TCS:self._Offset.END_MARKER], - byteorder="little" - ) - - def to_dict(self): - return { - "num_heap_pages": self.num_heap_pages, - "num_stack_pages": self.num_stack_pages, - "num_tcs": self.num_tcs - } - - -class EnclavePropertiesHeader: - - class _Offset(IntEnum): - SIZE = 0 - TYPE = 4 - SIZE_SETTINGS = 8 - END_MARKER = 32 - - def __init__(self, data): - self.size = int.from_bytes( - data[self._Offset.SIZE:self._Offset.TYPE], - byteorder="little" - ) - self.enclave_type = int.from_bytes( - data[self._Offset.TYPE:self._Offset.SIZE_SETTINGS], - byteorder="little" - ) - self.size_settings = EnclaveSizeSettings( - data[self._Offset.SIZE_SETTINGS:self._Offset.END_MARKER] - ) - - def to_dict(self): - return { - "size": self.size, - "enclave_type": self.enclave_type, - "size_settings": self.size_settings.to_dict() - } - - -class SGXSigstruct: - - class _Offset(IntEnum): - HEADER = 0 - TYPE = 12 - VENDOR = 16 - DATE = 20 - HEADER2 = 24 - SWDEFINED = 40 - RESERVED = 44 - MODULUS = 128 - EXPONENT = 512 - SIGNATURE = 516 - MISCSELECT = 900 - MISCMASK = 904 - RESERVED2 = 908 - ISVFAMILYID = 912 - ATTRIBUTES = 928 - ATTRIBUTEMASK = 944 - ENCLAVEHASH = 960 - RESERVED3 = 992 - ISVEXTPRODID = 1008 - ISVPRODID = 1024 - ISVSVN = 1026 - RESERVED4 = 1028 - Q1 = 1040 - Q2 = 1424 - END_MARKER = 1808 - - def __init__(self, data): - self.header = data[self._Offset.HEADER:self._Offset.TYPE] - self.type = int.from_bytes( - data[self._Offset.TYPE:self._Offset.VENDOR], - byteorder="little" - ) - self.vendor = int.from_bytes( - data[self._Offset.VENDOR:self._Offset.DATE], - byteorder="little" - ) - self.date = int.from_bytes( - data[self._Offset.DATE:self._Offset.HEADER2], - byteorder="little" - ) - self.header2 = data[self._Offset.HEADER2:self._Offset.SWDEFINED] - self.swdefined = int.from_bytes( - data[self._Offset.SWDEFINED:self._Offset.RESERVED], - byteorder="little" - ) - self.reserved = data[self._Offset.RESERVED:self._Offset.MODULUS] - self.modulus = data[self._Offset.MODULUS:self._Offset.EXPONENT] - self.exponent = data[self._Offset.EXPONENT:self._Offset.SIGNATURE] - self.signature = data[self._Offset.SIGNATURE:self._Offset.MISCSELECT] - self.miscselect = int.from_bytes( - data[self._Offset.MISCSELECT:self._Offset.MISCMASK], - byteorder="little" - ) - self.miscmask = int.from_bytes( - data[self._Offset.MISCMASK:self._Offset.RESERVED2], - byteorder="little" - ) - self.reserved2 = data[self._Offset.RESERVED2:self._Offset.ISVFAMILYID] - self.isvfamilyid = data[self._Offset.ISVFAMILYID:self._Offset.ATTRIBUTES] - self.attributes = data[self._Offset.ATTRIBUTES:self._Offset.ATTRIBUTEMASK] - self.attributemask = data[self._Offset.ATTRIBUTEMASK:self._Offset.ENCLAVEHASH] - self.enclavehash = data[self._Offset.ENCLAVEHASH:self._Offset.RESERVED3] - self.reserved3 = data[self._Offset.RESERVED3:self._Offset.ISVEXTPRODID] - self.isvextprodid = data[self._Offset.ISVEXTPRODID:self._Offset.ISVPRODID] - self.isvprodid = int.from_bytes( - data[self._Offset.ISVPRODID:self._Offset.ISVSVN], - byteorder="little" - ) - self.isvsvn = int.from_bytes( - data[self._Offset.ISVSVN:self._Offset.RESERVED4], - byteorder="little" - ) - self.reserved4 = data[self._Offset.RESERVED4:self._Offset.Q1] - self.q1 = data[self._Offset.Q1:self._Offset.Q2] - self.q2 = data[self._Offset.Q2:self._Offset.END_MARKER] - - def get_mrenclave(self): - return self.enclavehash.hex() - - def to_dict(self): - return { - "header": self.header.hex(), - "type": self.type, - "vendor": self.vendor, - "date": self.date, - "header2": self.header2.hex(), - "swdefined": self.swdefined, - "modulus": self.modulus.hex(), - "exponent": self.exponent.hex(), - "signature": self.signature.hex(), - "miscselect": self.miscselect, - "miscmask": self.miscmask, - "isvfamilyid": self.isvfamilyid.hex(), - "attributes": self.attributes.hex(), - "attributemask": self.attributemask.hex(), - "enclavehash": self.enclavehash.hex(), - "isvextprodid": self.isvextprodid.hex(), - "isvprodid": self.isvprodid, - "isvsvn": self.isvsvn, - "q1": self.q1.hex(), - "q2": self.q2.hex(), - } - - -class EnclaveProperties: - - class _Offset(IntEnum): - HEADER = 0 - CONFIG = 32 - IMAGE_INFO = 96 - SIGSTRUCT = 144 - END_MARKER = 1960 - - def __init__(self, data): - self.header = EnclavePropertiesHeader( - data[self._Offset.HEADER:self._Offset.CONFIG] - ) - self.sigstruct = SGXSigstruct( - data[self._Offset.SIGSTRUCT:self._Offset.END_MARKER] - ) - - def get_mrenclave(self): - return self.sigstruct.get_mrenclave() - - def to_dict(self): - return {"header": self.header.to_dict(), "sigstruct": self.sigstruct.to_dict()} +from sgx.sgxtypes.sgx_enclave_properties import EnclaveProperties class EnclaveBinary: diff --git a/middleware/sgx/sgxtypes/sgx_enclave_properties.py b/middleware/sgx/sgxtypes/sgx_enclave_properties.py new file mode 100644 index 00000000..a5c0f899 --- /dev/null +++ b/middleware/sgx/sgxtypes/sgx_enclave_properties.py @@ -0,0 +1,108 @@ +# 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. + +from enum import IntEnum +from admin.misc import AdminError +from sgx.sgxtypes.sgx_sigstruct import SGXSigstruct + + +class _SizeSettings: + class _Offset(IntEnum): + NUM_HEAP_PAGES = 0 + NUM_STACK_PAGES = 8 + NUM_TCS = 16 + END_MARKER = 24 + + def __init__(self, data): + self.num_heap_pages = int.from_bytes( + data[self._Offset.NUM_HEAP_PAGES:self._Offset.NUM_STACK_PAGES], + byteorder="little" + ) + self.num_stack_pages = int.from_bytes( + data[self._Offset.NUM_STACK_PAGES:self._Offset.NUM_TCS], + byteorder="little" + ) + self.num_tcs = int.from_bytes( + data[self._Offset.NUM_TCS:self._Offset.END_MARKER], + byteorder="little" + ) + + def to_dict(self): + return { + "num_heap_pages": self.num_heap_pages, + "num_stack_pages": self.num_stack_pages, + "num_tcs": self.num_tcs + } + + +class _Header: + class _Offset(IntEnum): + SIZE = 0 + TYPE = 4 + SIZE_SETTINGS = 8 + END_MARKER = 32 + + def __init__(self, data): + self.size = int.from_bytes( + data[self._Offset.SIZE:self._Offset.TYPE], + byteorder="little" + ) + self.enclave_type = int.from_bytes( + data[self._Offset.TYPE:self._Offset.SIZE_SETTINGS], + byteorder="little" + ) + self.size_settings = _SizeSettings( + data[self._Offset.SIZE_SETTINGS:self._Offset.END_MARKER] + ) + + def to_dict(self): + return { + "size": self.size, + "enclave_type": self.enclave_type, + "size_settings": self.size_settings.to_dict() + } + + +class EnclaveProperties: + class _Offset(IntEnum): + HEADER = 0 + CONFIG = 32 + IMAGE_INFO = 96 + SIGSTRUCT = 144 + END_MARKER = 1960 + + def __init__(self, data): + if len(data) < self._Offset.END_MARKER: + raise AdminError("Invalid data length") + + self.header = _Header( + data[self._Offset.HEADER:self._Offset.CONFIG] + ) + self.sigstruct = SGXSigstruct( + data[self._Offset.SIGSTRUCT:self._Offset.END_MARKER] + ) + + def get_mrenclave(self): + return self.sigstruct.get_mrenclave() + + def to_dict(self): + return {"header": self.header.to_dict(), "sigstruct": self.sigstruct.to_dict()} diff --git a/middleware/sgx/sgxtypes/sgx_sigstruct.py b/middleware/sgx/sgxtypes/sgx_sigstruct.py new file mode 100644 index 00000000..7f88e474 --- /dev/null +++ b/middleware/sgx/sgxtypes/sgx_sigstruct.py @@ -0,0 +1,137 @@ +# 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. + +from enum import IntEnum +from admin.misc import AdminError + + +class SGXSigstruct: + class _Offset(IntEnum): + HEADER = 0 + TYPE = 12 + VENDOR = 16 + DATE = 20 + HEADER2 = 24 + SWDEFINED = 40 + RESERVED = 44 + MODULUS = 128 + EXPONENT = 512 + SIGNATURE = 516 + MISCSELECT = 900 + MISCMASK = 904 + RESERVED2 = 908 + ISVFAMILYID = 912 + ATTRIBUTES = 928 + ATTRIBUTEMASK = 944 + ENCLAVEHASH = 960 + RESERVED3 = 992 + ISVEXTPRODID = 1008 + ISVPRODID = 1024 + ISVSVN = 1026 + RESERVED4 = 1028 + Q1 = 1040 + Q2 = 1424 + END_MARKER = 1808 + + def __init__(self, data): + if len(data) < self._Offset.END_MARKER: + raise AdminError("Invalid data length") + + self.header = data[self._Offset.HEADER:self._Offset.TYPE] + self.type = int.from_bytes( + data[self._Offset.TYPE:self._Offset.VENDOR], + byteorder="little" + ) + self.vendor = int.from_bytes( + data[self._Offset.VENDOR:self._Offset.DATE], + byteorder="little" + ) + self.date = int.from_bytes( + data[self._Offset.DATE:self._Offset.HEADER2], + byteorder="little" + ) + self.header2 = data[self._Offset.HEADER2:self._Offset.SWDEFINED] + self.swdefined = int.from_bytes( + data[self._Offset.SWDEFINED:self._Offset.RESERVED], + byteorder="little" + ) + self.reserved = data[self._Offset.RESERVED:self._Offset.MODULUS] + self.modulus = data[self._Offset.MODULUS:self._Offset.EXPONENT] + self.exponent = data[self._Offset.EXPONENT:self._Offset.SIGNATURE] + self.signature = data[self._Offset.SIGNATURE:self._Offset.MISCSELECT] + self.miscselect = int.from_bytes( + data[self._Offset.MISCSELECT:self._Offset.MISCMASK], + byteorder="little" + ) + self.miscmask = int.from_bytes( + data[self._Offset.MISCMASK:self._Offset.RESERVED2], + byteorder="little" + ) + self.reserved2 = data[self._Offset.RESERVED2:self._Offset.ISVFAMILYID] + self.isvfamilyid = data[self._Offset.ISVFAMILYID:self._Offset.ATTRIBUTES] + self.attributes = data[self._Offset.ATTRIBUTES:self._Offset.ATTRIBUTEMASK] + self.attributemask = data[self._Offset.ATTRIBUTEMASK:self._Offset.ENCLAVEHASH] + self.enclavehash = data[self._Offset.ENCLAVEHASH:self._Offset.RESERVED3] + self.reserved3 = data[self._Offset.RESERVED3:self._Offset.ISVEXTPRODID] + self.isvextprodid = data[self._Offset.ISVEXTPRODID:self._Offset.ISVPRODID] + self.isvprodid = int.from_bytes( + data[self._Offset.ISVPRODID:self._Offset.ISVSVN], + byteorder="little" + ) + self.isvsvn = int.from_bytes( + data[self._Offset.ISVSVN:self._Offset.RESERVED4], + byteorder="little" + ) + self.reserved4 = data[self._Offset.RESERVED4:self._Offset.Q1] + self.q1 = data[self._Offset.Q1:self._Offset.Q2] + self.q2 = data[self._Offset.Q2:self._Offset.END_MARKER] + + def get_mrenclave(self): + return self.enclavehash.hex() + + def to_dict(self): + return { + "header": self.header.hex(), + "type": self.type, + "vendor": self.vendor, + "date": self.date, + "header2": self.header2.hex(), + "swdefined": self.swdefined, + "reserved": self.reserved.hex(), + "modulus": self.modulus.hex(), + "exponent": self.exponent.hex(), + "signature": self.signature.hex(), + "miscselect": self.miscselect, + "miscmask": self.miscmask, + "reserved2": self.reserved2.hex(), + "isvfamilyid": self.isvfamilyid.hex(), + "attributes": self.attributes.hex(), + "attributemask": self.attributemask.hex(), + "enclavehash": self.enclavehash.hex(), + "reserved3": self.reserved3.hex(), + "isvextprodid": self.isvextprodid.hex(), + "isvprodid": self.isvprodid, + "isvsvn": self.isvsvn, + "reserved4": self.reserved4.hex(), + "q1": self.q1.hex(), + "q2": self.q2.hex(), + } diff --git a/middleware/tests/sgx/__init__.py b/middleware/tests/sgx/__init__.py new file mode 100644 index 00000000..11284d93 --- /dev/null +++ b/middleware/tests/sgx/__init__.py @@ -0,0 +1,21 @@ +# 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. diff --git a/middleware/tests/sgx/test_enclave_properties.py b/middleware/tests/sgx/test_enclave_properties.py new file mode 100644 index 00000000..9bf5bc26 --- /dev/null +++ b/middleware/tests/sgx/test_enclave_properties.py @@ -0,0 +1,106 @@ +# 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. + +from unittest import TestCase +from unittest.mock import patch +from admin.misc import AdminError +from sgx.sgxtypes.sgx_enclave_properties import EnclaveProperties, _Header + +import logging +import struct + +logging.disable(logging.CRITICAL) + + +class TestEnclaveProperties(TestCase): + def setUp(self): + self.size_num_heap_pages = 0x123456789ABCDEF0 + self.size_num_stack_pages = 0x9ABCDEF012345678 + self.size_num_tcs = 0x1122334455667788 + self.header_size = 0x12345678 + self.header_type = 0x9ABCDEF0 + + size_settings_bytes = struct.pack( + "