From f16eb0ca956666c51def7651489e44da94d5fb13 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Wed, 2 Oct 2024 10:29:02 -0500 Subject: [PATCH] Created a common library for parsing and serializing CoVE TEE Attestation Payload Signed-off-by: Wojciech Ozga --- .gitignore | 1 + Makefile | 4 +- confidential-vms/linux_vm/Makefile | 12 +- .../linux_vm/configurations/busybox.config | 6 + .../linux_vm/configurations/linux64-defconfig | 12 +- .../qemu_riscv64_virt_defconfig | 65 +++++--- .../hypervisor_rootfs/run_linux_vm.sh | 4 +- .../root/this_is_confidential_vm_filesystem | 0 .../qemu_riscv64_virt_defconfig | 18 +-- security-monitor/Cargo.toml | 3 + .../rust-crates/cove_tap/Cargo.toml | 20 +++ .../rust-crates/cove_tap/src/error.rs | 24 +++ .../rust-crates/cove_tap/src/lib.rs | 22 +++ .../rust-crates/cove_tap/src/parser.rs | 120 ++++++++++++++ .../rust-crates/cove_tap/src/serializer.rs | 119 ++++++++++++++ .../rust-crates/cove_tap/src/spec.rs | 110 +++++++++++++ security-monitor/src/error.rs | 13 +- .../promote_to_confidential_vm.rs | 30 +++- tools/local_attestation/Cargo.toml | 2 + tools/local_attestation/src/append.rs | 38 +++-- tools/local_attestation/src/constants.rs | 30 ---- tools/local_attestation/src/error.rs | 27 +++- tools/local_attestation/src/generate.rs | 148 +++++++----------- tools/local_attestation/src/main.rs | 1 - 24 files changed, 638 insertions(+), 191 deletions(-) create mode 100644 confidential-vms/linux_vm/configurations/busybox.config create mode 100644 confidential-vms/linux_vm/overlay/root/this_is_confidential_vm_filesystem create mode 100644 security-monitor/rust-crates/cove_tap/Cargo.toml create mode 100644 security-monitor/rust-crates/cove_tap/src/error.rs create mode 100644 security-monitor/rust-crates/cove_tap/src/lib.rs create mode 100644 security-monitor/rust-crates/cove_tap/src/parser.rs create mode 100644 security-monitor/rust-crates/cove_tap/src/serializer.rs create mode 100644 security-monitor/rust-crates/cove_tap/src/spec.rs delete mode 100644 tools/local_attestation/src/constants.rs diff --git a/.gitignore b/.gitignore index ec37d179..3c48340c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ build/* target/* +tools/local_attestation/target security-monitor/target configurations/overlay/root/harness/baremetal diff --git a/Makefile b/Makefile index bb075a4b..56d1291f 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ devtools: setup hypervisor: setup devtools PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" ACE_DIR=$(ACE_DIR) $(MAKE) -C hypervisor -confidential_vms: setup devtools hypervisor +confidential_vms: setup devtools hypervisor tools BIN_DIR="$(OVERLAY_ROOT_DIR)/" RELEASE="" $(MAKE) -C $(CONFIDENTIAL_VMS_SOURCE_DIR)/baremetal/ ;\ PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" ACE_DIR=$(ACE_DIR) $(MAKE) -C $(CONFIDENTIAL_VMS_SOURCE_DIR)/linux_vm/ buildroot ;\ PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" ACE_DIR=$(ACE_DIR) $(MAKE) -C $(CONFIDENTIAL_VMS_SOURCE_DIR)/linux_vm/ overlay ;\ @@ -58,7 +58,7 @@ confidential_vms: setup devtools hypervisor hypervisor_dev: PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" ACE_DIR=$(ACE_DIR) $(MAKE) -C hypervisor dev -dev: +dev: tools $(MAKE) -C $(CONFIDENTIAL_VMS_SOURCE_DIR)/linux_vm/ dev ;\ $(MAKE) -C $(CONFIDENTIAL_VMS_SOURCE_DIR)/linux_vm/ overlay ;\ PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" ACE_DIR=$(ACE_DIR) $(MAKE) -C hypervisor rootfs; diff --git a/confidential-vms/linux_vm/Makefile b/confidential-vms/linux_vm/Makefile index 3bd4a969..0af293b1 100644 --- a/confidential-vms/linux_vm/Makefile +++ b/confidential-vms/linux_vm/Makefile @@ -17,6 +17,7 @@ LINUX_VM_BUILDROOT_ROOTFS ?= $(LINUX_VM_BUILDROOT_WORK_DIR)/images/rootfs.ext2 LINUX_VM_BUILDROOT_ROOTFS_SIZE ?= "256M" LINUX_VM_OVERLAY_SOURCE_DIR ?= $(CONFIDENTIAL_VMS_LINUX_SOURCE_DIR)/overlay LINUX_VM_OVERLAY_WORK_DIR ?= $(CONFIDENTIAL_VMS_LINUX_WORK_DIR)/overlay +LINUX_VM_OVERLAY_WORK_ROOT_DIR ?= $(LINUX_VM_OVERLAY_WORK_DIR)/root LINUX_VM_IMAGE ?= $(LINUX_VM_BUILDROOT_WORK_DIR)/images/Image LINUX_VM_KERNEL_CONFIG ?= $(CONFIDENTIAL_VMS_LINUX_SOURCE_DIR)/configurations/linux64-defconfig LINUX_VM_BUILDROOT_CONFIG ?= $(CONFIDENTIAL_VMS_LINUX_SOURCE_DIR)/configurations/qemu_riscv64_virt_defconfig @@ -44,15 +45,18 @@ buildroot: setup echo "Building buildroot"; \ rm -rf $(LINUX_VM_BUILDROOT_WORK_DIR); \ mkdir -p $(LINUX_VM_BUILDROOT_WORK_DIR); \ - mkdir -p $(LINUX_VM_OVERLAY_WORK_DIR); \ + mkdir -p $(LINUX_VM_OVERLAY_WORK_ROOT_DIR); \ + cp $(LINUX_VM_KERNEL_CONFIG) $(LINUX_VM_BUILDROOT_WORK_DIR)/linux64-config; \ cp $(LINUX_VM_BUILDROOT_CONFIG) $(LINUX_VM_BUILDROOT_WORK_DIR)/.config; \ sed "s@^BR2_ROOTFS_OVERLAY=.*@BR2_ROOTFS_OVERLAY=\"$(LINUX_VM_OVERLAY_WORK_DIR)\"@g" -i $(LINUX_VM_BUILDROOT_WORK_DIR)/.config; \ sed "s@^BR2_TARGET_ROOTFS_EXT2_SIZE=.*@BR2_TARGET_ROOTFS_EXT2_SIZE=\"$(LINUX_VM_BUILDROOT_ROOTFS_SIZE)\"@g" -i $(LINUX_VM_BUILDROOT_WORK_DIR)/.config; \ - sed "s@^BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE=.*@BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE=\"$(LINUX_VM_KERNEL_CONFIG)\"@g" -i $(LINUX_VM_BUILDROOT_WORK_DIR)/.config; \ + sed "s@^BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE=.*@BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE=\"$(LINUX_VM_BUILDROOT_WORK_DIR)/linux64-config\"@g" -i $(LINUX_VM_BUILDROOT_WORK_DIR)/.config; \ sed "s@^BR2_LINUX_KERNEL_PATCH=.*@BR2_LINUX_KERNEL_PATCH=\"$(LINUX_VM_PATCHES_DIR)\"@g" -i $(LINUX_VM_BUILDROOT_WORK_DIR)/.config; \ $(MAKE) -s -C $(LINUX_VM_BUILDROOT_SOURCE_DIR) RISCV=$(RISCV_GNU_TOOLCHAIN_WORK_DIR) PATH=$(PATH) O=$(LINUX_VM_BUILDROOT_WORK_DIR) CROSS_COMPILE=$(CROSS_COMPILE) BR2_JLEVEL=0 olddefconfig; \ $(MAKE) -s -C $(LINUX_VM_BUILDROOT_SOURCE_DIR) RISCV=$(RISCV_GNU_TOOLCHAIN_WORK_DIR) PATH=$(PATH) O=$(LINUX_VM_BUILDROOT_WORK_DIR) BR2_JLEVEL=0; \ fi + # + # sed "s@^CONFIG_INITRAMFS_SOURCE=.*@CONFIG_INITRAMFS_SOURCE=\"$(LINUX_VM_BUILDROOT_WORK_DIR)/images/rootfs.cpio\"@g" -i $(LINUX_VM_BUILDROOT_WORK_DIR)/linux64-config; \ dev: echo "Rebuilding buildroot"; \ @@ -70,7 +74,9 @@ overlay: setup mkdir -p $(HYPERVISOR_OVERLAY_LINUX_VM_DIR) ;\ cp -r $(LINUX_VM_IMAGE) $(HYPERVISOR_OVERLAY_LINUX_VM_DIR)/ ;\ cp -r $(LINUX_VM_BUILDROOT_ROOTFS) $(HYPERVISOR_OVERLAY_LINUX_VM_DIR) ;\ - cp $(ACE_DIR)/tools/ace-tap $(LINUX_VM_OVERLAY_WORK_DIR)/ + cp -r $(LINUX_VM_BUILDROOT_WORK_DIR)/images/rootfs.cpio $(HYPERVISOR_OVERLAY_LINUX_VM_DIR) ;\ + mkdir -p $(LINUX_VM_OVERLAY_WORK_ROOT_DIR); \ + cp $(ACE_DIR)/tools/ace-tap $(LINUX_VM_OVERLAY_WORK_ROOT_DIR)/ clean: rm -rf $(ACE_DIR) diff --git a/confidential-vms/linux_vm/configurations/busybox.config b/confidential-vms/linux_vm/configurations/busybox.config new file mode 100644 index 00000000..706f303a --- /dev/null +++ b/confidential-vms/linux_vm/configurations/busybox.config @@ -0,0 +1,6 @@ +CONFIG_MOUNT=y +CONFIG_FEATURE_MOUNT_FSTAB=y +CONFIG_UMOUNT=y +CONFIG_FEATURE_UMOUNT_ALL=y +CONFIG_CRYPTSETUP=y +CONFIG_LOSETUP=y diff --git a/confidential-vms/linux_vm/configurations/linux64-defconfig b/confidential-vms/linux_vm/configurations/linux64-defconfig index 0accb5b0..cddf085f 100644 --- a/confidential-vms/linux_vm/configurations/linux64-defconfig +++ b/confidential-vms/linux_vm/configurations/linux64-defconfig @@ -186,7 +186,7 @@ CONFIG_CHECKPOINT_RESTORE=y # CONFIG_SYSFS_DEPRECATED is not set # CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y -CONFIG_INITRAMFS_SOURCE="" +# CONFIG_INITRAMFS_SOURCE="" CONFIG_RD_GZIP=y CONFIG_RD_BZIP2=y CONFIG_RD_LZMA=y @@ -1613,6 +1613,7 @@ CONFIG_DM_BIO_PRISON=m CONFIG_DM_PERSISTENT_DATA=m # CONFIG_DM_UNSTRIPED is not set # CONFIG_DM_CRYPT is not set +CONFIG_DM_CRYPT=y # CONFIG_DM_SNAPSHOT is not set CONFIG_DM_THIN_PROVISIONING=m # CONFIG_DM_CACHE is not set @@ -4404,7 +4405,7 @@ CONFIG_CRYPTO_RSA=y # # Block ciphers # -CONFIG_CRYPTO_AES=m +CONFIG_CRYPTO_AES=y # CONFIG_CRYPTO_AES_TI is not set # CONFIG_CRYPTO_ANUBIS is not set # CONFIG_CRYPTO_ARIA is not set @@ -4439,6 +4440,7 @@ CONFIG_CRYPTO_CTR=m # CONFIG_CRYPTO_OFB is not set # CONFIG_CRYPTO_PCBC is not set # CONFIG_CRYPTO_XTS is not set +CONFIG_CRYPTO_XTS=y # end of Length-preserving ciphers and modes # @@ -4466,8 +4468,8 @@ CONFIG_CRYPTO_HMAC=m # CONFIG_CRYPTO_POLY1305 is not set # CONFIG_CRYPTO_RMD160 is not set CONFIG_CRYPTO_SHA1=y -CONFIG_CRYPTO_SHA256=m -CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y # CONFIG_CRYPTO_SHA3 is not set # CONFIG_CRYPTO_SM3_GENERIC is not set # CONFIG_CRYPTO_STREEBOG is not set @@ -4513,8 +4515,10 @@ CONFIG_CRYPTO_JITTERENTROPY=m # CONFIG_CRYPTO_USER_API=y CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y # CONFIG_CRYPTO_USER_API_SKCIPHER is not set # CONFIG_CRYPTO_USER_API_RNG is not set +CONFIG_CRYPTO_USER_API_RNG=y # CONFIG_CRYPTO_USER_API_AEAD is not set CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE=y # end of Userspace interface diff --git a/confidential-vms/linux_vm/configurations/qemu_riscv64_virt_defconfig b/confidential-vms/linux_vm/configurations/qemu_riscv64_virt_defconfig index d7e4851c..203d1d72 100644 --- a/confidential-vms/linux_vm/configurations/qemu_riscv64_virt_defconfig +++ b/confidential-vms/linux_vm/configurations/qemu_riscv64_virt_defconfig @@ -1,5 +1,5 @@ -BR2_TARGET_GENERIC_HOSTNAME="cvm" -BR2_TARGET_GENERIC_ISSUE="Welcome to Confidential VM!" +BR2_TARGET_GENERIC_HOSTNAME="confidential_vm" +BR2_TARGET_GENERIC_ISSUE="Welcome to ACE Confidential VM!" # Architecture BR2_riscv=y BR2_RISCV_64=y @@ -7,15 +7,15 @@ BR2_RISCV_g=y BR2_RISCV_ABI_LP64D=y BR2_GCC_TARGET_ABI="lp64d" # -BR2_TOOLCHAIN_EXTERNAL=y -BR2_TOOLCHAIN_EXTERNAL_PATH="$(RISCV)" -BR2_TOOLCHAIN_EXTERNAL_CUSTOM_PREFIX="$(ARCH)-unknown-linux-gnu" -BR2_TOOLCHAIN_EXTERNAL_GCC_12=y -BR2_TOOLCHAIN_EXTERNAL_HEADERS_5_10=y -BR2_TOOLCHAIN_EXTERNAL_CUSTOM_GLIBC=y -#BR2_TOOLCHAIN_EXTERNAL_INET_RPC=y -BR2_TOOLCHAIN_EXTERNAL_INET_RPC=n -BR2_TOOLCHAIN_EXTERNAL_CXX=y +# BR2_TOOLCHAIN_EXTERNAL=y +# BR2_TOOLCHAIN_EXTERNAL_PATH="$(RISCV)" +# BR2_TOOLCHAIN_EXTERNAL_CUSTOM_PREFIX="$(ARCH)-unknown-linux-gnu" +# BR2_TOOLCHAIN_EXTERNAL_GCC_12=y +# BR2_TOOLCHAIN_EXTERNAL_HEADERS_5_10=y +# BR2_TOOLCHAIN_EXTERNAL_CUSTOM_GLIBC=y +# #BR2_TOOLCHAIN_EXTERNAL_INET_RPC=y +# BR2_TOOLCHAIN_EXTERNAL_INET_RPC=n +# BR2_TOOLCHAIN_EXTERNAL_CXX=y # Shell BR2_PACKAGE_BUSYBOX_SHOW_OTHERS=y @@ -29,8 +29,8 @@ BR2_TARGET_GENERIC_GETTY_PORT="ttyS0" BR2_TARGET_GENERIC_ROOT_PASSWD="passwd" # Filesystem -BR2_TARGET_ROOTFS_EXT2=y BR2_ROOTFS_OVERLAY="" +BR2_TARGET_ROOTFS_EXT2=y BR2_TARGET_ROOTFS_EXT2_2=n BR2_TARGET_ROOTFS_EXT2_3=n BR2_TARGET_ROOTFS_EXT2_4=y @@ -38,6 +38,9 @@ BR2_TARGET_ROOTFS_EXT2_SIZE="5G" # Kernel BR2_LINUX_KERNEL=y +# BR2_LINUX_KERNEL_CUSTOM_GIT=y +# BR2_LINUX_KERNEL_CUSTOM_REPO_URL="" +# BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="" BR2_LINUX_KERNEL_CUSTOM_VERSION=y BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.3-rc4" BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_6_3=y @@ -52,11 +55,6 @@ BR2_LINUX_KERNEL_PATCH="" # development with custom Linux kernel sources BR2_PACKAGE_OVERRIDE_FILE="" -# Bootloader -# BR2_TARGET_OPENSBI=y -# BR2_TARGET_OPENSBI_USE_PLAT=y -# BR2_TARGET_OPENSBI_PLAT="qemu/virt" - # Packages BR2_PACKAGE_DROPBEAR=y @@ -75,6 +73,37 @@ BR2_TARGET_OPENSBI_PLAT="generic" BR2_PER_PACKAGE_DIRECTORIES=y BR2_VERBOSE=0 +BR2_PACKAGE_NVME=y + +# Initramfs +# BR2_TARGET_ROOTFS_INITRAMFS=y # possible not needed when we do not require kernel to be rebuild with initramfs +BR2_TARGET_ROOTFS_CPIO=y +BR2_PACKAGE_BUSYBOX=y +BR2_INIT_BUSYBOX=y +BR2_PACKAGE_HOST_DRACUT=y +BR2_TARGET_ROOTFS_CPIO_DRACUT=y +# dm-crypt +BR2_PACKAGE_HOST_GENIMAGE=y +BR2_PACKAGE_CRYPTSETUP=y +BR2_PACKAGE_HOST_CRYPTSETUP=y +BR2_PACKAGE_E2FSPROGS=y + +# below not needed? +# BR2_PACKAGE_SYSTEMD=y +# BR2_PACKAGE_SYSTEMD_BOOT=y +# BR2_PACKAGE_SYSTEMD_INITRD=y +# BR2_PACKAGE_SYSTEMD_KERNELINSTALL=y +# BR2_TARGET_GRUB2_BOOT_PARTITION="cd" +# BR2_ROOTFS_POST_IMAGE_SCRIPT="" +# BR2_PACKAGE_PKGCONF=y +# BR2_SYSTEM_BIN_SH_BUSYBOX=y + +# BR2_PACKAGE_HOST_UBOOT_TOOLS=y +# BR2_TARGET_UBOOT=y + +# BR2_TARGET_GRUB2_HAS_EFI_BOOT=y +# BR2_TARGET_GRUB2_RISCV64_EFI=y +# BR2_TARGET_GRUB2=y +# BR2_TARGET_GRUB2_INSTALL_TOOLS=y -BR2_PACKAGE_NVME=y diff --git a/confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh b/confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh index f31e1920..b4153072 100755 --- a/confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh +++ b/confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh @@ -6,6 +6,7 @@ QEMU_CMD=qemu-system-riscv64 KERNEL=/root/linux_vm/Image DRIVE=/root/linux_vm/rootfs.ext2 +INITRAMFS=/root/linux_vm/rootfs.cpio HOST_PORT="$((3000 + RANDOM % 3000))" INTERACTIVE="-nographic" @@ -54,8 +55,9 @@ ${QEMU_CMD} ${DEBUG_OPTIONS} \ -machine virt -cpu rv64,f=true -smp ${SMP} -m ${MEMORY} \ -kernel ${KERNEL} \ -seed 0 \ + -initrd ${INITRAMFS} \ -global virtio-mmio.force-legacy=false \ - -append "console=ttyS0 ro root=/dev/vda swiotlb=mmnn,force promote_to_cove_guest" \ + -append "console=ttyS0 ro swiotlb=mmnn,force promote_to_cove_guest" \ -device virtio-blk-pci,drive=hd0,iommu_platform=on,disable-legacy=on,disable-modern=off \ -drive if=none,format=raw,file=${DRIVE},id=hd0 \ -device virtio-net-pci,netdev=net0,iommu_platform=on,disable-legacy=on,disable-modern=off \ diff --git a/confidential-vms/linux_vm/overlay/root/this_is_confidential_vm_filesystem b/confidential-vms/linux_vm/overlay/root/this_is_confidential_vm_filesystem new file mode 100644 index 00000000..e69de29b diff --git a/hypervisor/configurations/qemu_riscv64_virt_defconfig b/hypervisor/configurations/qemu_riscv64_virt_defconfig index d4d33e73..a37d1895 100644 --- a/hypervisor/configurations/qemu_riscv64_virt_defconfig +++ b/hypervisor/configurations/qemu_riscv64_virt_defconfig @@ -7,15 +7,15 @@ BR2_RISCV_g=y BR2_RISCV_ABI_LP64D=y BR2_GCC_TARGET_ABI="lp64d" # -BR2_TOOLCHAIN_EXTERNAL=y -BR2_TOOLCHAIN_EXTERNAL_PATH="$(RISCV)" -BR2_TOOLCHAIN_EXTERNAL_CUSTOM_PREFIX="$(ARCH)-unknown-linux-gnu" -BR2_TOOLCHAIN_EXTERNAL_GCC_12=y -BR2_TOOLCHAIN_EXTERNAL_HEADERS_5_10=y -BR2_TOOLCHAIN_EXTERNAL_CUSTOM_GLIBC=y -#BR2_TOOLCHAIN_EXTERNAL_INET_RPC=y -BR2_TOOLCHAIN_EXTERNAL_INET_RPC=n -BR2_TOOLCHAIN_EXTERNAL_CXX=y +# BR2_TOOLCHAIN_EXTERNAL=y +# BR2_TOOLCHAIN_EXTERNAL_PATH="$(RISCV)" +# BR2_TOOLCHAIN_EXTERNAL_CUSTOM_PREFIX="$(ARCH)-unknown-linux-gnu" +# BR2_TOOLCHAIN_EXTERNAL_GCC_12=y +# BR2_TOOLCHAIN_EXTERNAL_HEADERS_5_10=y +# BR2_TOOLCHAIN_EXTERNAL_CUSTOM_GLIBC=y +# #BR2_TOOLCHAIN_EXTERNAL_INET_RPC=y +# BR2_TOOLCHAIN_EXTERNAL_INET_RPC=n +# BR2_TOOLCHAIN_EXTERNAL_CXX=y # Shell BR2_PACKAGE_BUSYBOX_SHOW_OTHERS=y diff --git a/security-monitor/Cargo.toml b/security-monitor/Cargo.toml index e8dd93fb..86f7f137 100644 --- a/security-monitor/Cargo.toml +++ b/security-monitor/Cargo.toml @@ -27,6 +27,9 @@ riscv-decode = "0.2" # The `spin` crate provides synchronization primitives (Mutexes etc) using spinlocks spin = {version="0.9", default-features = false, features = ["once", "rwlock", "spin_mutex"]} +# This crates provides functionality to parse TEE attestation payload. +tap = {path = "rust-crates/cove_tap", features = ["parser"]} + # Used to measure confidential VM, required for attestation sha2 = { version = "0.10", default-features = false } diff --git a/security-monitor/rust-crates/cove_tap/Cargo.toml b/security-monitor/rust-crates/cove_tap/Cargo.toml new file mode 100644 index 00000000..f5843899 --- /dev/null +++ b/security-monitor/rust-crates/cove_tap/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "tap" +version = "0.1.0" +authors = ["Wojciech Ozga "] +description = "Library to parse the CoVE's TEE attestation payload" +edition = "2021" + +[dependencies] +#rsa = "0.9" # to create lockboxes: encrypt symetric key using public keys of target TEEs +#rand = "0.8" # to generate symmetric key used to encrypted payload + +# for symmetric encryption of payload +aes-gcm = {version="0.10.3", default-features = false, features=["aes", "alloc"]} + +# provides macros that help removing boilerplate code in rust error handling +thiserror-no-std = "2.0" + +[features] +parser = [] +serializer = [] \ No newline at end of file diff --git a/security-monitor/rust-crates/cove_tap/src/error.rs b/security-monitor/rust-crates/cove_tap/src/error.rs new file mode 100644 index 00000000..7e3a6f73 --- /dev/null +++ b/security-monitor/rust-crates/cove_tap/src/error.rs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use thiserror_no_std::Error; + +#[derive(Error, Debug)] +pub enum TapError { + #[error("Unsupported TAP Lockbox algorithm {0}")] + UnsupportedTapLockboxAlgorithm(u16), + #[error("Unsupported TAP digest entry type {0}")] + UnsupportedTapDigestEntryType(u16), + #[error("Unsupported TAP digest algorithm {0}")] + UnsupportedTapDigestAlgorithm(u16), + #[error("Unsupported TAP payload encryption algorithm {0}")] + UnsupportedTapPayloadEncryptionAlgorithm(u16), + #[error("Invalid magic in the beginning of TAP")] + InvalidMagicStart(), + #[error("Invalid size of the TAP")] + InvalidSize(), + + #[error("Aes error {0}")] + AesError(#[from] aes_gcm::Error) + +} \ No newline at end of file diff --git a/security-monitor/rust-crates/cove_tap/src/lib.rs b/security-monitor/rust-crates/cove_tap/src/lib.rs new file mode 100644 index 00000000..5b0f39ea --- /dev/null +++ b/security-monitor/rust-crates/cove_tap/src/lib.rs @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +#![no_std] +#![no_main] +extern crate alloc; + +mod error; +#[cfg(feature = "parser")] +mod parser; +mod spec; +#[cfg(feature = "serializer")] +mod serializer; + +#[cfg(feature = "parser")] +pub use parser::TeeAttestationPayloadParser; + +#[cfg(feature = "serializer")] +pub use serializer::TeeAttestationPayloadSerializer; + +pub use spec::*; +pub use error::*; \ No newline at end of file diff --git a/security-monitor/rust-crates/cove_tap/src/parser.rs b/security-monitor/rust-crates/cove_tap/src/parser.rs new file mode 100644 index 00000000..d3c2e690 --- /dev/null +++ b/security-monitor/rust-crates/cove_tap/src/parser.rs @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 + +pub use crate::error::TapError; +use alloc::vec::Vec; +use alloc::vec; +use crate::spec::*; + +pub struct TeeAttestationPayloadParser { + pub pointer: *const u8, + pub size: usize, +} + +impl TeeAttestationPayloadParser { + pub fn from_raw_pointer(pointer: *const u8, size: usize) -> Result { + Ok(Self { + pointer, size + }) + } + + pub fn parse_and_verify(&mut self) -> Result { + if self.read_u16()? != ACE_MAGIC_TAP_START { + return Err(TapError::InvalidMagicStart()); + } + if self.read_u16()? as usize != self.size { + return Err(TapError::InvalidSize()); + } + let number_of_lockboxes = self.read_u16()?; + let mut lockboxes = vec![]; + for _ in 0..number_of_lockboxes { + let size = self.read_u16()? as usize; + let name = self.read_u64()?; + let algorithm = TapLockboxAlgorithm::from_u16(self.read_u16()?)?; + let value = self.read_exact(size-10)?; + lockboxes.push(Lockbox { + name, + algorithm, + value + }); + } + // TODO: recover symmetric key + let symmetric_key = [0u8; 32]; + + let payload_encryption_algorithm = TapPayloadEncryptionAlgorithm::from_u16(self.read_u16()?)?; + match payload_encryption_algorithm { + TapPayloadEncryptionAlgorithm::Debug => {}, + TapPayloadEncryptionAlgorithm::AesGcm256 => self.decrypt_aes_gcm_256(symmetric_key)?, + } + + let number_of_digests = self.read_u16()?; + let mut digests = vec![]; + for _ in 0..number_of_digests { + let size = self.read_u16()? as usize; + let entry_type = TapDigestEntryType::from_u16(self.read_u16()?)?; + let algorithm = TapDigestAlgorithm::from_u16(self.read_u16()?)?; + let value = self.read_exact(size-4)?; + digests.push(TapDigest { + entry_type, + algorithm, + value + }); + } + + let number_of_secrets = self.read_u16()?; + let mut secrets = vec![]; + for _ in 0..number_of_secrets { + let size = self.read_u16()? as usize; + let name = self.read_u64()? as u64; + let value = self.read_exact(size-10)?; + secrets.push(TapSecret { name, value }); + } + + Ok(TeeAttestationPayload { + lockboxes, + digests, + secrets, + }) + } + + fn decrypt_aes_gcm_256(&mut self, symmetric_key: [u8; 32]) -> Result<(), TapError> { + use aes_gcm::{AeadInPlace, Aes256Gcm, Key, KeyInit, Tag, Nonce}; + + let nonce_size = self.read_u16()? as usize; + let nonce = self.read_exact(nonce_size)?; + let tag_size = self.read_u16()? as usize; + let tag = self.read_exact(tag_size)?; + let payload_size = self.read_u16()? as usize; + + let key: Key = symmetric_key.into(); + let cipher = Aes256Gcm::new(&key); + let nonce = Nonce::from_slice(&nonce); + let tag = Tag::from_slice(&tag); + let mut data_slice = unsafe{ core::slice::from_raw_parts_mut(self.pointer as *mut u8, payload_size) }; + cipher.decrypt_in_place_detached(nonce, b"", &mut data_slice, &tag)?; + Ok(()) + } + + fn read_u16(&mut self) -> Result { + let value = unsafe { (self.pointer as *const u16).read_volatile() }; + self.pointer = self.pointer.wrapping_add(2); + Ok(value) + } + + fn read_u64(&mut self) -> Result { + let value = unsafe { (self.pointer as *const u64).read_volatile() }; + self.pointer = self.pointer.wrapping_add(8); + Ok(value) + } + + fn read_exact(&mut self, size: usize) -> Result, TapError> { + let mut result = vec![]; + for _ in 0..size { + let value = unsafe { self.pointer.read_volatile() }; + self.pointer = self.pointer.wrapping_add(1); + result.push(value); + } + Ok(result) + } +} \ No newline at end of file diff --git a/security-monitor/rust-crates/cove_tap/src/serializer.rs b/security-monitor/rust-crates/cove_tap/src/serializer.rs new file mode 100644 index 00000000..f662cd12 --- /dev/null +++ b/security-monitor/rust-crates/cove_tap/src/serializer.rs @@ -0,0 +1,119 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use crate::error::TapError; +use alloc::vec; +use crate::spec::*; +use alloc::vec::Vec; + +pub struct TeeAttestationPayloadSerializer { + +} + +impl TeeAttestationPayloadSerializer { + pub fn new() -> Self { + Self {} + } + + pub fn serialize(&self, mut payload: TeeAttestationPayload) -> Result, TapError> { + let digests = self.serialize_digests(&mut payload)?; + let secrets = self.serialize_secrets(&mut payload)?; + let mut encrypted_part = self.encrypt_aes_gcm_256(digests, secrets)?; + let mut lockboxes = self.serialize_lockboxes(&mut payload)?; + + let total_size = lockboxes.len() + encrypted_part.len() + ACE_FOOTER_SIZE; // 4 bytes for footer + + let mut result = vec![]; + result.append(&mut ACE_MAGIC_TAP_START.to_le_bytes().to_vec()); + result.append(&mut (total_size as u16).to_le_bytes().to_vec()); + result.append(&mut lockboxes); + result.append(&mut encrypted_part); + result.append(&mut ACE_MAGIC_TAP_END.to_le_bytes().to_vec()); + result.append(&mut ((total_size + ACE_HEADER_SIZE) as u16).to_le_bytes().to_vec()); + + Ok(result) + } + + fn serialize_lockboxes(&self, payload: &mut TeeAttestationPayload) -> Result, TapError> { + // TODO: sanity check: lockboxes < 1024 + let mut result = vec![]; + result.append(&mut (payload.lockboxes.len() as u16).to_le_bytes().to_vec()); + for mut lockbox in payload.lockboxes.drain(..) { + let entry_size = lockbox.value.len() + 10; + result.append(&mut (entry_size as u16).to_le_bytes().to_vec()); + result.append(&mut (lockbox.name as u64).to_le_bytes().to_vec()); + result.append(&mut (lockbox.algorithm as u16).to_le_bytes().to_vec()); + result.append(&mut lockbox.value); + } + Ok(result) + } + + fn serialize_digests(&self, payload: &mut TeeAttestationPayload) -> Result, TapError> { + // TODO: sanity check: digests < 1024 + let mut result = vec![]; + result.append(&mut (payload.digests.len() as u16).to_le_bytes().to_vec()); + for mut digest in payload.digests.drain(..) { + let entry_size = digest.value.len() + 2 + 2; + result.append(&mut (entry_size as u16).to_le_bytes().to_vec()); + result.append(&mut (digest.entry_type as u16).to_le_bytes().to_vec()); + result.append(&mut (digest.algorithm as u16).to_le_bytes().to_vec()); + result.append(&mut digest.value); + } + Ok(result) + } + + fn serialize_secrets(&self, payload: &mut TeeAttestationPayload) -> Result, TapError> { + // TODO: sanity check: secrets < 1024 + let mut result = vec![]; + result.append(&mut (payload.secrets.len() as u16).to_le_bytes().to_vec()); + for mut secret in payload.secrets.drain(..) { + let entry_size = secret.value.len() + 10; + result.append(&mut (entry_size as u16).to_le_bytes().to_vec()); + result.append(&mut (secret.name).to_le_bytes().to_vec()); + result.append(&mut secret.value); + } + Ok(result) + } + + fn encrypt_aes_gcm_256(&self, mut digests: Vec, mut secrets: Vec) -> Result, TapError> { + use aes_gcm::{AeadInPlace, Aes256Gcm, Key, KeyInit}; + // use rand::rngs::OsRng; + + let mut encrypted_part = vec![]; + encrypted_part.append(&mut digests); + encrypted_part.append(&mut secrets); + + let symmetric_key = [0u8; 32]; + // rand::thread_rng().fill_bytes(&mut symmetric_key); + + let key: Key = symmetric_key.into(); + let cipher = Aes256Gcm::new(&key); + let nonce = [0u8; 12]; + // let nonce = Aes256Gcm::generate_nonce(&mut OsRng); + let nonce = aes_gcm::Nonce::from_slice(&nonce); + let tag = cipher + .encrypt_in_place_detached(&nonce, b"", &mut encrypted_part) + .unwrap(); + + let mut result = vec![]; + result.append(&mut (TapPayloadEncryptionAlgorithm::AesGcm256 as u16).to_le_bytes().to_vec()); + result.append(&mut (nonce.as_slice().len() as u16).to_le_bytes().to_vec()); + result.append(&mut nonce.as_slice().to_vec()); + result.append(&mut (tag.as_slice().len() as u16).to_le_bytes().to_vec()); + result.append(&mut tag.as_slice().to_vec()); + result.append(&mut (encrypted_part.len() as u16).to_le_bytes().to_vec()); + result.append(&mut encrypted_part); + + Ok(result) + } + + // fn encrypt_rsa_2048_sha256_oasp(value: &[u8], public_key_file: String) -> Result, Error> { + // use rsa::pkcs1::DecodeRsaPublicKey; + // let public_key_pem: Vec = std::fs::read(public_key_file.clone()) + // .map_err(|_| Error::CannotOpenFile(public_key_file))?; + // let public_key = rsa::RsaPublicKey::from_pkcs1_pem(&String::from_utf8_lossy(&public_key_pem))?; + // let padding = rsa::Oaep::new::(); + // let encrypted_data = public_key.encrypt(&mut rand::thread_rng(), padding, value)?; + // Ok(encrypted_data) + // } +} \ No newline at end of file diff --git a/security-monitor/rust-crates/cove_tap/src/spec.rs b/security-monitor/rust-crates/cove_tap/src/spec.rs new file mode 100644 index 00000000..904fb140 --- /dev/null +++ b/security-monitor/rust-crates/cove_tap/src/spec.rs @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use crate::error::TapError; +use alloc::vec::Vec; + +pub const ACE_HEADER_SIZE: usize = 4; +pub const ACE_FOOTER_SIZE: usize = 4; +pub const ACE_MAGIC_TAP_START: u16 = 0xACE0; +pub const ACE_MAGIC_TAP_END: u16 = 0xACE1; + +pub struct TeeAttestationPayload { + pub lockboxes: Vec, + pub digests: Vec, + pub secrets: Vec, +} + +pub struct Lockbox { + pub name: u64, + pub algorithm: TapLockboxAlgorithm, + pub value: Vec, +} + +#[repr(u16)] +#[derive(Debug)] +pub enum TapLockboxAlgorithm { + Debug = 0, + Rsa2048Sha256Oasp = 1, +} + +impl TapLockboxAlgorithm { + pub fn from_u16(value: u16) -> Result { + match value { + 0 => Ok(Self::Debug), + 1 => Ok(Self::Rsa2048Sha256Oasp), + v => Err(TapError::UnsupportedTapLockboxAlgorithm(v)), + } + } +} + +pub struct TapDigest { + pub entry_type: TapDigestEntryType, + pub algorithm: TapDigestAlgorithm, + pub value: Vec, +} + +#[repr(u16)] +#[derive(Debug)] +pub enum TapDigestEntryType { + Kernel = 0, + KernelCommandLine = 1, + Initramfs = 2, +} + +impl TapDigestEntryType { + pub fn from_u16(value: u16) -> Result { + match value { + 0 => Ok(Self::Kernel), + 1 => Ok(Self::KernelCommandLine), + 2 => Ok(Self::Initramfs), + v => Err(TapError::UnsupportedTapDigestEntryType(v)), + } + } +} + +#[repr(u16)] +#[derive(Debug)] +pub enum TapDigestAlgorithm { + Debug = 0, + Sha512 = 1, +} + +impl TapDigestAlgorithm { + pub fn from_u16(value: u16) -> Result { + match value { + 0 => Ok(Self::Debug), + 1 => Ok(Self::Sha512), + v => Err(TapError::UnsupportedTapDigestAlgorithm(v)), + } + } + + pub fn digest_size(&self) -> u16 { + match self { + &Self::Debug => 0, + &Self::Sha512 => 512 / 8, + } + } +} + +pub struct TapSecret { + pub name: u64, + pub value: Vec, +} + +#[repr(u16)] +#[derive(Debug)] +pub enum TapPayloadEncryptionAlgorithm { + Debug = 0, + AesGcm256 = 1, +} + +impl TapPayloadEncryptionAlgorithm { + pub fn from_u16(value: u16) -> Result { + match value { + 0 => Ok(Self::Debug), + 1 => Ok(Self::AesGcm256), + v => Err(TapError::UnsupportedTapPayloadEncryptionAlgorithm(v)), + } + } +} \ No newline at end of file diff --git a/security-monitor/src/error.rs b/security-monitor/src/error.rs index 494788ae..23ee695f 100644 --- a/security-monitor/src/error.rs +++ b/security-monitor/src/error.rs @@ -35,14 +35,18 @@ pub enum Error { TooManyConfidentialVms(), #[error("Unsupported paging mode")] UnsupportedPagingMode(), + #[error("Incorrectly aligned FDT")] + FdtNotAlignedTo64Bits(), #[error("FDT size is invalid. Expecting at least 40 bytes and maximum 40960 bytes")] FdtInvalidSize(), #[error("Exceeded the max number of harts per VM")] InvalidNumberOfHartsInFdt(), - #[error("Incorrectly aligned authentication blob")] - AuthBlobNotAlignedTo64Bits(), - #[error("Authentication blob size is invalid.")] + #[error("Incorrectly aligned authentication payload")] + AuthBlobNotAlignedTo32Bits(), + #[error("Authentication payload size is invalid.")] AuthBlobInvalidSize(), + #[error("Error when parsing TEE attestation payload: {0}")] + AttestationPayloadParsingError(#[from] tap::TapError), /* SBI invalid address */ #[error("Address is not aligned")] @@ -122,9 +126,10 @@ impl Error { Self::InvalidCall(_, _) => SBI_ERR_INVALID_PARAM as usize, Self::PageTableCorrupted() => SBI_ERR_INVALID_PARAM as usize, Self::UnsupportedPagingMode() => SBI_ERR_INVALID_PARAM as usize, + Self::FdtNotAlignedTo64Bits() => SBI_ERR_INVALID_PARAM as usize, Self::FdtInvalidSize() => SBI_ERR_INVALID_PARAM as usize, Self::InvalidNumberOfHartsInFdt() => SBI_ERR_INVALID_PARAM as usize, - Self::AuthBlobNotAlignedTo64Bits() => SBI_ERR_INVALID_PARAM as usize, + Self::AuthBlobNotAlignedTo32Bits() => SBI_ERR_INVALID_PARAM as usize, Self::AuthBlobInvalidSize() => SBI_ERR_INVALID_PARAM as usize, Self::DeviceTreeError(_) => SBI_ERR_INVALID_PARAM as usize, diff --git a/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/promote_to_confidential_vm.rs b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/promote_to_confidential_vm.rs index 9a3f0700..e85e4f39 100644 --- a/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/promote_to_confidential_vm.rs +++ b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/promote_to_confidential_vm.rs @@ -34,7 +34,7 @@ pub struct PromoteToConfidentialVm { impl PromoteToConfidentialVm { const FDT_ALIGNMENT_IN_BYTES: usize = 8; - const TAP_ALIGNMENT_IN_BYTES: usize = 8; + const TAP_ALIGNMENT_IN_BYTES: usize = 4; const BOOT_HART_ID: usize = 0; pub fn from_hypervisor_hart(hypervisor_hart: &HypervisorHart) -> Self { @@ -64,6 +64,7 @@ impl PromoteToConfidentialVm { fn create_confidential_vm(&self, shared_memory: &NaclSharedMemory) -> Result { debug!("Promoting a VM into a confidential VM"); + debug!("Auth blob: {:?}", self.auth_blob_address); // Copy the entire VM's state to the confidential memory, recreating the MMU configuration. let memory_protector = ConfidentialVmMemoryProtector::from_vm_state(&self.hgatp)?; @@ -101,7 +102,7 @@ impl PromoteToConfidentialVm { let address_in_confidential_memory = memory_protector.translate_address(&self.fdt_address)?; // Make sure that the address is 8-bytes aligned. Once we ensure this, we can safely read 8 bytes because they must be within // the page boundary. These 8 bytes should contain the `magic` (first 4 bytes) and `size` (next 4 bytes). - ensure!(address_in_confidential_memory.is_aligned_to(Self::FDT_ALIGNMENT_IN_BYTES), Error::AuthBlobNotAlignedTo64Bits())?; + ensure!(address_in_confidential_memory.is_aligned_to(Self::FDT_ALIGNMENT_IN_BYTES), Error::FdtNotAlignedTo64Bits())?; // Below use of unsafe is ok because (1) the security monitor owns the memory region containing the data of the not-yet-created // confidential VM's and (2) there is only one physical hart executing this code. let fdt_total_size = unsafe { FlattenedDeviceTree::total_size(address_in_confidential_memory.to_ptr())? }; @@ -132,21 +133,34 @@ impl PromoteToConfidentialVm { fn authenticate_and_authorize_vm( &self, memory_protector: &ConfidentialVmMemoryProtector, _measurements: &StaticMeasurements, ) -> Result<(), Error> { + use tap::TeeAttestationPayloadParser; if let Some(blob_address) = self.auth_blob_address { debug!("Performing local attestation"); let address_in_confidential_memory = memory_protector.translate_address(&blob_address)?; // Make sure that the address is 8-bytes aligned. Once we ensure this, we can safely read 8 bytes because they must be within // the page boundary. These 8 bytes should contain the `magic` (first 4 bytes) and `size` (next 4 bytes). - ensure!(address_in_confidential_memory.is_aligned_to(Self::TAP_ALIGNMENT_IN_BYTES), Error::AuthBlobNotAlignedTo64Bits())?; + ensure!(address_in_confidential_memory.is_aligned_to(Self::TAP_ALIGNMENT_IN_BYTES), Error::AuthBlobNotAlignedTo32Bits())?; // Below use of unsafe is ok because (1) the security monitor owns the memory region containing the data of the not-yet-created // confidential VM's and (2) there is only one physical hart executing this code. - let magic_and_size: usize = unsafe { address_in_confidential_memory.read_volatile() }; - let auth_blob_total_size: usize = u32::from_be((magic_and_size >> 32) as u32) as usize; + let header: u64 = + unsafe { address_in_confidential_memory.read_volatile().try_into().map_err(|_| Error::AuthBlobNotAlignedTo32Bits())? }; + let total_size = ((header >> 16) & 0xFFFF) as usize; // To work with the authentication blob, we must have it as a continous chunk of memory. We accept only authentication blobs // that fit within 2MiB - ensure!(auth_blob_total_size.div_ceil(PageSize::Size2MiB.in_bytes()) == 1, Error::AuthBlobInvalidSize())?; - let large_page = Self::relocate(memory_protector, &blob_address, auth_blob_total_size)?; - + ensure!(total_size.div_ceil(PageSize::Size2MiB.in_bytes()) == 1, Error::AuthBlobInvalidSize())?; + let large_page = Self::relocate(memory_protector, &blob_address, total_size)?; + let mut parser = unsafe { TeeAttestationPayloadParser::from_raw_pointer(large_page.address().to_ptr(), total_size)? }; + let tap = parser.parse_and_verify()?; + + debug!("TAP contains {} lockboxes", tap.lockboxes.len()); + debug!("TAP contains {} secrets", tap.secrets.len()); + for digest in tap.digests.iter() { + use crate::alloc::string::ToString; + use alloc::format; + use alloc::string::String; + let hex: String = digest.value.iter().map(|b| format!("{:02x}", b).to_string()).collect::>().join(" "); + debug!("TAP digest: {:?} {:?} {}", digest.entry_type, digest.algorithm, hex); + } // TODO: local attestation should occure here, i.e., we should verify the authentication blob signature // TODO: compare measurements to the one signed in the authentication blob diff --git a/tools/local_attestation/Cargo.toml b/tools/local_attestation/Cargo.toml index d0369d8d..f69d959c 100644 --- a/tools/local_attestation/Cargo.toml +++ b/tools/local_attestation/Cargo.toml @@ -15,4 +15,6 @@ rsa = "0.9" # to create lockboxes: encrypt symetric key using public keys of tar rand = "0.8" # to generate symmetric key used to encrypted payload aes-gcm = "0.10.3" # for symmetric encryption of payload +tap = {path="../../security-monitor/rust-crates/cove_tap", features=["serializer"]} + thiserror = "1.0" # provides macros that help removing boilerplate code in the rust error handling diff --git a/tools/local_attestation/src/append.rs b/tools/local_attestation/src/append.rs index e09ca8ff..8b0de2ae 100644 --- a/tools/local_attestation/src/append.rs +++ b/tools/local_attestation/src/append.rs @@ -2,12 +2,14 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use crate::error::Error; -use byteorder::{LittleEndian, WriteBytesExt}; +use byteorder::WriteBytesExt; use std::fs::OpenOptions; +use std::io::Seek; +use std::io::SeekFrom; pub fn append_tap( input_file: String, - tap_file: String, + tap_file_name: String, output_file: Option, ) -> Result<(), Error> { let output_file_name = match output_file { @@ -18,17 +20,31 @@ pub fn append_tap( Some(f) => f, None => input_file, }; - let mut file1 = OpenOptions::new() + let output_file_size = file_size(&output_file_name)?; + + let mut output_file = OpenOptions::new() .write(true) .append(true) .open(output_file_name)?; - let mut file2 = OpenOptions::new().read(true).open(tap_file)?; - let tap_size_in_bytes = std::io::copy(&mut file2, &mut file1)?; - file1 - .write_u32::(crate::constants::ACE_TAP_MAGIC) - .unwrap(); - file1 - .write_u16::(tap_size_in_bytes.try_into().unwrap()) - .unwrap(); + + // TAP must be 8-bytes aligned + let mut aligned = (output_file_size >> 3) << 3; + if aligned < output_file_size { + aligned = ((output_file_size >> 3) + 1) << 3; + } + let padding_size_in_bytes = aligned - output_file_size; + for _ in 0..padding_size_in_bytes { + output_file.write_u8(0u8)?; + } + + let mut tap_file = OpenOptions::new().read(true).open(tap_file_name)?; + std::io::copy(&mut tap_file, &mut output_file)?; Ok(()) } + +fn file_size(file_name: &str) -> Result { + Ok(OpenOptions::new() + .read(true) + .open(file_name)? + .seek(SeekFrom::End(0))?) +} diff --git a/tools/local_attestation/src/constants.rs b/tools/local_attestation/src/constants.rs deleted file mode 100644 index f05534f6..00000000 --- a/tools/local_attestation/src/constants.rs +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IBM Corporation -// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich -// SPDX-License-Identifier: Apache-2.0 - -pub const ACE_TAP_MAGIC: u32 = 0xACEACE00; - -#[repr(u8)] -pub enum TapDigestEntryType { - Kernel, - KernelCommandLine, - Initramfs, -} - -#[repr(u8)] -pub enum TapDigestAlgorithm { - Sha512, -} - -impl TapDigestAlgorithm { - pub fn digest_size(&self) -> u16 { - match self { - &Self::Sha512 => 512 / 8, - } - } -} - -#[repr(u8)] -pub enum TapLockboxAlgorithm { - Rsa_2048_Sha256_OASP = 0, -} diff --git a/tools/local_attestation/src/error.rs b/tools/local_attestation/src/error.rs index 0bdf19ef..b9f860ed 100644 --- a/tools/local_attestation/src/error.rs +++ b/tools/local_attestation/src/error.rs @@ -2,6 +2,9 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 +// use thiserror_no_std::Error; +// #[derive(Error, Debug)] + #[derive(thiserror::Error, Debug)] pub enum Error { #[error("IO Error")] @@ -12,13 +15,27 @@ pub enum Error { RsaError(#[from] rsa::Error), #[error("Invalid parameter")] InvalidParameter(String), - + #[error("Tap Error")] + TapSerializerError(tap::TapError), #[error("Int casting Error")] TryFromIntErr(#[from] std::num::TryFromIntError), - - #[error("Symmetric key size does not match the HMAC function")] - InvalidSizeOfSymmetricKey(), - #[error("Cannot open file {0}")] CannotOpenFile(String), } + +impl From for Error { + fn from(err: tap::TapError) -> Self { + Error::TapSerializerError(err) + } +} + +#[macro_export] +macro_rules! ensure { + ($cond:expr, $error:expr) => { + if !$cond { + Err($error) + } else { + Ok(()) + } + }; +} diff --git a/tools/local_attestation/src/generate.rs b/tools/local_attestation/src/generate.rs index e71ab384..267201df 100644 --- a/tools/local_attestation/src/generate.rs +++ b/tools/local_attestation/src/generate.rs @@ -1,17 +1,20 @@ // SPDX-FileCopyrightText: 2024 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 +use crate::ensure; use crate::error::Error; -use byteorder::{LittleEndian, WriteBytesExt}; -use rand::RngCore; use sha2::Digest; use std::fs::OpenOptions; use std::io::Write; -// use hex_literal::hex; -use crate::constants::TapDigestAlgorithm; -use crate::constants::TapDigestEntryType; -use crate::constants::TapLockboxAlgorithm; +use tap::Lockbox; +use tap::TapDigest; +use tap::TapDigestAlgorithm; +use tap::TapDigestEntryType; +use tap::TapLockboxAlgorithm; +use tap::TapSecret; +use tap::TeeAttestationPayload; +use tap::TeeAttestationPayloadSerializer; pub fn generate_tap( kernel_file: String, @@ -21,81 +24,55 @@ pub fn generate_tap( tee_public_keys_files: Vec, output_file: String, ) -> Result<(), Error> { - // Sanity check: - // - number of secrets must be lower than 256 - if confidential_vm_secrets.len() >= 256 { - return Err(Error::InvalidParameter(format!( - "Confidential VM can receive maximum 256 secrets" - ))); - } - // - number of lockboxes must be lower than 1024 - if tee_public_keys_files.len() >= 1024 { - return Err(Error::InvalidParameter(format!( - "Confidential VM TAP support max 1024 lockboxes" - ))); - } + ensure!( + confidential_vm_secrets.len() < 256, + Error::InvalidParameter(format!("Confidential VM can receive maximum 256 secrets")) + )?; + ensure!( + tee_public_keys_files.len() < 1024, + Error::InvalidParameter(format!("Confidential VM TAP support max 1024 lockboxes")) + )?; - // symmetric key will be used to encrypt payload and calculate hmac - let mut symmetric_key = [0u8; 32]; - rand::thread_rng().fill_bytes(&mut symmetric_key); + // let symmetric_key = [0u8; 32]; - // generate lockboxes let mut lockboxes = vec![]; - // number of lock boxes - lockboxes.write_u16::(tee_public_keys_files.len().try_into()?)?; - for public_key_file in tee_public_keys_files.iter() { - // lock box entry: entry size; algorithm; ciphertext - let mut ciphertext = - encrypt_rsa_2048_sha256_oasp(&symmetric_key, public_key_file.to_string())?; - lockboxes.write_u16::((8 + ciphertext.len()) as u16)?; - lockboxes.write_u8(TapLockboxAlgorithm::Rsa_2048_Sha256_OASP as u8)?; - lockboxes.append(&mut ciphertext); - } + lockboxes.push(Lockbox { + name: 0u64, + algorithm: TapLockboxAlgorithm::Debug, + value: [0xFF; 16].to_vec(), + }); - let mut payload = vec![]; - // number of digests - payload.write_u8(3)?; - // first digest: entry size; entry type; algorithm; digest - payload.write_u16::(2 + TapDigestAlgorithm::Sha512.digest_size())?; - payload.write_u8(TapDigestEntryType::Kernel as u8)?; - payload.write_u8(TapDigestAlgorithm::Sha512 as u8)?; - payload.append(&mut measure_file_integrity(kernel_file)?); - // second digest - payload.write_u16::(2 + TapDigestAlgorithm::Sha512.digest_size())?; - payload.write_u8(TapDigestEntryType::KernelCommandLine as u8)?; - payload.write_u8(TapDigestAlgorithm::Sha512 as u8)?; - payload.append(&mut measure_string_integrity(kernel_commandline)?); - // third digest - payload.write_u16::(2 + TapDigestAlgorithm::Sha512.digest_size())?; - payload.write_u8(TapDigestEntryType::Initramfs as u8)?; - payload.write_u8(TapDigestAlgorithm::Sha512 as u8)?; - payload.append(&mut measure_file_integrity(initramfs_file)?); - // number of confidential VM's secrets - payload.write_u8(confidential_vm_secrets.len().try_into()?)?; - for (key, value) in confidential_vm_secrets.iter() { - // each confidential VM's secret entry consist of: entry size; key; value - payload.write_u16::((8 + value.as_bytes().len()) as u16)?; - payload.write_u64::(*key as u64)?; - payload.append(&mut value.as_bytes().to_vec()); - } - // payload's HMAC - let mut payload_hmac = hmac_sha512(&symmetric_key, &payload)?; + let mut digests = vec![]; + digests.push(TapDigest { + entry_type: TapDigestEntryType::Kernel, + algorithm: TapDigestAlgorithm::Sha512, + value: measure_file_integrity(kernel_file)?, + }); + digests.push(TapDigest { + entry_type: TapDigestEntryType::KernelCommandLine, + algorithm: TapDigestAlgorithm::Sha512, + value: measure_string_integrity(kernel_commandline)?, + }); + digests.push(TapDigest { + entry_type: TapDigestEntryType::Initramfs, + algorithm: TapDigestAlgorithm::Sha512, + value: measure_file_integrity(initramfs_file)?, + }); - // encrypt payload and hmac - let mut serialized_payload_and_hmac = vec![]; - serialized_payload_and_hmac.write_u16::(payload.len().try_into()?)?; - serialized_payload_and_hmac.append(&mut payload); - serialized_payload_and_hmac.write_u16::(payload_hmac.len().try_into()?)?; - serialized_payload_and_hmac.append(&mut payload_hmac); + let mut secrets = vec![]; + secrets.push(TapSecret { + name: 0, + value: [0xFF; 16].to_vec(), + }); - use aes_gcm::{AeadCore, AeadInPlace, Aes256Gcm, Key, KeyInit}; - use rand::rngs::OsRng; - let key: Key = symmetric_key.into(); - let cipher = Aes256Gcm::new(&key); - let nonce = Aes256Gcm::generate_nonce(&mut OsRng); - cipher - .encrypt_in_place(&nonce, b"", &mut serialized_payload_and_hmac) - .unwrap(); + let tap = TeeAttestationPayload { + lockboxes, + digests, + secrets, + }; + + let serializer = TeeAttestationPayloadSerializer::new(); + let serialized = serializer.serialize(tap)?; // write the entire TAP to the output file let mut output = OpenOptions::new() @@ -104,30 +81,11 @@ pub fn generate_tap( .append(false) .open(output_file.clone()) .map_err(|_| Error::CannotOpenFile(output_file))?; - output.write(&lockboxes)?; - output.write(&serialized_payload_and_hmac)?; + output.write(&serialized)?; Ok(()) } -fn encrypt_rsa_2048_sha256_oasp(value: &[u8], public_key_file: String) -> Result, Error> { - use rsa::pkcs1::DecodeRsaPublicKey; - let public_key_pem: Vec = std::fs::read(public_key_file.clone()) - .map_err(|_| Error::CannotOpenFile(public_key_file))?; - let public_key = rsa::RsaPublicKey::from_pkcs1_pem(&String::from_utf8_lossy(&public_key_pem))?; - let padding = rsa::Oaep::new::(); - let encrypted_data = public_key.encrypt(&mut rand::thread_rng(), padding, value)?; - Ok(encrypted_data) -} - -fn hmac_sha512(hmac_key: &[u8], payload: &[u8]) -> Result, Error> { - use hmac::Mac; - let mut hmac = hmac::Hmac::::new_from_slice(hmac_key) - .map_err(|_| Error::InvalidSizeOfSymmetricKey())?; - hmac.update(payload); - Ok(hmac.finalize().into_bytes().to_vec()) -} - fn measure_file_integrity(file: String) -> Result, Error> { let mut hasher = sha2::Sha512::new(); let mut file = diff --git a/tools/local_attestation/src/main.rs b/tools/local_attestation/src/main.rs index 26d2ff3f..8f1b756f 100644 --- a/tools/local_attestation/src/main.rs +++ b/tools/local_attestation/src/main.rs @@ -5,7 +5,6 @@ use crate::error::Error; use clap::{Parser, Subcommand}; mod append; -mod constants; mod error; mod generate;