From 46ad6688a154b88abef84eacdf954d50ae99ecc9 Mon Sep 17 00:00:00 2001 From: Olivier Le Thanh Duong Date: Fri, 5 Jul 2024 13:41:39 +0200 Subject: [PATCH] Confidential: Provide method to calculate firmware hash Which is required to calculate and verify the measurement Also add test and test for compute_confidential_measure --- src/aleph/sdk/utils.py | 15 ++++++++++ tests/unit/test_utils.py | 63 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/aleph/sdk/utils.py b/src/aleph/sdk/utils.py index 5c641d5c..116c7b42 100644 --- a/src/aleph/sdk/utils.py +++ b/src/aleph/sdk/utils.py @@ -268,6 +268,21 @@ def get_vm_measure(sev_data: SEVMeasurement) -> Tuple[bytes, bytes]: return vm_measure, nonce +def calculate_firmware_hash(firmware_path: Path) -> str: + """Calculate the hash of the firmware (OVMF) file to be used in validating the measurements + + Returned as hex encoded string""" + + # https://www.qemu.org/docs/master/system/i386/amd-memory-encryption.html + # The value of GCTX.LD is SHA256(firmware_blob || kernel_hashes_blob || vmsas_blob), where: + # firmware_blob is the content of the entire firmware flash file (for example, OVMF.fd). [...] + # and verified again sevctl, see tests + firmware_content = firmware_path.read_bytes() + hash_calculator = hashlib.sha256(firmware_content) + + return hash_calculator.hexdigest() + + def compute_confidential_measure( sev_info: SEVInfo, tik: bytes, expected_hash: str, nonce: bytes ) -> hmac.HMAC: diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 85f274e6..bfca23a5 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -1,3 +1,4 @@ +import base64 import datetime import pytest as pytest @@ -18,7 +19,14 @@ PersistentVolume, ) -from aleph.sdk.utils import enum_as_str, get_message_type_value, parse_volume +from aleph.sdk.types import SEVInfo +from aleph.sdk.utils import ( + calculate_firmware_hash, + compute_confidential_measure, + enum_as_str, + get_message_type_value, + parse_volume, +) def test_get_message_type_value(): @@ -174,3 +182,56 @@ def test_parse_persistent_volume(): volume = parse_volume(volume) assert volume assert isinstance(volume, PersistentVolume) + + +def test_calculate_firmware_hash(mocker): + mock_path = mocker.Mock( + read_bytes=mocker.Mock(return_value=b"abc"), + ) + + assert ( + calculate_firmware_hash(mock_path) + == "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + ) + + +def test_compute_confidential_measure(): + """Verify that we properly calculate the measurement we use agains the server + + Validated against the sevctl command: + $ RUST_LOG=trace sevctl measurement build --api-major 01 --api-minor 55 --build-id 24 --policy 1 + --tik ~/pycharm-aleph-sdk-python/decadecadecadecadecadecadecadecadecadecadecadecadecadecadecadeca_tik.bin + --firmware /usr/share/ovmf/OVMF.fd --nonce URQNqJAqh/2ep4drjx/XvA + + [2024-07-05T11:19:06Z DEBUG sevctl::measurement] firmware + table len=4194304 sha256: d06471f485c0a61aba5a431ec136b947be56907acf6ed96afb11788ae4525aeb + [2024-07-05T11:19:06Z DEBUG sevctl::measurement] --tik base64: npOTEc4mtRGfXfB+G6EBdw== + [2024-07-05T11:19:06Z DEBUG sevctl::measurement] --nonce base64: URQNqJAqh/2ep4drjx/XvA== + [2024-07-05T11:19:06Z DEBUG sevctl::measurement] Raw measurement: BAE3GAEAAADQZHH0hcCmGrpaQx7BNrlHvlaQes9u2Wr7EXiK5FJa61EUDaiQKof9nqeHa48f17w= + [2024-07-05T11:19:06Z DEBUG sevctl::measurement] Signed measurement: ls2jv10V3HVShVI/RHCo/a43WO0soLZf0huU9ZZstIw= + [2024-07-05T11:19:06Z DEBUG sevctl::measurement] Measurement + nonce: ls2jv10V3HVShVI/RHCo/a43WO0soLZf0huU9ZZstIxRFA2okCqH/Z6nh2uPH9e8 + """ + + tik = bytes.fromhex("9e939311ce26b5119f5df07e1ba10177") + assert base64.b64encode(tik) == b"npOTEc4mtRGfXfB+G6EBdw==" + expected_hash = "d06471f485c0a61aba5a431ec136b947be56907acf6ed96afb11788ae4525aeb" + nonce = base64.b64decode("URQNqJAqh/2ep4drjx/XvA==") + sev_info = SEVInfo.parse_obj( + { + "enabled": True, + "api_major": 1, + "api_minor": 55, + "build_id": 24, + "policy": 1, + "state": "running", + "handle": 1, + } + ) + + assert ( + base64.b64encode( + compute_confidential_measure( + sev_info, tik, expected_hash, nonce=nonce + ).digest() + ) + == b"ls2jv10V3HVShVI/RHCo/a43WO0soLZf0huU9ZZstIw=" + )