From ce1e1752a511bd9656f2e41d2f5ed8f799a44942 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Thu, 5 Sep 2024 04:23:45 -0500 Subject: [PATCH 01/18] Adjusted to proper CoVE naming of the SBI extension Signed-off-by: Wojciech Ozga --- .../src/confidential_flow/declassify_to_confidential_vm.rs | 2 +- .../src/non_confidential_flow/finite_state_machine.rs | 2 +- .../destroy_confidential_vm.rs | 0 .../get_security_monitor_info.rs | 0 .../{cove_hypervisor_extension => cove_host_extension}/mod.rs | 0 .../promote_to_confidential_vm.rs | 0 .../run_confidential_hart.rs | 0 security-monitor/src/non_confidential_flow/handlers/mod.rs | 2 +- 8 files changed, 3 insertions(+), 3 deletions(-) rename security-monitor/src/non_confidential_flow/handlers/{cove_hypervisor_extension => cove_host_extension}/destroy_confidential_vm.rs (100%) rename security-monitor/src/non_confidential_flow/handlers/{cove_hypervisor_extension => cove_host_extension}/get_security_monitor_info.rs (100%) rename security-monitor/src/non_confidential_flow/handlers/{cove_hypervisor_extension => cove_host_extension}/mod.rs (100%) rename security-monitor/src/non_confidential_flow/handlers/{cove_hypervisor_extension => cove_host_extension}/promote_to_confidential_vm.rs (100%) rename security-monitor/src/non_confidential_flow/handlers/{cove_hypervisor_extension => cove_host_extension}/run_confidential_hart.rs (100%) diff --git a/security-monitor/src/confidential_flow/declassify_to_confidential_vm.rs b/security-monitor/src/confidential_flow/declassify_to_confidential_vm.rs index cc7384a5..37c38e06 100644 --- a/security-monitor/src/confidential_flow/declassify_to_confidential_vm.rs +++ b/security-monitor/src/confidential_flow/declassify_to_confidential_vm.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::confidential_flow::handlers::mmio::{MmioLoadResponse, MmioStoreResponse}; use crate::confidential_flow::handlers::sbi::SbiResponse; -use crate::non_confidential_flow::handlers::cove_hypervisor_extension::RunConfidentialHart; +use crate::non_confidential_flow::handlers::cove_host_extension::RunConfidentialHart; /// Declassifiers that expose part of the hypervisor's state to a confidential VM's hart. pub enum DeclassifyToConfidentialVm { diff --git a/security-monitor/src/non_confidential_flow/finite_state_machine.rs b/security-monitor/src/non_confidential_flow/finite_state_machine.rs index 9a1fef90..572d8bb3 100644 --- a/security-monitor/src/non_confidential_flow/finite_state_machine.rs +++ b/security-monitor/src/non_confidential_flow/finite_state_machine.rs @@ -11,7 +11,7 @@ use crate::core::architecture::TrapCause; use crate::core::architecture::TrapCause::*; use crate::core::control_data::{ConfidentialVmId, HardwareHart, HypervisorHart}; use crate::error::Error; -use crate::non_confidential_flow::handlers::cove_hypervisor_extension::{ +use crate::non_confidential_flow::handlers::cove_host_extension::{ DestroyConfidentialVm, GetSecurityMonitorInfo, PromoteToConfidentialVm, RunConfidentialHart, }; use crate::non_confidential_flow::handlers::nested_acceleration_extension::{NaclProbeFeature, NaclSetupSharedMemory}; diff --git a/security-monitor/src/non_confidential_flow/handlers/cove_hypervisor_extension/destroy_confidential_vm.rs b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/destroy_confidential_vm.rs similarity index 100% rename from security-monitor/src/non_confidential_flow/handlers/cove_hypervisor_extension/destroy_confidential_vm.rs rename to security-monitor/src/non_confidential_flow/handlers/cove_host_extension/destroy_confidential_vm.rs diff --git a/security-monitor/src/non_confidential_flow/handlers/cove_hypervisor_extension/get_security_monitor_info.rs b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs similarity index 100% rename from security-monitor/src/non_confidential_flow/handlers/cove_hypervisor_extension/get_security_monitor_info.rs rename to security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs diff --git a/security-monitor/src/non_confidential_flow/handlers/cove_hypervisor_extension/mod.rs b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/mod.rs similarity index 100% rename from security-monitor/src/non_confidential_flow/handlers/cove_hypervisor_extension/mod.rs rename to security-monitor/src/non_confidential_flow/handlers/cove_host_extension/mod.rs diff --git a/security-monitor/src/non_confidential_flow/handlers/cove_hypervisor_extension/promote_to_confidential_vm.rs b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/promote_to_confidential_vm.rs similarity index 100% rename from security-monitor/src/non_confidential_flow/handlers/cove_hypervisor_extension/promote_to_confidential_vm.rs rename to security-monitor/src/non_confidential_flow/handlers/cove_host_extension/promote_to_confidential_vm.rs diff --git a/security-monitor/src/non_confidential_flow/handlers/cove_hypervisor_extension/run_confidential_hart.rs b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/run_confidential_hart.rs similarity index 100% rename from security-monitor/src/non_confidential_flow/handlers/cove_hypervisor_extension/run_confidential_hart.rs rename to security-monitor/src/non_confidential_flow/handlers/cove_host_extension/run_confidential_hart.rs diff --git a/security-monitor/src/non_confidential_flow/handlers/mod.rs b/security-monitor/src/non_confidential_flow/handlers/mod.rs index 388ca6de..ac562078 100644 --- a/security-monitor/src/non_confidential_flow/handlers/mod.rs +++ b/security-monitor/src/non_confidential_flow/handlers/mod.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -pub mod cove_hypervisor_extension; +pub mod cove_host_extension; pub mod nested_acceleration_extension; pub mod opensbi; pub mod supervisor_binary_interface; From 5aa23ccaeda5be68fa66d1e74c4c6005eb9b9a88 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Thu, 5 Sep 2024 05:35:04 -0500 Subject: [PATCH 02/18] Adjusting COVH structures to the newest CoVE spec Signed-off-by: Wojciech Ozga --- .../architecture/riscv/sbi/covh_extension.rs | 28 +++++++++++-------- .../get_security_monitor_info.rs | 18 ++++++------ 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/security-monitor/src/core/architecture/riscv/sbi/covh_extension.rs b/security-monitor/src/core/architecture/riscv/sbi/covh_extension.rs index 6ea690ad..2c40fc3e 100644 --- a/security-monitor/src/core/architecture/riscv/sbi/covh_extension.rs +++ b/security-monitor/src/core/architecture/riscv/sbi/covh_extension.rs @@ -47,21 +47,27 @@ impl CovhExtension { } } -/// State of the security monitor communicated to the hypervisor. This structure is defined in CoVE specification. -#[repr(u32)] -pub enum SecurityMonitorState { - NotLoaded = 0, - Loaded = 1, - Ready = 2, -} - /// Information written by the security monitor to the hypervisor memory, representing the state of the security monitor. This structure is /// defined in CoVE specification. #[repr(C)] -pub struct SecurityMonitorInfo { - pub security_monitor_state: SecurityMonitorState, - pub security_monitor_version: u32, +pub struct TsmInfo { + pub tsm_state: u32, + pub tsm_impl_id: u32, + pub tsm_version: u32, + pub tsm_capabilities: u64, pub state_pages: u64, pub max_vcpus: u64, pub vcpu_state_pages: u64, } + +impl TsmInfo { + pub const COVE_TSM_STATE_NOT_LOADED: u32 = 0; + pub const COVE_TSM_STATE_LOADED: u32 = 1; + pub const COVE_TSM_STATE_READY: u32 = 2; + pub const COVE_TSM_IMPL_ACE: u32 = 2; + pub const COVE_TSM_CAP_ATTESTATION_LOCAL_MASK: u64 = 1 << 1; + pub const COVE_TSM_CAP_ATTESTATION_REMOTE_MASK: u64 = 1 << 2; + pub const COVE_TSM_CAP_AIA_MASK: u64 = 1 << 3; + pub const COVE_TSM_CAP_MRIF_MASK: u64 = 1 << 4; + pub const COVE_TSM_CAP_MEMORY_ALLOCATION_MASK: u64 = 1 << 5; +} diff --git a/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs index 38ade5fa..d0571428 100644 --- a/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs +++ b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::architecture::riscv::sbi::{SecurityMonitorInfo, SecurityMonitorState}; +use crate::core::architecture::riscv::sbi::TsmInfo; use crate::core::architecture::GeneralPurposeRegister; use crate::core::control_data::{ConfidentialVm, HypervisorHart}; use crate::core::memory_layout::NonConfidentialMemoryAddress; @@ -37,22 +37,24 @@ impl GetSecurityMonitorInfo { } fn fill_tsm_info_state(&self) -> Result { - let info = SecurityMonitorInfo { - security_monitor_state: SecurityMonitorState::Ready, - security_monitor_version: self.get_version(), + let info = TsmInfo { + tsm_state: TsmInfo::COVE_TSM_STATE_READY, + tsm_impl_id: TsmInfo::COVE_TSM_IMPL_ACE, + tsm_version: self.get_version(), + tsm_capabilities: TsmInfo::COVE_TSM_CAP_ATTESTATION_LOCAL_MASK, state_pages: 0, max_vcpus: u64::try_from(ConfidentialVm::MAX_NUMBER_OF_HARTS_PER_VM).unwrap_or(0), vcpu_state_pages: 0, }; // Check that the input arguments define a memory region in non-confidential memory that is large enough to store the - // `SecurityMonitorInfo` structure. + // `TsmInfo` structure. let ptr = NonConfidentialMemoryAddress::new(self.tsm_info_address as *mut usize)?; NonConfidentialMemoryAddress::new((self.tsm_info_address + self.tsm_info_len) as *mut usize)?; - ensure!(self.tsm_info_len >= core::mem::size_of::(), Error::InvalidParameter())?; + ensure!(self.tsm_info_len >= core::mem::size_of::(), Error::InvalidParameter())?; // below unsafe operation is ok because pointer is a valid address in non-confidential memory, and we have enough space to write the // reponse. - unsafe { (ptr.as_ptr() as *mut SecurityMonitorInfo).write(info) }; - Ok(core::mem::size_of::()) + unsafe { (ptr.as_ptr() as *mut TsmInfo).write(info) }; + Ok(core::mem::size_of::()) } fn get_version(&self) -> u32 { From 21b1351833cc2386854ef5ac64fb87a2224ecdbe Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Thu, 5 Sep 2024 05:35:04 -0500 Subject: [PATCH 03/18] Adjusting COVH structures to the newest CoVE spec Signed-off-by: Wojciech Ozga --- .../architecture/riscv/sbi/covh_extension.rs | 28 +++++++++++-------- .../get_security_monitor_info.rs | 18 ++++++------ 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/security-monitor/src/core/architecture/riscv/sbi/covh_extension.rs b/security-monitor/src/core/architecture/riscv/sbi/covh_extension.rs index 6ea690ad..2c40fc3e 100644 --- a/security-monitor/src/core/architecture/riscv/sbi/covh_extension.rs +++ b/security-monitor/src/core/architecture/riscv/sbi/covh_extension.rs @@ -47,21 +47,27 @@ impl CovhExtension { } } -/// State of the security monitor communicated to the hypervisor. This structure is defined in CoVE specification. -#[repr(u32)] -pub enum SecurityMonitorState { - NotLoaded = 0, - Loaded = 1, - Ready = 2, -} - /// Information written by the security monitor to the hypervisor memory, representing the state of the security monitor. This structure is /// defined in CoVE specification. #[repr(C)] -pub struct SecurityMonitorInfo { - pub security_monitor_state: SecurityMonitorState, - pub security_monitor_version: u32, +pub struct TsmInfo { + pub tsm_state: u32, + pub tsm_impl_id: u32, + pub tsm_version: u32, + pub tsm_capabilities: u64, pub state_pages: u64, pub max_vcpus: u64, pub vcpu_state_pages: u64, } + +impl TsmInfo { + pub const COVE_TSM_STATE_NOT_LOADED: u32 = 0; + pub const COVE_TSM_STATE_LOADED: u32 = 1; + pub const COVE_TSM_STATE_READY: u32 = 2; + pub const COVE_TSM_IMPL_ACE: u32 = 2; + pub const COVE_TSM_CAP_ATTESTATION_LOCAL_MASK: u64 = 1 << 1; + pub const COVE_TSM_CAP_ATTESTATION_REMOTE_MASK: u64 = 1 << 2; + pub const COVE_TSM_CAP_AIA_MASK: u64 = 1 << 3; + pub const COVE_TSM_CAP_MRIF_MASK: u64 = 1 << 4; + pub const COVE_TSM_CAP_MEMORY_ALLOCATION_MASK: u64 = 1 << 5; +} diff --git a/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs index 38ade5fa..d0571428 100644 --- a/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs +++ b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::architecture::riscv::sbi::{SecurityMonitorInfo, SecurityMonitorState}; +use crate::core::architecture::riscv::sbi::TsmInfo; use crate::core::architecture::GeneralPurposeRegister; use crate::core::control_data::{ConfidentialVm, HypervisorHart}; use crate::core::memory_layout::NonConfidentialMemoryAddress; @@ -37,22 +37,24 @@ impl GetSecurityMonitorInfo { } fn fill_tsm_info_state(&self) -> Result { - let info = SecurityMonitorInfo { - security_monitor_state: SecurityMonitorState::Ready, - security_monitor_version: self.get_version(), + let info = TsmInfo { + tsm_state: TsmInfo::COVE_TSM_STATE_READY, + tsm_impl_id: TsmInfo::COVE_TSM_IMPL_ACE, + tsm_version: self.get_version(), + tsm_capabilities: TsmInfo::COVE_TSM_CAP_ATTESTATION_LOCAL_MASK, state_pages: 0, max_vcpus: u64::try_from(ConfidentialVm::MAX_NUMBER_OF_HARTS_PER_VM).unwrap_or(0), vcpu_state_pages: 0, }; // Check that the input arguments define a memory region in non-confidential memory that is large enough to store the - // `SecurityMonitorInfo` structure. + // `TsmInfo` structure. let ptr = NonConfidentialMemoryAddress::new(self.tsm_info_address as *mut usize)?; NonConfidentialMemoryAddress::new((self.tsm_info_address + self.tsm_info_len) as *mut usize)?; - ensure!(self.tsm_info_len >= core::mem::size_of::(), Error::InvalidParameter())?; + ensure!(self.tsm_info_len >= core::mem::size_of::(), Error::InvalidParameter())?; // below unsafe operation is ok because pointer is a valid address in non-confidential memory, and we have enough space to write the // reponse. - unsafe { (ptr.as_ptr() as *mut SecurityMonitorInfo).write(info) }; - Ok(core::mem::size_of::()) + unsafe { (ptr.as_ptr() as *mut TsmInfo).write(info) }; + Ok(core::mem::size_of::()) } fn get_version(&self) -> u32 { From c1cacfacef94df67a62e7abbfff533b21457223a Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Thu, 5 Sep 2024 08:24:19 -0500 Subject: [PATCH 04/18] Added missing capability to allow for promote to tvm calls Signed-off-by: Wojciech Ozga --- .../src/core/architecture/riscv/sbi/covh_extension.rs | 1 + .../handlers/cove_host_extension/get_security_monitor_info.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/security-monitor/src/core/architecture/riscv/sbi/covh_extension.rs b/security-monitor/src/core/architecture/riscv/sbi/covh_extension.rs index 2c40fc3e..6d4a008e 100644 --- a/security-monitor/src/core/architecture/riscv/sbi/covh_extension.rs +++ b/security-monitor/src/core/architecture/riscv/sbi/covh_extension.rs @@ -65,6 +65,7 @@ impl TsmInfo { pub const COVE_TSM_STATE_LOADED: u32 = 1; pub const COVE_TSM_STATE_READY: u32 = 2; pub const COVE_TSM_IMPL_ACE: u32 = 2; + pub const COVE_TSM_CAP_PROMOTE_TVM: u64 = 1 << 0; pub const COVE_TSM_CAP_ATTESTATION_LOCAL_MASK: u64 = 1 << 1; pub const COVE_TSM_CAP_ATTESTATION_REMOTE_MASK: u64 = 1 << 2; pub const COVE_TSM_CAP_AIA_MASK: u64 = 1 << 3; diff --git a/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs index d0571428..c8e92e5a 100644 --- a/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs +++ b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/get_security_monitor_info.rs @@ -41,7 +41,7 @@ impl GetSecurityMonitorInfo { tsm_state: TsmInfo::COVE_TSM_STATE_READY, tsm_impl_id: TsmInfo::COVE_TSM_IMPL_ACE, tsm_version: self.get_version(), - tsm_capabilities: TsmInfo::COVE_TSM_CAP_ATTESTATION_LOCAL_MASK, + tsm_capabilities: TsmInfo::COVE_TSM_CAP_PROMOTE_TVM | TsmInfo::COVE_TSM_CAP_ATTESTATION_LOCAL_MASK, state_pages: 0, max_vcpus: u64::try_from(ConfidentialVm::MAX_NUMBER_OF_HARTS_PER_VM).unwrap_or(0), vcpu_state_pages: 0, From 74515fbdb50ed26176e0f45145f83853d6a14963 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Thu, 19 Sep 2024 11:13:38 -0500 Subject: [PATCH 05/18] initial version of the tool to create TEE Attestation Payload Signed-off-by: Wojciech Ozga --- tools/attestation_payload/Cargo.toml | 18 +++ tools/attestation_payload/README.md | 3 + tools/attestation_payload/src/append.rs | 24 ++++ tools/attestation_payload/src/constants.rs | 30 +++++ tools/attestation_payload/src/error.rs | 24 ++++ tools/attestation_payload/src/generate.rs | 123 +++++++++++++++++++++ tools/attestation_payload/src/main.rs | 65 +++++++++++ 7 files changed, 287 insertions(+) create mode 100644 tools/attestation_payload/Cargo.toml create mode 100644 tools/attestation_payload/README.md create mode 100644 tools/attestation_payload/src/append.rs create mode 100644 tools/attestation_payload/src/constants.rs create mode 100644 tools/attestation_payload/src/error.rs create mode 100644 tools/attestation_payload/src/generate.rs create mode 100644 tools/attestation_payload/src/main.rs diff --git a/tools/attestation_payload/Cargo.toml b/tools/attestation_payload/Cargo.toml new file mode 100644 index 00000000..67f8cc34 --- /dev/null +++ b/tools/attestation_payload/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "ace-tap" +version = "0.1.0" +authors = ["Wojciech Ozga "] +description = "Tool to create TEE attestation payload for a confidential VM" +edition = "2021" + +[dependencies] +clap = { version="4", features = ["derive"] } # for command line argument parsing + +byteorder = "1.5" # to serialize numbers using little/big endianness +sha2 = "0.10" # to calculate integrity measurements of kernel, initramfs etc. +hmac = "0.12" # to calculate HMAC over encrypted payload +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 +aes-gcm = "0.10.3" # for symmetric encryption of payload + +thiserror = "1.0" # provides macros that help removing boilerplate code in the rust error handling \ No newline at end of file diff --git a/tools/attestation_payload/README.md b/tools/attestation_payload/README.md new file mode 100644 index 00000000..d25a57c7 --- /dev/null +++ b/tools/attestation_payload/README.md @@ -0,0 +1,3 @@ +# Usage + +Generate symmetric key \ No newline at end of file diff --git a/tools/attestation_payload/src/append.rs b/tools/attestation_payload/src/append.rs new file mode 100644 index 00000000..c7cdd33b --- /dev/null +++ b/tools/attestation_payload/src/append.rs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2024 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use crate::error::Error; + +pub fn append_tap(input_file: String, tap_file: String, output_file: Option) -> Result<(), Error> { + use std::fs::OpenOptions; + use byteorder::{LittleEndian, WriteBytesExt}; + + let output_file_name = match output_file { + Some(f) if input_file != f => { + std::fs::copy(input_file, f.clone())?; + f + } + Some(f) => f, + None => input_file, + }; + let mut file1 = 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(); + Ok(()) +} \ No newline at end of file diff --git a/tools/attestation_payload/src/constants.rs b/tools/attestation_payload/src/constants.rs new file mode 100644 index 00000000..3c6e75a3 --- /dev/null +++ b/tools/attestation_payload/src/constants.rs @@ -0,0 +1,30 @@ +// 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, +} \ No newline at end of file diff --git a/tools/attestation_payload/src/error.rs b/tools/attestation_payload/src/error.rs new file mode 100644 index 00000000..e5cb7d13 --- /dev/null +++ b/tools/attestation_payload/src/error.rs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2024 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("IO Error")] + IoError(#[from] std::io::Error), + #[error("PKCS1 Error")] + Pkcs1Error(#[from] rsa::pkcs1::Error), + #[error("RSA Error")] + RsaError(#[from] rsa::Error), + #[error("Invalid parameter")] + InvalidParameter(String), + + #[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) +} \ No newline at end of file diff --git a/tools/attestation_payload/src/generate.rs b/tools/attestation_payload/src/generate.rs new file mode 100644 index 00000000..4cfcaa7c --- /dev/null +++ b/tools/attestation_payload/src/generate.rs @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: 2024 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use crate::error::Error; +use byteorder::{LittleEndian, WriteBytesExt}; +use std::io::{Write}; +use std::fs::OpenOptions; +use sha2::{Digest}; +use rand::RngCore; +// use hex_literal::hex; + +use crate::constants::TapDigestAlgorithm; +use crate::constants::TapDigestEntryType; +use crate::constants::TapLockboxAlgorithm; + +pub fn generate_tap(kernel_file: String, kernel_commandline: String, initramfs_file: String, confidential_vm_secrets: Vec<(usize, String)>, 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"))); + } + + // 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); + + // 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); + } + + 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)?; + + // 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); + + use aes_gcm::{Aes256Gcm, KeyInit, AeadInPlace, Key, AeadCore}; + 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(); + + // write the entire TAP to the output file + let mut output = OpenOptions::new().create_new(true).write(true).append(false).open(output_file.clone()).map_err(|_| Error::CannotOpenFile(output_file))?; + output.write(&lockboxes)?; + output.write(&serialized_payload_and_hmac)?; + + 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 = std::fs::File::open(file.clone()).map_err(|_| Error::CannotOpenFile(file.clone()))?; + std::io::copy(&mut file, &mut hasher)?; + let digest = hasher.finalize(); + println!("Measured file {:?}, digest={:?}", file, digest); + Ok(digest.to_vec()) +} + +fn measure_string_integrity(value: String) -> Result, Error> { + let mut hasher = sha2::Sha512::new(); + hasher.update(value.as_bytes()); + Ok(hasher.finalize().to_vec()) +} \ No newline at end of file diff --git a/tools/attestation_payload/src/main.rs b/tools/attestation_payload/src/main.rs new file mode 100644 index 00000000..219a8067 --- /dev/null +++ b/tools/attestation_payload/src/main.rs @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: 2024 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use clap::{Parser, Subcommand}; +use crate::error::Error; + +mod error; +mod constants; +mod generate; +mod append; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Args { + #[command(subcommand)] + cmd: Commands +} + +#[derive(Subcommand, Debug, Clone)] +enum Commands { + Append { + #[arg(short, long)] + input_file: String, + #[arg(short, long)] + tap_file: String, + #[arg(short, long)] + output_file: Option, + }, + Generate { + #[arg(short='k', long)] + kernel_file: String, + #[arg(short='b', long)] + kernel_commandline: String, + #[arg(short, long)] + initramfs_file: String, + #[arg(value_parser = parse_key_val::)] + confidential_vm_secrets: Vec<(usize, String)>, + #[clap(short, long, value_delimiter = ' ', num_args = 1..)] + tee_public_keys_files: Vec, + #[arg(short, long)] + output_file: String, + } +} + +/// Parse a single key-value pair +fn parse_key_val(s: &str) -> Result<(T, U), Box> +where + T: std::str::FromStr, + T::Err: std::error::Error + Send + Sync + 'static, + U: std::str::FromStr, + U::Err: std::error::Error + Send + Sync + 'static, +{ + let pos = s + .find('=') + .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?; + Ok((s[..pos].parse()?, s[pos + 1..].parse()?)) +} + +fn main() -> Result<(), Error> { + Ok(match Args::parse().cmd { + Commands::Append {input_file, tap_file, output_file} => append::append_tap(input_file, tap_file, output_file), + Commands::Generate {kernel_file, kernel_commandline, initramfs_file, confidential_vm_secrets, tee_public_keys_files, output_file} => generate::generate_tap(kernel_file, kernel_commandline, initramfs_file, confidential_vm_secrets, tee_public_keys_files, output_file), + }?) +} + From ebf72474f089e5486724fad2d8d4e05c1c01254b Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Fri, 20 Sep 2024 09:03:28 -0500 Subject: [PATCH 06/18] Added utility to create TEE attestation payload to the VM image Signed-off-by: Wojciech Ozga --- Makefile | 4 +- confidential-vms/linux_vm/Makefile | 7 +- .../run_linux_vm.sh | 0 .../Cargo.toml | 2 +- tools/local_attestation/Makefile | 54 ++++++++++++++++ .../README.md | 0 .../src/append.rs | 26 +++++--- .../src/constants.rs | 6 +- .../src/error.rs | 4 +- .../src/generate.rs | 64 +++++++++++++------ .../src/main.rs | 41 ++++++++---- 11 files changed, 159 insertions(+), 49 deletions(-) rename confidential-vms/linux_vm/{rootfs => hypervisor_rootfs}/run_linux_vm.sh (100%) rename tools/{attestation_payload => local_attestation}/Cargo.toml (95%) create mode 100644 tools/local_attestation/Makefile rename tools/{attestation_payload => local_attestation}/README.md (100%) rename tools/{attestation_payload => local_attestation}/src/append.rs (52%) rename tools/{attestation_payload => local_attestation}/src/constants.rs (90%) rename tools/{attestation_payload => local_attestation}/src/error.rs (95%) rename tools/{attestation_payload => local_attestation}/src/generate.rs (72%) rename tools/{attestation_payload => local_attestation}/src/main.rs (65%) diff --git a/Makefile b/Makefile index 59324624..bb075a4b 100644 --- a/Makefile +++ b/Makefile @@ -79,7 +79,9 @@ emulator: setup devtools tools: setup mkdir -p $(TOOLS_WORK_DIR) - cp -rf $(TOOLS_SOURCE_DIR)/* $(TOOLS_WORK_DIR) + PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" ACE_DIR=$(ACE_DIR) CROSS_COMPILE=$(CROSS_COMPILE) $(MAKE) -C tools/local_attestation build + cp -rf $(TOOLS_SOURCE_DIR)/*.sh $(TOOLS_WORK_DIR)/ + cp -rf $(TOOLS_SOURCE_DIR)/ace $(TOOLS_WORK_DIR)/ verify: rm -rf $(ACE_DIR)/security_monitor/verify/ diff --git a/confidential-vms/linux_vm/Makefile b/confidential-vms/linux_vm/Makefile index c658358a..3bd4a969 100644 --- a/confidential-vms/linux_vm/Makefile +++ b/confidential-vms/linux_vm/Makefile @@ -10,7 +10,7 @@ CONFIDENTIAL_VMS_WORK_DIR ?= $(ACE_DIR)/confidential_vms/ CONFIDENTIAL_VMS_LINUX_WORK_DIR ?= $(CONFIDENTIAL_VMS_WORK_DIR)/linux_vm/ CONFIDENTIAL_VMS_LINUX_SOURCE_DIR ?= $(MAKEFILE_SOURCE_DIR) -LINUX_VM_ROOTFS_SOURCE_DIR ?= $(CONFIDENTIAL_VMS_LINUX_SOURCE_DIR)/rootfs +LINUX_VM_ROOTFS_SOURCE_DIR ?= $(CONFIDENTIAL_VMS_LINUX_SOURCE_DIR)/hypervisor_rootfs LINUX_VM_BUILDROOT_SOURCE_DIR ?= $(CONFIDENTIAL_VMS_LINUX_SOURCE_DIR)/../../hypervisor/buildroot LINUX_VM_BUILDROOT_WORK_DIR ?= $(CONFIDENTIAL_VMS_LINUX_WORK_DIR)/buildroot LINUX_VM_BUILDROOT_ROOTFS ?= $(LINUX_VM_BUILDROOT_WORK_DIR)/images/rootfs.ext2 @@ -26,6 +26,8 @@ LINUX_VM_PATCHES_DIR ?= $(CONFIDENTIAL_VMS_LINUX_SOURCE_DIR)/patches/linux/6. HYPERVISOR_OVERLAY_DIR ?= $(ACE_DIR)/hypervisor/overlay/ HYPERVISOR_OVERLAY_ROOT_DIR ?= $(HYPERVISOR_OVERLAY_DIR)/root/ HYPERVISOR_OVERLAY_LINUX_VM_DIR ?= $(HYPERVISOR_OVERLAY_ROOT_DIR)/linux_vm/ +# +LOCAL_ATTESTATION_UTILS_DIR ?= $(CONFIDENTIAL_VMS_LINUX_SOURCE_DIR)/../../tools/local_attestation RISCV_GNU_TOOLCHAIN_WORK_DIR ?= $(ACE_DIR)/riscv-gnu-toolchain/ export PATH := $(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH) @@ -67,7 +69,8 @@ overlay: setup rm -rf $(HYPERVISOR_OVERLAY_LINUX_VM_DIR) ;\ 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 -r $(LINUX_VM_BUILDROOT_ROOTFS) $(HYPERVISOR_OVERLAY_LINUX_VM_DIR) ;\ + cp $(ACE_DIR)/tools/ace-tap $(LINUX_VM_OVERLAY_WORK_DIR)/ clean: rm -rf $(ACE_DIR) diff --git a/confidential-vms/linux_vm/rootfs/run_linux_vm.sh b/confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh similarity index 100% rename from confidential-vms/linux_vm/rootfs/run_linux_vm.sh rename to confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh diff --git a/tools/attestation_payload/Cargo.toml b/tools/local_attestation/Cargo.toml similarity index 95% rename from tools/attestation_payload/Cargo.toml rename to tools/local_attestation/Cargo.toml index 67f8cc34..d0369d8d 100644 --- a/tools/attestation_payload/Cargo.toml +++ b/tools/local_attestation/Cargo.toml @@ -15,4 +15,4 @@ 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 -thiserror = "1.0" # provides macros that help removing boilerplate code in the rust error handling \ No newline at end of file +thiserror = "1.0" # provides macros that help removing boilerplate code in the rust error handling diff --git a/tools/local_attestation/Makefile b/tools/local_attestation/Makefile new file mode 100644 index 00000000..7a1a8fa7 --- /dev/null +++ b/tools/local_attestation/Makefile @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: 2023 IBM Corporation +# SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +# SPDX-License-Identifier: Apache-2.0 +CARGO = cargo +RELEASE = --release +CHAIN=riscv64gc-unknown-linux-gnu +TARGET = --target=$(CHAIN) +RUSTFLAGS = -C linker=riscv64-unknown-linux-gnu-gcc +EXEC_NAME = ace-tap +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +MAKEFILE_SOURCE_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) + +ACE_DIR := $(if $(ACE_DIR),$(ACE_DIR),$(MAKEFILE_SOURCE_DIR)../build/target/) + +WORK_DIR=$(ACE_DIR)/local_attestation +TOOLS_DIR=$(ACE_DIR)/tools + +PLATFORM_RISCV_ABI ?= lp64d +PLATFORM_RISCV_ISA ?= rv64gc +PLATFORM_RISCV_XLEN ?= 64 +CROSS_COMPILE ?= riscv64-unknown-linux-gnu- + +all: build + +build: fmt + echo "Building Local Attestation utility" ;\ + mkdir -p $(WORK_DIR) ; \ + rm -f $(WORK_DIR)/$(CHAIN)/release/$(EXEC_NAME); \ + RUSTFLAGS='$(RUSTFLAGS)' CARGO_TARGET_DIR=$(WORK_DIR) INSTALL_DIR=$(ACE_DIR)/ $(CARGO) build $(RELEASE) $(TARGET) ; \ + cp $(WORK_DIR)/$(CHAIN)/release/$(EXEC_NAME) $(TOOLS_DIR)/ ; + +doc: + @$(CARGO) doc + +check: build test + +audit: + @$(CARGO) audit + +clippy: + @$(CARGO) clippy + +fmt: + @$(CARGO) fmt + +bench: + @$(CARGO) bench + +clean: + @$(CARGO) clean + rm -f bin/$(EXEC_NAME) + +.PHONY: all build hack opensbi doc check test bench clean diff --git a/tools/attestation_payload/README.md b/tools/local_attestation/README.md similarity index 100% rename from tools/attestation_payload/README.md rename to tools/local_attestation/README.md diff --git a/tools/attestation_payload/src/append.rs b/tools/local_attestation/src/append.rs similarity index 52% rename from tools/attestation_payload/src/append.rs rename to tools/local_attestation/src/append.rs index c7cdd33b..e09ca8ff 100644 --- a/tools/attestation_payload/src/append.rs +++ b/tools/local_attestation/src/append.rs @@ -2,11 +2,14 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use crate::error::Error; +use byteorder::{LittleEndian, WriteBytesExt}; +use std::fs::OpenOptions; -pub fn append_tap(input_file: String, tap_file: String, output_file: Option) -> Result<(), Error> { - use std::fs::OpenOptions; - use byteorder::{LittleEndian, WriteBytesExt}; - +pub fn append_tap( + input_file: String, + tap_file: String, + output_file: Option, +) -> Result<(), Error> { let output_file_name = match output_file { Some(f) if input_file != f => { std::fs::copy(input_file, f.clone())?; @@ -15,10 +18,17 @@ pub fn append_tap(input_file: String, tap_file: String, output_file: Option f, None => input_file, }; - let mut file1 = OpenOptions::new().write(true).append(true).open(output_file_name)?; + let mut file1 = 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(); + file1 + .write_u32::(crate::constants::ACE_TAP_MAGIC) + .unwrap(); + file1 + .write_u16::(tap_size_in_bytes.try_into().unwrap()) + .unwrap(); Ok(()) -} \ No newline at end of file +} diff --git a/tools/attestation_payload/src/constants.rs b/tools/local_attestation/src/constants.rs similarity index 90% rename from tools/attestation_payload/src/constants.rs rename to tools/local_attestation/src/constants.rs index 3c6e75a3..f05534f6 100644 --- a/tools/attestation_payload/src/constants.rs +++ b/tools/local_attestation/src/constants.rs @@ -8,7 +8,7 @@ pub const ACE_TAP_MAGIC: u32 = 0xACEACE00; pub enum TapDigestEntryType { Kernel, KernelCommandLine, - Initramfs + Initramfs, } #[repr(u8)] @@ -19,7 +19,7 @@ pub enum TapDigestAlgorithm { impl TapDigestAlgorithm { pub fn digest_size(&self) -> u16 { match self { - &Self::Sha512 => 512/8, + &Self::Sha512 => 512 / 8, } } } @@ -27,4 +27,4 @@ impl TapDigestAlgorithm { #[repr(u8)] pub enum TapLockboxAlgorithm { Rsa_2048_Sha256_OASP = 0, -} \ No newline at end of file +} diff --git a/tools/attestation_payload/src/error.rs b/tools/local_attestation/src/error.rs similarity index 95% rename from tools/attestation_payload/src/error.rs rename to tools/local_attestation/src/error.rs index e5cb7d13..0bdf19ef 100644 --- a/tools/attestation_payload/src/error.rs +++ b/tools/local_attestation/src/error.rs @@ -20,5 +20,5 @@ pub enum Error { InvalidSizeOfSymmetricKey(), #[error("Cannot open file {0}")] - CannotOpenFile(String) -} \ No newline at end of file + CannotOpenFile(String), +} diff --git a/tools/attestation_payload/src/generate.rs b/tools/local_attestation/src/generate.rs similarity index 72% rename from tools/attestation_payload/src/generate.rs rename to tools/local_attestation/src/generate.rs index 4cfcaa7c..e71ab384 100644 --- a/tools/attestation_payload/src/generate.rs +++ b/tools/local_attestation/src/generate.rs @@ -3,25 +3,36 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::Error; use byteorder::{LittleEndian, WriteBytesExt}; -use std::io::{Write}; -use std::fs::OpenOptions; -use sha2::{Digest}; 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; -pub fn generate_tap(kernel_file: String, kernel_commandline: String, initramfs_file: String, confidential_vm_secrets: Vec<(usize, String)>, tee_public_keys_files: Vec, output_file: String) -> Result<(), Error> { +pub fn generate_tap( + kernel_file: String, + kernel_commandline: String, + initramfs_file: String, + confidential_vm_secrets: Vec<(usize, String)>, + 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"))); + 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"))); + return Err(Error::InvalidParameter(format!( + "Confidential VM TAP support max 1024 lockboxes" + ))); } // symmetric key will be used to encrypt payload and calculate hmac @@ -34,27 +45,28 @@ pub fn generate_tap(kernel_file: String, kernel_commandline: String, initramfs_f 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())?; + 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); } - + 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())?; + // 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_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_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)?); @@ -76,15 +88,22 @@ pub fn generate_tap(kernel_file: String, kernel_commandline: String, initramfs_f serialized_payload_and_hmac.write_u16::(payload_hmac.len().try_into()?)?; serialized_payload_and_hmac.append(&mut payload_hmac); - use aes_gcm::{Aes256Gcm, KeyInit, AeadInPlace, Key, AeadCore}; + 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(); + cipher + .encrypt_in_place(&nonce, b"", &mut serialized_payload_and_hmac) + .unwrap(); // write the entire TAP to the output file - let mut output = OpenOptions::new().create_new(true).write(true).append(false).open(output_file.clone()).map_err(|_| Error::CannotOpenFile(output_file))?; + let mut output = OpenOptions::new() + .create_new(true) + .write(true) + .append(false) + .open(output_file.clone()) + .map_err(|_| Error::CannotOpenFile(output_file))?; output.write(&lockboxes)?; output.write(&serialized_payload_and_hmac)?; @@ -93,7 +112,8 @@ pub fn generate_tap(kernel_file: String, kernel_commandline: String, initramfs_f 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_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)?; @@ -101,23 +121,25 @@ fn encrypt_rsa_2048_sha256_oasp(value: &[u8], public_key_file: String) -> Result } 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())?; + 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 = std::fs::File::open(file.clone()).map_err(|_| Error::CannotOpenFile(file.clone()))?; + let mut file = + std::fs::File::open(file.clone()).map_err(|_| Error::CannotOpenFile(file.clone()))?; std::io::copy(&mut file, &mut hasher)?; let digest = hasher.finalize(); println!("Measured file {:?}, digest={:?}", file, digest); Ok(digest.to_vec()) } -fn measure_string_integrity(value: String) -> Result, Error> { +fn measure_string_integrity(value: String) -> Result, Error> { let mut hasher = sha2::Sha512::new(); hasher.update(value.as_bytes()); Ok(hasher.finalize().to_vec()) -} \ No newline at end of file +} diff --git a/tools/attestation_payload/src/main.rs b/tools/local_attestation/src/main.rs similarity index 65% rename from tools/attestation_payload/src/main.rs rename to tools/local_attestation/src/main.rs index 219a8067..26d2ff3f 100644 --- a/tools/attestation_payload/src/main.rs +++ b/tools/local_attestation/src/main.rs @@ -1,19 +1,19 @@ // SPDX-FileCopyrightText: 2024 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use clap::{Parser, Subcommand}; use crate::error::Error; +use clap::{Parser, Subcommand}; -mod error; +mod append; mod constants; +mod error; mod generate; -mod append; #[derive(Parser)] #[command(author, version, about, long_about = None)] struct Args { #[command(subcommand)] - cmd: Commands + cmd: Commands, } #[derive(Subcommand, Debug, Clone)] @@ -27,9 +27,9 @@ enum Commands { output_file: Option, }, Generate { - #[arg(short='k', long)] + #[arg(short = 'k', long)] kernel_file: String, - #[arg(short='b', long)] + #[arg(short = 'b', long)] kernel_commandline: String, #[arg(short, long)] initramfs_file: String, @@ -39,11 +39,13 @@ enum Commands { tee_public_keys_files: Vec, #[arg(short, long)] output_file: String, - } + }, } /// Parse a single key-value pair -fn parse_key_val(s: &str) -> Result<(T, U), Box> +fn parse_key_val( + s: &str, +) -> Result<(T, U), Box> where T: std::str::FromStr, T::Err: std::error::Error + Send + Sync + 'static, @@ -58,8 +60,25 @@ where fn main() -> Result<(), Error> { Ok(match Args::parse().cmd { - Commands::Append {input_file, tap_file, output_file} => append::append_tap(input_file, tap_file, output_file), - Commands::Generate {kernel_file, kernel_commandline, initramfs_file, confidential_vm_secrets, tee_public_keys_files, output_file} => generate::generate_tap(kernel_file, kernel_commandline, initramfs_file, confidential_vm_secrets, tee_public_keys_files, output_file), + Commands::Append { + input_file, + tap_file, + output_file, + } => append::append_tap(input_file, tap_file, output_file), + Commands::Generate { + kernel_file, + kernel_commandline, + initramfs_file, + confidential_vm_secrets, + tee_public_keys_files, + output_file, + } => generate::generate_tap( + kernel_file, + kernel_commandline, + initramfs_file, + confidential_vm_secrets, + tee_public_keys_files, + output_file, + ), }?) } - From f16eb0ca956666c51def7651489e44da94d5fb13 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Wed, 2 Oct 2024 10:29:02 -0500 Subject: [PATCH 07/18] 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; From 4bad096212e4dc67b5987f26392662d026238d1b Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Sun, 6 Oct 2024 02:34:08 -0500 Subject: [PATCH 08/18] Veryfing VM's measurements during promotion Signed-off-by: Wojciech Ozga --- .../rust-crates/cove_tap/src/lib.rs | 4 +- .../rust-crates/cove_tap/src/parser.rs | 7 +- .../rust-crates/cove_tap/src/spec.rs | 24 +++- .../core/control_data/confidential_hart.rs | 3 + .../src/core/control_data/confidential_vm.rs | 11 +- .../confidential_vm_measurement.rs | 6 + .../confidential_vm_physical_address.rs | 2 +- security-monitor/src/error.rs | 2 + .../promote_to_confidential_vm.rs | 112 +++++++++++------- tools/local_attestation/Cargo.toml | 2 +- tools/local_attestation/src/append.rs | 26 +++- tools/local_attestation/src/error.rs | 5 +- tools/local_attestation/src/generate.rs | 69 +++++------ tools/local_attestation/src/main.rs | 38 +++--- 14 files changed, 190 insertions(+), 121 deletions(-) diff --git a/security-monitor/rust-crates/cove_tap/src/lib.rs b/security-monitor/rust-crates/cove_tap/src/lib.rs index 5b0f39ea..ec261380 100644 --- a/security-monitor/rust-crates/cove_tap/src/lib.rs +++ b/security-monitor/rust-crates/cove_tap/src/lib.rs @@ -8,7 +8,6 @@ extern crate alloc; mod error; #[cfg(feature = "parser")] mod parser; -mod spec; #[cfg(feature = "serializer")] mod serializer; @@ -19,4 +18,5 @@ pub use parser::TeeAttestationPayloadParser; pub use serializer::TeeAttestationPayloadSerializer; pub use spec::*; -pub use error::*; \ No newline at end of file +pub use error::*; +pub mod spec; \ 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 index d3c2e690..96e61e5e 100644 --- a/security-monitor/rust-crates/cove_tap/src/parser.rs +++ b/security-monitor/rust-crates/cove_tap/src/parser.rs @@ -23,9 +23,10 @@ impl TeeAttestationPayloadParser { if self.read_u16()? != ACE_MAGIC_TAP_START { return Err(TapError::InvalidMagicStart()); } - if self.read_u16()? as usize != self.size { - return Err(TapError::InvalidSize()); - } + self.read_u16()?; + // 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 { diff --git a/security-monitor/rust-crates/cove_tap/src/spec.rs b/security-monitor/rust-crates/cove_tap/src/spec.rs index 904fb140..5aab3a9b 100644 --- a/security-monitor/rust-crates/cove_tap/src/spec.rs +++ b/security-monitor/rust-crates/cove_tap/src/spec.rs @@ -44,23 +44,35 @@ pub struct TapDigest { pub value: Vec, } +impl TapDigest { + pub fn value_in_hex(&self) -> alloc::string::String { + use crate::alloc::string::ToString; + self.value.iter().map(|b| alloc::format!("{:02x}", b).to_string()).collect::>().join("") + } +} + #[repr(u16)] #[derive(Debug)] pub enum TapDigestEntryType { - Kernel = 0, - KernelCommandLine = 1, - Initramfs = 2, + VmCodeAndData = 4, + VmBootHart = 5, } impl TapDigestEntryType { pub fn from_u16(value: u16) -> Result { match value { - 0 => Ok(Self::Kernel), - 1 => Ok(Self::KernelCommandLine), - 2 => Ok(Self::Initramfs), + 4 => Ok(Self::VmCodeAndData), + 5 => Ok(Self::VmBootHart), v => Err(TapError::UnsupportedTapDigestEntryType(v)), } } + + pub fn to_u16(&self) -> u16 { + match self { + Self::VmCodeAndData => 4, + Self::VmBootHart => 5, + } + } } #[repr(u16)] diff --git a/security-monitor/src/core/control_data/confidential_hart.rs b/security-monitor/src/core/control_data/confidential_hart.rs index d8d3e32f..db1065e8 100644 --- a/security-monitor/src/core/control_data/confidential_hart.rs +++ b/security-monitor/src/core/control_data/confidential_hart.rs @@ -130,6 +130,9 @@ impl ConfidentialHart { let mut confidential_hart = Self::from_vm_hart_reset(id, htimedelta, shared_memory); let confidential_hart_state = &mut confidential_hart.confidential_hart_state; confidential_hart_state.set_gprs(shared_memory.gprs()); + // this register is cleared as it can contain undeterministic address of TEE Attestation Payload that would destroy integrity + // measurements + confidential_hart_state.gprs_mut().write(GeneralPurposeRegister::a1, 0); confidential_hart_state.csrs_mut().vsstatus.save_nacl_value_in_main_memory(&shared_memory); confidential_hart_state.csrs_mut().vsie.save_nacl_value_in_main_memory(&shared_memory); confidential_hart_state.csrs_mut().vstvec.save_nacl_value_in_main_memory(&shared_memory); diff --git a/security-monitor/src/core/control_data/confidential_vm.rs b/security-monitor/src/core/control_data/confidential_vm.rs index 40876ecb..97be2ec8 100644 --- a/security-monitor/src/core/control_data/confidential_vm.rs +++ b/security-monitor/src/core/control_data/confidential_vm.rs @@ -11,11 +11,13 @@ use crate::error::Error; use alloc::collections::BTreeMap; use alloc::vec::Vec; use spin::{Mutex, MutexGuard}; +use tap::TapSecret; pub struct ConfidentialVm { id: ConfidentialVmId, - measurements: StaticMeasurements, confidential_harts: Vec, + measurements: StaticMeasurements, + secrets: Vec, remote_commands: BTreeMap>>, memory_protector: ConfidentialVmMemoryProtector, allowed_external_interrupts: usize, @@ -37,7 +39,7 @@ impl ConfidentialVm { /// /// The id of the confidential VM must be unique. pub fn new( - id: ConfidentialVmId, mut confidential_harts: Vec, measurements: StaticMeasurements, + id: ConfidentialVmId, mut confidential_harts: Vec, measurements: StaticMeasurements, secrets: Vec, mut memory_protector: ConfidentialVmMemoryProtector, ) -> Self { memory_protector.set_confidential_vm_id(id); @@ -50,10 +52,11 @@ impl ConfidentialVm { .collect(); Self { id, - measurements, confidential_harts, - memory_protector, + measurements, + secrets, remote_commands, + memory_protector, allowed_external_interrupts: 0, mmio_regions: Vec::with_capacity(8), } diff --git a/security-monitor/src/core/control_data/confidential_vm_measurement.rs b/security-monitor/src/core/control_data/confidential_vm_measurement.rs index 10d8e14b..71c3e853 100644 --- a/security-monitor/src/core/control_data/confidential_vm_measurement.rs +++ b/security-monitor/src/core/control_data/confidential_vm_measurement.rs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 +use crate::error::Error; use sha2::digest::crypto_common::generic_array::GenericArray; pub type DigestType = sha2::Sha384; @@ -22,6 +23,11 @@ impl StaticMeasurements { measurements.0[TVM_CONFIGURATION_REGISTER_ID] = configuration; measurements } + + pub fn compare(&self, pcr_id: usize, digest: MeasurementDigest) -> Result { + ensure!(pcr_id < NUMBER_OF_REGISTERS, Error::InvalidParameter())?; + Ok(self.0[pcr_id] == digest) + } } impl core::fmt::Debug for StaticMeasurements { diff --git a/security-monitor/src/core/memory_layout/confidential_vm_physical_address.rs b/security-monitor/src/core/memory_layout/confidential_vm_physical_address.rs index a39b00ec..71797cef 100644 --- a/security-monitor/src/core/memory_layout/confidential_vm_physical_address.rs +++ b/security-monitor/src/core/memory_layout/confidential_vm_physical_address.rs @@ -21,6 +21,6 @@ impl ConfidentialVmPhysicalAddress { impl core::fmt::Debug for ConfidentialVmPhysicalAddress { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "Confidential VM physical address={:x}", self.0) + write!(f, "0x{:x}", self.0) } } diff --git a/security-monitor/src/error.rs b/security-monitor/src/error.rs index 23ee695f..6d2ce65f 100644 --- a/security-monitor/src/error.rs +++ b/security-monitor/src/error.rs @@ -47,6 +47,8 @@ pub enum Error { AuthBlobInvalidSize(), #[error("Error when parsing TEE attestation payload: {0}")] AttestationPayloadParsingError(#[from] tap::TapError), + #[error("Local attestation failed. Invalid measurements")] + LocalAttestationFailed(), /* SBI invalid address */ #[error("Address is not aligned")] 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 e85e4f39..b999542a 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 @@ -14,6 +14,7 @@ use crate::non_confidential_flow::handlers::supervisor_binary_interface::SbiResp use crate::non_confidential_flow::{ApplyToHypervisorHart, NonConfidentialFlow}; use alloc::vec::Vec; use flattened_device_tree::FlattenedDeviceTree; +use tap::{TapSecret, TeeAttestationPayload, TeeAttestationPayloadParser}; /// Creates a confidential VM in a single-step. This handler implements the Promote to TVM call defined by the COVH ABI in the CoVE /// specification. With this call, the hypervisor presents a state of a virtual machine, requesting the security monitor to promote it to a @@ -27,7 +28,7 @@ use flattened_device_tree::FlattenedDeviceTree; /// * The address of the authentication blob must be either `0` or aligned to 8 bytes. pub struct PromoteToConfidentialVm { fdt_address: ConfidentialVmPhysicalAddress, - auth_blob_address: Option, + tee_attestation_payload_address: Option, program_counter: usize, hgatp: Hgatp, } @@ -39,13 +40,13 @@ impl PromoteToConfidentialVm { pub fn from_hypervisor_hart(hypervisor_hart: &HypervisorHart) -> Self { let fdt_address = ConfidentialVmPhysicalAddress::new(hypervisor_hart.gprs().read(GeneralPurposeRegister::a0)); - let auth_blob_address = match hypervisor_hart.gprs().read(GeneralPurposeRegister::a1) { + let tee_attestation_payload_address = match hypervisor_hart.gprs().read(GeneralPurposeRegister::a1) { 0 => None, address => Some(ConfidentialVmPhysicalAddress::new(address)), }; let program_counter = hypervisor_hart.gprs().read(GeneralPurposeRegister::a2); let hgatp = Hgatp::from(hypervisor_hart.csrs().hgatp.read()); - Self { fdt_address, auth_blob_address, program_counter, hgatp } + Self { fdt_address, tee_attestation_payload_address, program_counter, hgatp } } pub fn handle(self, non_confidential_flow: NonConfidentialFlow) -> ! { @@ -63,9 +64,8 @@ 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. + debug!("Promoting a VM to a confidential VM"); + debug!("Copying the entire VM's state to the confidential memory, recreating the MMU configuration"); let memory_protector = ConfidentialVmMemoryProtector::from_vm_state(&self.hgatp)?; // The pointer to the flattened device tree (FDT) as well as the entire FDT must be treated as an untrusted input, which measurement @@ -76,6 +76,7 @@ impl PromoteToConfidentialVm { let htimedelta = 0; // We create a fixed number of harts (all but the boot hart are in the reset state). + debug!("Copying boot hart's state"); let confidential_harts: Vec<_> = (0..number_of_confidential_harts) .map(|confidential_hart_id| match confidential_hart_id { Self::BOOT_HART_ID => ConfidentialHart::from_vm_hart(confidential_hart_id, self.program_counter, htimedelta, shared_memory), @@ -83,22 +84,25 @@ impl PromoteToConfidentialVm { }) .collect(); + let tee_attestation_payload = self.read_tee_attestation_payload(&memory_protector)?; + let measured_pages_digest = memory_protector.measure()?; let confidential_hart_digest = confidential_harts[Self::BOOT_HART_ID].measure(); let measurements = StaticMeasurements::new(measured_pages_digest, confidential_hart_digest); debug!("VM measurements: {:?}", measurements); - self.authenticate_and_authorize_vm(&memory_protector, &measurements)?; + let secrets = self.authenticate_and_authorize_vm(tee_attestation_payload, &measurements)?; ControlDataStorage::try_write(|control_data| { // We have a write lock on the entire control data! Spend here as little time as possible because we are // blocking all other harts from accessing the control data. This influences all confidential VMs in the system! let id = control_data.unique_id()?; - control_data.insert_confidential_vm(ConfidentialVm::new(id, confidential_harts, measurements, memory_protector)) + control_data.insert_confidential_vm(ConfidentialVm::new(id, confidential_harts, measurements, secrets, memory_protector)) }) } fn process_device_tree(&self, memory_protector: &ConfidentialVmMemoryProtector) -> Result { + debug!("Reading flatten device tree (FDT) at memory address 0x{:?}", self.fdt_address); 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). @@ -110,7 +114,7 @@ impl PromoteToConfidentialVm { // To work with FDT, we must have it as a continous chunk of memory. We accept only FDTs that fit within 2MiB ensure!(fdt_total_size.div_ceil(PageSize::Size2MiB.in_bytes()) == 1, Error::FdtInvalidSize())?; - let large_page = Self::relocate(memory_protector, &self.fdt_address, fdt_total_size)?; + let large_page = Self::relocate(memory_protector, &self.fdt_address, fdt_total_size, false)?; // Security note: We parse untrusted FDT using an external library. A vulnerability in this library might blow up our security // guarantees! Below unsafe is ok because FDT address is at least size of the FDT header and all FDT is in a continuous chunk of @@ -128,46 +132,62 @@ impl PromoteToConfidentialVm { Ok(number_of_confidential_harts) } + fn read_tee_attestation_payload( + &self, memory_protector: &ConfidentialVmMemoryProtector, + ) -> Result, Error> { + match self.tee_attestation_payload_address { + Some(tee_attestation_payload_address) => { + debug!("Reading TEE attestation payload (TAP) at memory address {:?}", tee_attestation_payload_address); + let address_in_confidential_memory = memory_protector.translate_address(&tee_attestation_payload_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::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 header: u64 = + unsafe { address_in_confidential_memory.read_volatile().try_into().map_err(|_| Error::AuthBlobNotAlignedTo32Bits())? }; + let total_size = ((header >> 16) & 0xFFFF) as usize + tap::ACE_HEADER_SIZE; + // 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!(total_size.div_ceil(PageSize::Size2MiB.in_bytes()) == 1, Error::AuthBlobInvalidSize())?; + let large_page = Self::relocate(memory_protector, &tee_attestation_payload_address, total_size, true)?; + + // TODO: we should parse to the blob key that will allow to unlock the lockbox. + let mut parser = unsafe { TeeAttestationPayloadParser::from_raw_pointer(large_page.address().to_ptr(), total_size)? }; + let tee_attestation_payload = parser.parse_and_verify()?; + + // Clean up, deallocate pages + PageAllocator::release_pages(alloc::vec![large_page.deallocate()]); + + Ok(Some(tee_attestation_payload)) + } + None => Ok(None), + } + } + /// Performs local attestation. It decides if the VM can be promote into a confidential VM and decrypts the attestation secret intended /// for this confidential VM. 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::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 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!(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); + &self, tee_attestation_payload: Option, measurements: &StaticMeasurements, + ) -> Result, Error> { + match tee_attestation_payload { + Some(tee_attestation_payload) => { + debug!("Authenticating and authorizing the confidential VM using read TAP"); + debug!("TAP contains {} lockboxes", tee_attestation_payload.lockboxes.len()); + debug!("TAP contains {} secrets", tee_attestation_payload.secrets.len()); + for digest in tee_attestation_payload.digests.iter() { + debug!("TAP digest: {:?} {:?} {}", digest.entry_type, digest.algorithm, digest.value_in_hex()); + // TODO: make sure we compare digests of the same algorithm... + use crate::core::control_data::MeasurementDigest; + let pcr_value = MeasurementDigest::clone_from_slice(&digest.value); + ensure!(measurements.compare(digest.entry_type.to_u16() as usize, pcr_value)?, Error::LocalAttestationFailed())?; + } + debug!("Attestation successful"); + Ok(tee_attestation_payload.secrets) } - // 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 - - // Clean up, deallocate pages - PageAllocator::release_pages(alloc::vec![large_page.deallocate()]); + None => Ok(alloc::vec![]), } - Ok(()) } /// Copies a buffer into a single large page. The input buffer is continuous across guest physical pages with G-stage address @@ -176,6 +196,7 @@ impl PromoteToConfidentialVm { /// 8-bytes. The caller is responsible for deallocating the page. fn relocate( memory_protector: &ConfidentialVmMemoryProtector, base_address: &ConfidentialVmPhysicalAddress, number_of_bytes_to_copy: usize, + clear: bool, ) -> Result, Error> { ensure!((base_address.usize() as *const u8).is_aligned_to(core::mem::size_of::()), Error::AddressNotAligned())?; let mut large_page = PageAllocator::acquire_page(PageSize::Size2MiB)?.zeroize(); @@ -185,6 +206,9 @@ impl PromoteToConfidentialVm { let confidential_vm_physical_address = base_address.add(copied_bytes); let confidential_memory_address = memory_protector.translate_address(&confidential_vm_physical_address)?; let value: usize = unsafe { confidential_memory_address.read_volatile() }; + if clear { + unsafe { confidential_memory_address.write_volatile(0) }; + } large_page.write(copied_bytes, value)?; copied_bytes += core::mem::size_of::(); } diff --git a/tools/local_attestation/Cargo.toml b/tools/local_attestation/Cargo.toml index f69d959c..a65d5204 100644 --- a/tools/local_attestation/Cargo.toml +++ b/tools/local_attestation/Cargo.toml @@ -15,6 +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"]} +tap = {path="../../security-monitor/rust-crates/cove_tap", features=["parser", "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 8b0de2ae..aa81193f 100644 --- a/tools/local_attestation/src/append.rs +++ b/tools/local_attestation/src/append.rs @@ -4,6 +4,7 @@ use crate::error::Error; use byteorder::WriteBytesExt; use std::fs::OpenOptions; +use std::io::Read; use std::io::Seek; use std::io::SeekFrom; @@ -20,25 +21,42 @@ pub fn append_tap( Some(f) => f, None => input_file, }; - let output_file_size = file_size(&output_file_name)?; + let mut output_file_size = file_size(&output_file_name)?; let mut output_file = OpenOptions::new() + .read(true) .write(true) .append(true) .open(output_file_name)?; + // check first if there is a TAP already appended + output_file.seek(SeekFrom::End(-4))?; + let mut footer = [0u8; 4]; + output_file.read_exact(&mut footer)?; + if u16::from_le_bytes([footer[0], footer[1]]) == tap::spec::ACE_MAGIC_TAP_END { + println!("Removing old TAP"); + let old_tap_size = u16::from_le_bytes([footer[2], footer[3]]) as u64; + output_file.set_len(output_file_size - old_tap_size)?; + output_file.seek(SeekFrom::End(0))?; + output_file_size -= old_tap_size; + } + // 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)?; - } + println!( + "Padding with {} bytes to align to u64", + padding_size_in_bytes + ); + (0..padding_size_in_bytes).try_for_each(|_| 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)?; + + println!("TAP appended successfully"); Ok(()) } diff --git a/tools/local_attestation/src/error.rs b/tools/local_attestation/src/error.rs index b9f860ed..781c7781 100644 --- a/tools/local_attestation/src/error.rs +++ b/tools/local_attestation/src/error.rs @@ -2,9 +2,6 @@ // 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")] @@ -21,6 +18,8 @@ pub enum Error { TryFromIntErr(#[from] std::num::TryFromIntError), #[error("Cannot open file {0}")] CannotOpenFile(String), + #[error("Could not parse int")] + IntParseError(#[from] core::num::ParseIntError), } impl From for Error { diff --git a/tools/local_attestation/src/generate.rs b/tools/local_attestation/src/generate.rs index 267201df..dce1cd64 100644 --- a/tools/local_attestation/src/generate.rs +++ b/tools/local_attestation/src/generate.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 use crate::ensure; use crate::error::Error; -use sha2::Digest; use std::fs::OpenOptions; use std::io::Write; @@ -17,10 +16,8 @@ use tap::TeeAttestationPayload; use tap::TeeAttestationPayloadSerializer; pub fn generate_tap( - kernel_file: String, - kernel_commandline: String, - initramfs_file: String, - confidential_vm_secrets: Vec<(usize, String)>, + pcrs: Vec<(u16, Vec)>, + confidential_vm_secrets: Vec<(usize, Vec)>, tee_public_keys_files: Vec, output_file: String, ) -> Result<(), Error> { @@ -43,21 +40,15 @@ pub fn generate_tap( }); 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)?, - }); + for (pcr_number, pcr_value) in pcrs.into_iter() { + let tap_digest = TapDigest { + entry_type: TapDigestEntryType::from_u16(pcr_number)?, + algorithm: TapDigestAlgorithm::Sha512, + value: pcr_value, + }; + println!("Writing PCR{}={}", pcr_number, tap_digest.value_in_hex()); + digests.push(tap_digest); + } let mut secrets = vec![]; secrets.push(TapSecret { @@ -77,27 +68,31 @@ pub fn generate_tap( // write the entire TAP to the output file let mut output = OpenOptions::new() .create_new(true) + .read(true) .write(true) .append(false) .open(output_file.clone()) - .map_err(|_| Error::CannotOpenFile(output_file))?; + .map_err(|_| Error::CannotOpenFile(output_file.clone()))?; output.write(&serialized)?; - Ok(()) -} - -fn measure_file_integrity(file: String) -> Result, Error> { - let mut hasher = sha2::Sha512::new(); - let mut file = - std::fs::File::open(file.clone()).map_err(|_| Error::CannotOpenFile(file.clone()))?; - std::io::copy(&mut file, &mut hasher)?; - let digest = hasher.finalize(); - println!("Measured file {:?}, digest={:?}", file, digest); - Ok(digest.to_vec()) -} + // test if everything went well + use std::io::Read; + use std::io::{Seek, SeekFrom}; + output.seek(SeekFrom::End(-1 * (serialized.len() as i64)))?; + let mut test_data: Vec = vec![0u8; serialized.len()]; + output.read_exact(&mut test_data)?; + let mut parser = tap::TeeAttestationPayloadParser::from_raw_pointer( + test_data.as_mut_ptr() as *const u8, + test_data.len() - 4, + )?; + let tap = parser.parse_and_verify()?; + for digest in tap.digests.iter() { + println!( + "Read PCR{}={}", + digest.entry_type.to_u16(), + digest.value_in_hex() + ); + } -fn measure_string_integrity(value: String) -> Result, Error> { - let mut hasher = sha2::Sha512::new(); - hasher.update(value.as_bytes()); - Ok(hasher.finalize().to_vec()) + Ok(()) } diff --git a/tools/local_attestation/src/main.rs b/tools/local_attestation/src/main.rs index 8f1b756f..c02382a9 100644 --- a/tools/local_attestation/src/main.rs +++ b/tools/local_attestation/src/main.rs @@ -26,14 +26,10 @@ enum Commands { output_file: Option, }, Generate { - #[arg(short = 'k', long)] - kernel_file: String, - #[arg(short = 'b', long)] - kernel_commandline: String, - #[arg(short, long)] - initramfs_file: String, - #[arg(value_parser = parse_key_val::)] - confidential_vm_secrets: Vec<(usize, String)>, + #[arg(short='p', long="pcrs", value_parser = parse_key_val::, value_delimiter = ',', required=true)] + pcrs: Vec<(u16, Vec)>, + #[arg(long="secrets", value_parser = parse_key_val::, value_delimiter = ',')] + confidential_vm_secrets: Vec<(usize, Vec)>, #[clap(short, long, value_delimiter = ' ', num_args = 1..)] tee_public_keys_files: Vec, #[arg(short, long)] @@ -44,7 +40,7 @@ enum Commands { /// Parse a single key-value pair fn parse_key_val( s: &str, -) -> Result<(T, U), Box> +) -> Result<(T, Vec), Box> where T: std::str::FromStr, T::Err: std::error::Error + Send + Sync + 'static, @@ -54,7 +50,21 @@ where let pos = s .find('=') .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?; - Ok((s[..pos].parse()?, s[pos + 1..].parse()?)) + let key = s[..pos].parse()?; + let value = &s[pos + 1..]; + let value = if value.starts_with("0x") { + decode_hex(&value[2..])? + } else { + value.as_bytes().to_vec() + }; + Ok((key, value)) +} + +pub fn decode_hex(s: &str) -> Result, core::num::ParseIntError> { + (0..s.len()) + .step_by(2) + .map(|i| u8::from_str_radix(&s[i..i + 2], 16)) + .collect() } fn main() -> Result<(), Error> { @@ -65,16 +75,12 @@ fn main() -> Result<(), Error> { output_file, } => append::append_tap(input_file, tap_file, output_file), Commands::Generate { - kernel_file, - kernel_commandline, - initramfs_file, + pcrs, confidential_vm_secrets, tee_public_keys_files, output_file, } => generate::generate_tap( - kernel_file, - kernel_commandline, - initramfs_file, + pcrs, confidential_vm_secrets, tee_public_keys_files, output_file, From c0074e2dde3d2ab9ae255f8f7c73e223cc52903f Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Sun, 6 Oct 2024 03:46:01 -0500 Subject: [PATCH 09/18] Added SBI call to retrieve local attestation secret Signed-off-by: Wojciech Ozga --- .../confidential_flow/finite_state_machine.rs | 2 + .../handlers/attestation/mod.rs | 6 +++ .../handlers/attestation/retrieve_secret.rs | 48 +++++++++++++++++++ .../src/confidential_flow/handlers/mod.rs | 1 + .../architecture/riscv/sbi/covg_extension.rs | 3 ++ .../src/core/control_data/confidential_vm.rs | 14 ++++-- .../confidential_memory_address.rs | 2 +- .../confidential_vm_physical_address.rs | 4 ++ .../promote_to_confidential_vm.rs | 8 ++-- 9 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 security-monitor/src/confidential_flow/handlers/attestation/mod.rs create mode 100644 security-monitor/src/confidential_flow/handlers/attestation/retrieve_secret.rs diff --git a/security-monitor/src/confidential_flow/finite_state_machine.rs b/security-monitor/src/confidential_flow/finite_state_machine.rs index 424243c3..e14f776d 100644 --- a/security-monitor/src/confidential_flow/finite_state_machine.rs +++ b/security-monitor/src/confidential_flow/finite_state_machine.rs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 +use crate::confidential_flow::handlers::attestation::RetrieveSecretRequest; use crate::confidential_flow::handlers::interrupts::{AllowExternalInterrupt, ExposeEnabledInterrupts, HandleInterrupt}; use crate::confidential_flow::handlers::mmio::{ AddMmioRegion, MmioLoadRequest, MmioLoadResponse, MmioStoreRequest, MmioStoreResponse, RemoveMmioRegion, @@ -92,6 +93,7 @@ impl<'a> ConfidentialFlow<'a> { VsEcall(Covg(AllowExternalInterrupt)) => AllowExternalInterrupt::from_confidential_hart(flow.confidential_hart()).handle(flow), VsEcall(Covg(ShareMemory)) => SharePageRequest::from_confidential_hart(flow.confidential_hart()).handle(flow), VsEcall(Covg(UnshareMemory)) => UnsharePageRequest::from_confidential_hart(flow.confidential_hart()).handle(flow), + VsEcall(Covg(RetrieveSecret)) => RetrieveSecretRequest::from_confidential_hart(flow.confidential_hart()).handle(flow), VsEcall(_) => InvalidCall::from_confidential_hart(flow.confidential_hart()).handle(flow), GuestLoadPageFault => MmioLoadRequest::from_confidential_hart(flow.confidential_hart()).handle(flow), VirtualInstruction => VirtualInstruction::from_confidential_hart(flow.confidential_hart()).handle(flow), diff --git a/security-monitor/src/confidential_flow/handlers/attestation/mod.rs b/security-monitor/src/confidential_flow/handlers/attestation/mod.rs new file mode 100644 index 00000000..6e79e83a --- /dev/null +++ b/security-monitor/src/confidential_flow/handlers/attestation/mod.rs @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +pub use retrieve_secret::RetrieveSecretRequest; + +mod retrieve_secret; diff --git a/security-monitor/src/confidential_flow/handlers/attestation/retrieve_secret.rs b/security-monitor/src/confidential_flow/handlers/attestation/retrieve_secret.rs new file mode 100644 index 00000000..4c9009e1 --- /dev/null +++ b/security-monitor/src/confidential_flow/handlers/attestation/retrieve_secret.rs @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2024 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use crate::confidential_flow::handlers::sbi::SbiResponse; +use crate::confidential_flow::{ApplyToConfidentialHart, ConfidentialFlow}; +use crate::core::architecture::{GeneralPurposeRegister, PageSize}; +use crate::core::control_data::{ConfidentialHart, ControlDataStorage}; +use crate::core::memory_layout::ConfidentialVmPhysicalAddress; +use crate::error::Error; + +/// Provides the confidential VM with a secret that was decoded from the attestation payload during the promotion of the VM to confidential +/// VM. Secret is written to the buffer allocated by the confidential VM and passed as arguments to the call. +pub struct RetrieveSecretRequest { + output_buffer_address: ConfidentialVmPhysicalAddress, + output_buffer_size: usize, +} + +impl RetrieveSecretRequest { + pub fn from_confidential_hart(confidential_hart: &ConfidentialHart) -> Self { + Self { + output_buffer_address: ConfidentialVmPhysicalAddress::new(confidential_hart.gprs().read(GeneralPurposeRegister::a0)), + output_buffer_size: confidential_hart.gprs().read(GeneralPurposeRegister::a1), + } + } + + pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! { + let transformation = ControlDataStorage::try_confidential_vm(confidential_flow.confidential_vm_id(), |ref mut confidential_vm| { + ensure!(self.output_buffer_address.is_aligned_to(PageSize::Size4KiB.in_bytes()), Error::AddressNotAligned())?; + ensure!(self.output_buffer_size <= PageSize::Size4KiB.in_bytes(), Error::AddressNotAligned())?; + let secret = confidential_vm.secret(0); + ensure!(secret.len() <= self.output_buffer_size, Error::AddressNotAligned())?; + + let number_of_usize_elements = secret.len().div_ceil(8); + for offset in 0..number_of_usize_elements { + let end_boundary = core::cmp::min(secret.len(), offset + 8); + let mut bytes: [u8; 8] = secret[offset..end_boundary].to_vec().try_into().unwrap(); + (end_boundary..8).for_each(|i| bytes[i] = 0u8); + let value = usize::from_le_bytes(bytes); + let confidential_memory_address = + confidential_vm.memory_protector().translate_address(&self.output_buffer_address.add(offset))?; + unsafe { confidential_memory_address.write_volatile(value) }; + } + Ok(SbiResponse::success()) + }) + .unwrap_or_else(|error| SbiResponse::error(error)); + confidential_flow.apply_and_exit_to_confidential_hart(ApplyToConfidentialHart::SbiResponse(transformation)) + } +} diff --git a/security-monitor/src/confidential_flow/handlers/mod.rs b/security-monitor/src/confidential_flow/handlers/mod.rs index a86368e4..78541f54 100644 --- a/security-monitor/src/confidential_flow/handlers/mod.rs +++ b/security-monitor/src/confidential_flow/handlers/mod.rs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 +pub mod attestation; pub mod interrupts; pub mod mmio; pub mod sbi; diff --git a/security-monitor/src/core/architecture/riscv/sbi/covg_extension.rs b/security-monitor/src/core/architecture/riscv/sbi/covg_extension.rs index b2590bcb..921aa98b 100644 --- a/security-monitor/src/core/architecture/riscv/sbi/covg_extension.rs +++ b/security-monitor/src/core/architecture/riscv/sbi/covg_extension.rs @@ -10,6 +10,7 @@ pub enum CovgExtension { UnshareMemory, AllowExternalInterrupt, DenyExternalInterrupt, + RetrieveSecret, Unknown(usize, usize), } @@ -21,6 +22,7 @@ impl CovgExtension { pub const SBI_EXT_COVG_UNSHARE_MEMORY: usize = 3; pub const SBI_EXT_COVG_ALLOW_EXT_INTERRUPT: usize = 4; pub const SBI_EXT_COVG_DENY_EXT_INTERRUPT: usize = 5; + pub const SBI_EXT_COVG_RETRIEVE_SECRET: usize = 9; pub fn from_function_id(function_id: usize) -> Self { match function_id { @@ -30,6 +32,7 @@ impl CovgExtension { Self::SBI_EXT_COVG_UNSHARE_MEMORY => Self::UnshareMemory, Self::SBI_EXT_COVG_ALLOW_EXT_INTERRUPT => Self::AllowExternalInterrupt, Self::SBI_EXT_COVG_DENY_EXT_INTERRUPT => Self::DenyExternalInterrupt, + Self::SBI_EXT_COVG_RETRIEVE_SECRET => Self::RetrieveSecret, _ => Self::Unknown(Self::EXTID, function_id), } } diff --git a/security-monitor/src/core/control_data/confidential_vm.rs b/security-monitor/src/core/control_data/confidential_vm.rs index 97be2ec8..e2abdd6a 100644 --- a/security-monitor/src/core/control_data/confidential_vm.rs +++ b/security-monitor/src/core/control_data/confidential_vm.rs @@ -16,7 +16,7 @@ use tap::TapSecret; pub struct ConfidentialVm { id: ConfidentialVmId, confidential_harts: Vec, - measurements: StaticMeasurements, + _measurements: StaticMeasurements, secrets: Vec, remote_commands: BTreeMap>>, memory_protector: ConfidentialVmMemoryProtector, @@ -39,7 +39,7 @@ impl ConfidentialVm { /// /// The id of the confidential VM must be unique. pub fn new( - id: ConfidentialVmId, mut confidential_harts: Vec, measurements: StaticMeasurements, secrets: Vec, + id: ConfidentialVmId, mut confidential_harts: Vec, _measurements: StaticMeasurements, secrets: Vec, mut memory_protector: ConfidentialVmMemoryProtector, ) -> Self { memory_protector.set_confidential_vm_id(id); @@ -53,7 +53,7 @@ impl ConfidentialVm { Self { id, confidential_harts, - measurements, + _measurements, secrets, remote_commands, memory_protector, @@ -66,10 +66,18 @@ impl ConfidentialVm { self.id } + pub fn memory_protector(&self) -> &ConfidentialVmMemoryProtector { + &self.memory_protector + } + pub fn memory_protector_mut(&mut self) -> &mut ConfidentialVmMemoryProtector { &mut self.memory_protector } + pub fn secret(&self, secret_id: usize) -> Vec { + self.secrets.iter().find(|&s| s.name == secret_id as u64).and_then(|s| Some(s.value.to_vec())).unwrap_or(alloc::vec![]) + } + pub(super) fn deallocate(self) { self.memory_protector.into_root_page_table().deallocate(); } diff --git a/security-monitor/src/core/memory_layout/confidential_memory_address.rs b/security-monitor/src/core/memory_layout/confidential_memory_address.rs index 217f1ed8..bb8072f6 100644 --- a/security-monitor/src/core/memory_layout/confidential_memory_address.rs +++ b/security-monitor/src/core/memory_layout/confidential_memory_address.rs @@ -85,7 +85,7 @@ impl ConfidentialMemoryAddress { /// # Safety /// /// The caller must ensure that the address at given offset is still within the confidential memory region. - // TODO: can we require the offset to be a multiple of usize? + // TODO: can we require the offset to be a multiple of usize? (woz: yes we can) #[rr::only_spec] #[rr::params("l", "off", "lmax", "MEMORY_CONFIG")] #[rr::args("#l", "off", "lmax")] diff --git a/security-monitor/src/core/memory_layout/confidential_vm_physical_address.rs b/security-monitor/src/core/memory_layout/confidential_vm_physical_address.rs index 71797cef..9f951d9e 100644 --- a/security-monitor/src/core/memory_layout/confidential_vm_physical_address.rs +++ b/security-monitor/src/core/memory_layout/confidential_vm_physical_address.rs @@ -17,6 +17,10 @@ impl ConfidentialVmPhysicalAddress { pub fn usize(&self) -> usize { self.0 } + + pub fn is_aligned_to(&self, align: usize) -> bool { + self.0 % align == 0 + } } impl core::fmt::Debug for ConfidentialVmPhysicalAddress { 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 b999542a..219d6e41 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 @@ -171,19 +171,17 @@ impl PromoteToConfidentialVm { fn authenticate_and_authorize_vm( &self, tee_attestation_payload: Option, measurements: &StaticMeasurements, ) -> Result, Error> { + use crate::core::control_data::MeasurementDigest; match tee_attestation_payload { Some(tee_attestation_payload) => { debug!("Authenticating and authorizing the confidential VM using read TAP"); - debug!("TAP contains {} lockboxes", tee_attestation_payload.lockboxes.len()); - debug!("TAP contains {} secrets", tee_attestation_payload.secrets.len()); for digest in tee_attestation_payload.digests.iter() { - debug!("TAP digest: {:?} {:?} {}", digest.entry_type, digest.algorithm, digest.value_in_hex()); + debug!("Reference measurement: {:?} {:?} {}", digest.entry_type, digest.algorithm, digest.value_in_hex()); // TODO: make sure we compare digests of the same algorithm... - use crate::core::control_data::MeasurementDigest; let pcr_value = MeasurementDigest::clone_from_slice(&digest.value); ensure!(measurements.compare(digest.entry_type.to_u16() as usize, pcr_value)?, Error::LocalAttestationFailed())?; } - debug!("Attestation successful"); + debug!("Attestation successful, read {} secrets", tee_attestation_payload.secrets.len()); Ok(tee_attestation_payload.secrets) } None => Ok(alloc::vec![]), From e5d61f8a28bc74e88e43702a46d31e62ac18d643 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Sun, 6 Oct 2024 03:57:25 -0500 Subject: [PATCH 10/18] cleaning up Signed-off-by: Wojciech Ozga --- .../rust-crates/cove_tap/src/parser.rs | 4 +-- .../rust-crates/cove_tap/src/serializer.rs | 2 +- .../rust-crates/cove_tap/src/spec.rs | 26 +++---------------- .../promote_to_confidential_vm.rs | 9 +++---- tools/local_attestation/src/generate.rs | 14 +++------- 5 files changed, 14 insertions(+), 41 deletions(-) diff --git a/security-monitor/rust-crates/cove_tap/src/parser.rs b/security-monitor/rust-crates/cove_tap/src/parser.rs index 96e61e5e..31bf918f 100644 --- a/security-monitor/rust-crates/cove_tap/src/parser.rs +++ b/security-monitor/rust-crates/cove_tap/src/parser.rs @@ -53,11 +53,11 @@ impl TeeAttestationPayloadParser { 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 pcr_id = self.read_u16()?; let algorithm = TapDigestAlgorithm::from_u16(self.read_u16()?)?; let value = self.read_exact(size-4)?; digests.push(TapDigest { - entry_type, + pcr_id, algorithm, value }); diff --git a/security-monitor/rust-crates/cove_tap/src/serializer.rs b/security-monitor/rust-crates/cove_tap/src/serializer.rs index f662cd12..87121bb5 100644 --- a/security-monitor/rust-crates/cove_tap/src/serializer.rs +++ b/security-monitor/rust-crates/cove_tap/src/serializer.rs @@ -55,7 +55,7 @@ impl TeeAttestationPayloadSerializer { 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.pcr_id).to_le_bytes().to_vec()); result.append(&mut (digest.algorithm as u16).to_le_bytes().to_vec()); result.append(&mut digest.value); } diff --git a/security-monitor/rust-crates/cove_tap/src/spec.rs b/security-monitor/rust-crates/cove_tap/src/spec.rs index 5aab3a9b..7757c4b2 100644 --- a/security-monitor/rust-crates/cove_tap/src/spec.rs +++ b/security-monitor/rust-crates/cove_tap/src/spec.rs @@ -39,7 +39,7 @@ impl TapLockboxAlgorithm { } pub struct TapDigest { - pub entry_type: TapDigestEntryType, + pub pcr_id: u16, pub algorithm: TapDigestAlgorithm, pub value: Vec, } @@ -49,29 +49,9 @@ impl TapDigest { use crate::alloc::string::ToString; self.value.iter().map(|b| alloc::format!("{:02x}", b).to_string()).collect::>().join("") } -} - -#[repr(u16)] -#[derive(Debug)] -pub enum TapDigestEntryType { - VmCodeAndData = 4, - VmBootHart = 5, -} - -impl TapDigestEntryType { - pub fn from_u16(value: u16) -> Result { - match value { - 4 => Ok(Self::VmCodeAndData), - 5 => Ok(Self::VmBootHart), - v => Err(TapError::UnsupportedTapDigestEntryType(v)), - } - } - pub fn to_u16(&self) -> u16 { - match self { - Self::VmCodeAndData => 4, - Self::VmBootHart => 5, - } + pub fn pcr_id(&self) -> u16 { + self.pcr_id } } 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 219d6e41..aefffe5c 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 @@ -65,7 +65,7 @@ impl PromoteToConfidentialVm { fn create_confidential_vm(&self, shared_memory: &NaclSharedMemory) -> Result { debug!("Promoting a VM to a confidential VM"); - debug!("Copying the entire VM's state to the confidential memory, recreating the MMU configuration"); + // Copying the entire VM's state to the confidential memory, recreating the MMU configuration let memory_protector = ConfidentialVmMemoryProtector::from_vm_state(&self.hgatp)?; // The pointer to the flattened device tree (FDT) as well as the entire FDT must be treated as an untrusted input, which measurement @@ -76,7 +76,6 @@ impl PromoteToConfidentialVm { let htimedelta = 0; // We create a fixed number of harts (all but the boot hart are in the reset state). - debug!("Copying boot hart's state"); let confidential_harts: Vec<_> = (0..number_of_confidential_harts) .map(|confidential_hart_id| match confidential_hart_id { Self::BOOT_HART_ID => ConfidentialHart::from_vm_hart(confidential_hart_id, self.program_counter, htimedelta, shared_memory), @@ -174,12 +173,12 @@ impl PromoteToConfidentialVm { use crate::core::control_data::MeasurementDigest; match tee_attestation_payload { Some(tee_attestation_payload) => { - debug!("Authenticating and authorizing the confidential VM using read TAP"); + debug!("Authenticating and authorizing the confidential VM"); for digest in tee_attestation_payload.digests.iter() { - debug!("Reference measurement: {:?} {:?} {}", digest.entry_type, digest.algorithm, digest.value_in_hex()); + debug!("Reference measurement: {:?}={:?}=0x{}", digest.pcr_id, digest.algorithm, digest.value_in_hex()); // TODO: make sure we compare digests of the same algorithm... let pcr_value = MeasurementDigest::clone_from_slice(&digest.value); - ensure!(measurements.compare(digest.entry_type.to_u16() as usize, pcr_value)?, Error::LocalAttestationFailed())?; + ensure!(measurements.compare(digest.pcr_id() as usize, pcr_value)?, Error::LocalAttestationFailed())?; } debug!("Attestation successful, read {} secrets", tee_attestation_payload.secrets.len()); Ok(tee_attestation_payload.secrets) diff --git a/tools/local_attestation/src/generate.rs b/tools/local_attestation/src/generate.rs index dce1cd64..83138c83 100644 --- a/tools/local_attestation/src/generate.rs +++ b/tools/local_attestation/src/generate.rs @@ -5,11 +5,9 @@ use crate::ensure; use crate::error::Error; use std::fs::OpenOptions; use std::io::Write; - use tap::Lockbox; use tap::TapDigest; use tap::TapDigestAlgorithm; -use tap::TapDigestEntryType; use tap::TapLockboxAlgorithm; use tap::TapSecret; use tap::TeeAttestationPayload; @@ -40,13 +38,13 @@ pub fn generate_tap( }); let mut digests = vec![]; - for (pcr_number, pcr_value) in pcrs.into_iter() { + for (pcr_id, pcr_value) in pcrs.into_iter() { let tap_digest = TapDigest { - entry_type: TapDigestEntryType::from_u16(pcr_number)?, + pcr_id, algorithm: TapDigestAlgorithm::Sha512, value: pcr_value, }; - println!("Writing PCR{}={}", pcr_number, tap_digest.value_in_hex()); + println!("Writing PCR{}={}", pcr_id, tap_digest.value_in_hex()); digests.push(tap_digest); } @@ -87,11 +85,7 @@ pub fn generate_tap( )?; let tap = parser.parse_and_verify()?; for digest in tap.digests.iter() { - println!( - "Read PCR{}={}", - digest.entry_type.to_u16(), - digest.value_in_hex() - ); + println!("Read PCR{}={}", digest.pcr_id, digest.value_in_hex()); } Ok(()) From d7e1f5d82331a625ad9ddaa3f0b92333fed62702 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Mon, 7 Oct 2024 10:28:16 -0500 Subject: [PATCH 11/18] cleaning up Signed-off-by: Wojciech Ozga --- .gitignore | 2 +- Makefile | 8 +-- confidential-vms/linux_vm/Makefile | 18 +++-- .../hypervisor_rootfs/run_linux_vm.sh | 5 +- .../linux_vm/overlay/root/ace_module/Makefile | 27 ++++++++ .../linux_vm/overlay/root/ace_module/ace.c | 39 +++++++++++ .../root/this_is_confidential_vm_filesystem | 1 + .../this_is_confidential_vm_filesystem | 1 + .../rust-crates/cove_tap/src/error.rs | 8 +-- .../rust-crates/cove_tap/src/lib.rs | 4 +- .../rust-crates/cove_tap/src/parser.rs | 32 +++++---- .../rust-crates/cove_tap/src/serializer.rs | 20 +++--- .../rust-crates/cove_tap/src/spec.rs | 45 ++++++------ .../handlers/attestation/retrieve_secret.rs | 18 +++-- .../src/core/control_data/confidential_vm.rs | 15 ++-- security-monitor/src/error.rs | 2 + .../promote_to_confidential_vm.rs | 66 ++++++++---------- .../Cargo.toml | 4 +- .../Makefile | 10 +-- .../README.md | 0 tools/cove_tap_tool/src/attach.rs | 54 +++++++++++++++ .../src/error.rs | 2 + .../src/generate.rs | 53 ++++++--------- .../src/main.rs | 12 ++-- tools/local_attestation/src/append.rs | 68 ------------------- 25 files changed, 283 insertions(+), 231 deletions(-) create mode 100644 confidential-vms/linux_vm/overlay/root/ace_module/Makefile create mode 100644 confidential-vms/linux_vm/overlay/root/ace_module/ace.c rename tools/{local_attestation => cove_tap_tool}/Cargo.toml (88%) rename tools/{local_attestation => cove_tap_tool}/Makefile (90%) rename tools/{local_attestation => cove_tap_tool}/README.md (100%) create mode 100644 tools/cove_tap_tool/src/attach.rs rename tools/{local_attestation => cove_tap_tool}/src/error.rs (94%) rename tools/{local_attestation => cove_tap_tool}/src/generate.rs (58%) rename tools/{local_attestation => cove_tap_tool}/src/main.rs (89%) delete mode 100644 tools/local_attestation/src/append.rs diff --git a/.gitignore b/.gitignore index 3c48340c..86d8269b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ build/* target/* -tools/local_attestation/target +tools/cove_tap_tool/target security-monitor/target configurations/overlay/root/harness/baremetal diff --git a/Makefile b/Makefile index 56d1291f..e5ff42e1 100644 --- a/Makefile +++ b/Makefile @@ -51,11 +51,11 @@ hypervisor: setup devtools 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 ;\ + # 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/ rootfs ;\ PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" ACE_DIR=$(ACE_DIR) $(MAKE) -C hypervisor rootfs; -hypervisor_dev: +hypervisor_dev: PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" ACE_DIR=$(ACE_DIR) $(MAKE) -C hypervisor dev dev: tools @@ -79,7 +79,7 @@ emulator: setup devtools tools: setup mkdir -p $(TOOLS_WORK_DIR) - PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" ACE_DIR=$(ACE_DIR) CROSS_COMPILE=$(CROSS_COMPILE) $(MAKE) -C tools/local_attestation build + PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" ACE_DIR=$(ACE_DIR) CROSS_COMPILE=$(CROSS_COMPILE) $(MAKE) -C tools/cove_tap_tool build cp -rf $(TOOLS_SOURCE_DIR)/*.sh $(TOOLS_WORK_DIR)/ cp -rf $(TOOLS_SOURCE_DIR)/ace $(TOOLS_WORK_DIR)/ diff --git a/confidential-vms/linux_vm/Makefile b/confidential-vms/linux_vm/Makefile index 0af293b1..45a5b08b 100644 --- a/confidential-vms/linux_vm/Makefile +++ b/confidential-vms/linux_vm/Makefile @@ -23,12 +23,11 @@ LINUX_VM_KERNEL_CONFIG ?= $(CONFIDENTIAL_VMS_LINUX_SOURCE_DIR)/configurations LINUX_VM_BUILDROOT_CONFIG ?= $(CONFIDENTIAL_VMS_LINUX_SOURCE_DIR)/configurations/qemu_riscv64_virt_defconfig LINUX_VM_BUILDROOT_OVERRIDE_DIR ?= $(CONFIDENTIAL_VMS_LINUX_SOURCE_DIR)/configurations/package_override.dev LINUX_VM_PATCHES_DIR ?= $(CONFIDENTIAL_VMS_LINUX_SOURCE_DIR)/patches/linux/6.3-rc4 +LINUX_DIR ?= $(LINUX_VM_BUILDROOT_WORK_DIR)/build/linux-6.3-rc4/ # overlays HYPERVISOR_OVERLAY_DIR ?= $(ACE_DIR)/hypervisor/overlay/ HYPERVISOR_OVERLAY_ROOT_DIR ?= $(HYPERVISOR_OVERLAY_DIR)/root/ HYPERVISOR_OVERLAY_LINUX_VM_DIR ?= $(HYPERVISOR_OVERLAY_ROOT_DIR)/linux_vm/ -# -LOCAL_ATTESTATION_UTILS_DIR ?= $(CONFIDENTIAL_VMS_LINUX_SOURCE_DIR)/../../tools/local_attestation RISCV_GNU_TOOLCHAIN_WORK_DIR ?= $(ACE_DIR)/riscv-gnu-toolchain/ export PATH := $(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH) @@ -60,6 +59,7 @@ buildroot: setup dev: echo "Rebuilding buildroot"; \ + sed "s@^BR2_ROOTFS_OVERLAY=.*@BR2_ROOTFS_OVERLAY=\"$(LINUX_VM_OVERLAY_WORK_DIR)\"@g" -i $(LINUX_VM_BUILDROOT_WORK_DIR)/.config; \ sed "s@^BR2_PACKAGE_OVERRIDE_FILE=.*@BR2_PACKAGE_OVERRIDE_FILE=\"$(LINUX_VM_BUILDROOT_OVERRIDE_DIR)\"@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_TARGET_ROOTFS_EXT2_SIZE=.*@BR2_TARGET_ROOTFS_EXT2_SIZE=\"$(LINUX_VM_BUILDROOT_ROOTFS_SIZE)\"@g" -i $(LINUX_VM_BUILDROOT_WORK_DIR)/.config; \ @@ -67,18 +67,22 @@ dev: overlay: setup mkdir -p $(LINUX_VM_OVERLAY_WORK_DIR) ;\ + mkdir -p $(LINUX_VM_OVERLAY_WORK_ROOT_DIR); \ cp -r $(LINUX_VM_OVERLAY_SOURCE_DIR)/* $(LINUX_VM_OVERLAY_WORK_DIR)/ ;\ + PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" $(MAKE) -s -C $(LINUX_VM_OVERLAY_WORK_ROOT_DIR)/ace_module/ RISCV=$(RISCV_GNU_TOOLCHAIN_WORK_DIR) PATH=$(PATH) ARCH=riscv KDIR=$(LINUX_DIR) CROSS_COMPILE=$(CROSS_COMPILE) CC="riscv64-unknown-linux-gnu-gcc" O=$(LINUX_DIR) ;\ mkdir -p $(HYPERVISOR_OVERLAY_ROOT_DIR) ;\ cp $(LINUX_VM_ROOTFS_SOURCE_DIR)/*.sh $(HYPERVISOR_OVERLAY_ROOT_DIR)/ ;\ - rm -rf $(HYPERVISOR_OVERLAY_LINUX_VM_DIR) ;\ - mkdir -p $(HYPERVISOR_OVERLAY_LINUX_VM_DIR) ;\ + rm -rf $(HYPERVISOR_OVERLAY_LINUX_VM_DIR) && 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 -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)/ + +rootfs: overlay + PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" $(MAKE) -s -C $(LINUX_VM_BUILDROOT_SOURCE_DIR) RISCV=$(RISCV_GNU_TOOLCHAIN_WORK_DIR) PATH=$(PATH) ARCH=riscv64 KDIR=$(LINUX_DIR) CROSS_COMPILE=$(CROSS_COMPILE) O=$(LINUX_VM_BUILDROOT_WORK_DIR) rootfs-ext2; \ + cp -r $(LINUX_VM_BUILDROOT_WORK_DIR)/images/rootfs.cpio $(HYPERVISOR_OVERLAY_LINUX_VM_DIR); + clean: rm -rf $(ACE_DIR) -.PHONY: all buildroot linux clean overlay rootfs +.PHONY: all buildroot linux clean overlay 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 b4153072..3319a8dd 100755 --- a/confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh +++ b/confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh @@ -32,7 +32,7 @@ for i in "$@"; do -m=*|--memory=*) MEMORY="${i#*=}" shift - ;; + ;; --daemonize*) INTERACTIVE="-daemonize" shift @@ -55,11 +55,10 @@ ${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 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 \ -netdev user,id=net0,net=192.168.100.1/24,dhcpstart=192.168.100.128,hostfwd=tcp::${HOST_PORT}-:22 \ - -nographic + -nographic diff --git a/confidential-vms/linux_vm/overlay/root/ace_module/Makefile b/confidential-vms/linux_vm/overlay/root/ace_module/Makefile new file mode 100644 index 00000000..12d8028b --- /dev/null +++ b/confidential-vms/linux_vm/overlay/root/ace_module/Makefile @@ -0,0 +1,27 @@ +CONFIG_MODULE_SIG=n + +startstop-objs := start.o stop.o +obj-m += ace.o + +ifeq ($(CONFIG_STATUS_CHECK_GCC),y) + CC=$(STATUS_CHECK_GCC) + ccflags-y += -fanalyzer +endif + +KDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(CURDIR) + +default: + $(MAKE) -C $(KDIR) CC="$(CC)" M="$(PWD)" modules + rm -f .Module* .modules* .ace* *.symvers *.order ace.mod.* *.o *.mod + +load: + @insmod ace.ko + +clean: + $(MAKE) -C /lib/modules/$(shell uname -r)/build CC="$(CC)" M="$(PWD)" clean + $(RM) other/cat_noblock *.plist + +indent: + clang-format -i *[.ch] + clang-format -i other/*[.ch] \ No newline at end of file diff --git a/confidential-vms/linux_vm/overlay/root/ace_module/ace.c b/confidential-vms/linux_vm/overlay/root/ace_module/ace.c new file mode 100644 index 00000000..287ea693 --- /dev/null +++ b/confidential-vms/linux_vm/overlay/root/ace_module/ace.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include /* usleep_range */ +#include +#include /* single_open, single_release */ +#include /* kmalloc, kfree */ + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Wojciech Ozga "); + +static int ace_init(void){ + volatile u8 *secret; + struct sbiret ret; + int i; + + printk(KERN_ALERT "Requesting secret from the security monitor\n"); + secret = kmalloc(1024*sizeof(u8), GFP_KERNEL); + ret = sbi_ecall(0x434F5647, 9, virt_to_phys((void *) secret), 1024, 0, 0, 0, 0); + if (!ret.error) { + printk(KERN_ALERT "Secret=0x"); + for (i=0; i Result { Ok(Self { pointer, size }) } - pub fn parse_and_verify(&mut self) -> Result { - if self.read_u16()? != ACE_MAGIC_TAP_START { + pub fn parse_and_verify(&mut self) -> Result { + if self.read_u32()? != ACE_MAGIC_TAP_START { return Err(TapError::InvalidMagicStart()); } self.read_u16()?; @@ -32,7 +32,7 @@ impl TeeAttestationPayloadParser { 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 algorithm = LockboxAlgorithm::from_u16(self.read_u16()?)?; let value = self.read_exact(size-10)?; lockboxes.push(Lockbox { name, @@ -43,10 +43,10 @@ impl TeeAttestationPayloadParser { // TODO: recover symmetric key let symmetric_key = [0u8; 32]; - let payload_encryption_algorithm = TapPayloadEncryptionAlgorithm::from_u16(self.read_u16()?)?; + let payload_encryption_algorithm = PayloadEncryptionAlgorithm::from_u16(self.read_u16()?)?; match payload_encryption_algorithm { - TapPayloadEncryptionAlgorithm::Debug => {}, - TapPayloadEncryptionAlgorithm::AesGcm256 => self.decrypt_aes_gcm_256(symmetric_key)?, + PayloadEncryptionAlgorithm::Debug => {}, + PayloadEncryptionAlgorithm::AesGcm256 => self.decrypt_aes_gcm_256(symmetric_key)?, } let number_of_digests = self.read_u16()?; @@ -54,9 +54,9 @@ impl TeeAttestationPayloadParser { for _ in 0..number_of_digests { let size = self.read_u16()? as usize; let pcr_id = self.read_u16()?; - let algorithm = TapDigestAlgorithm::from_u16(self.read_u16()?)?; + let algorithm = DigestAlgorithm::from_u16(self.read_u16()?)?; let value = self.read_exact(size-4)?; - digests.push(TapDigest { + digests.push(Digest { pcr_id, algorithm, value @@ -69,10 +69,10 @@ impl TeeAttestationPayloadParser { 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 }); + secrets.push(Secret { name, value }); } - Ok(TeeAttestationPayload { + Ok(AttestationPayload { lockboxes, digests, secrets, @@ -91,7 +91,7 @@ impl TeeAttestationPayloadParser { let key: Key = symmetric_key.into(); let cipher = Aes256Gcm::new(&key); let nonce = Nonce::from_slice(&nonce); - let tag = Tag::from_slice(&tag); + 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(()) @@ -103,6 +103,12 @@ impl TeeAttestationPayloadParser { Ok(value) } + fn read_u32(&mut self) -> Result { + let value = unsafe { (self.pointer as *const u32).read_volatile() }; + self.pointer = self.pointer.wrapping_add(4); + 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); diff --git a/security-monitor/rust-crates/cove_tap/src/serializer.rs b/security-monitor/rust-crates/cove_tap/src/serializer.rs index 87121bb5..a5b21c35 100644 --- a/security-monitor/rust-crates/cove_tap/src/serializer.rs +++ b/security-monitor/rust-crates/cove_tap/src/serializer.rs @@ -6,35 +6,35 @@ use alloc::vec; use crate::spec::*; use alloc::vec::Vec; -pub struct TeeAttestationPayloadSerializer { +pub struct AttestationPayloadSerializer { } -impl TeeAttestationPayloadSerializer { +impl AttestationPayloadSerializer { pub fn new() -> Self { Self {} } - pub fn serialize(&self, mut payload: TeeAttestationPayload) -> Result, TapError> { + pub fn serialize(&self, mut payload: AttestationPayload) -> 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 total_size = lockboxes.len() + encrypted_part.len(); 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()); + // 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> { + fn serialize_lockboxes(&self, payload: &mut AttestationPayload) -> Result, TapError> { // TODO: sanity check: lockboxes < 1024 let mut result = vec![]; result.append(&mut (payload.lockboxes.len() as u16).to_le_bytes().to_vec()); @@ -48,7 +48,7 @@ impl TeeAttestationPayloadSerializer { Ok(result) } - fn serialize_digests(&self, payload: &mut TeeAttestationPayload) -> Result, TapError> { + fn serialize_digests(&self, payload: &mut AttestationPayload) -> Result, TapError> { // TODO: sanity check: digests < 1024 let mut result = vec![]; result.append(&mut (payload.digests.len() as u16).to_le_bytes().to_vec()); @@ -62,7 +62,7 @@ impl TeeAttestationPayloadSerializer { Ok(result) } - fn serialize_secrets(&self, payload: &mut TeeAttestationPayload) -> Result, TapError> { + fn serialize_secrets(&self, payload: &mut AttestationPayload) -> Result, TapError> { // TODO: sanity check: secrets < 1024 let mut result = vec![]; result.append(&mut (payload.secrets.len() as u16).to_le_bytes().to_vec()); @@ -96,7 +96,7 @@ impl TeeAttestationPayloadSerializer { .unwrap(); let mut result = vec![]; - result.append(&mut (TapPayloadEncryptionAlgorithm::AesGcm256 as u16).to_le_bytes().to_vec()); + result.append(&mut (PayloadEncryptionAlgorithm::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()); diff --git a/security-monitor/rust-crates/cove_tap/src/spec.rs b/security-monitor/rust-crates/cove_tap/src/spec.rs index 7757c4b2..6538be04 100644 --- a/security-monitor/rust-crates/cove_tap/src/spec.rs +++ b/security-monitor/rust-crates/cove_tap/src/spec.rs @@ -4,47 +4,48 @@ 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 const ACE_HEADER_SIZE: usize = 8; +pub const ACE_FOOTER_SIZE: usize = 8; +pub const ACE_MAGIC_TAP_START: u32 = 0xACE0ACE0; +pub const ACE_MAGIC_TAP_END: u32 = 0xACE1ACE1; +pub const ACE_MAX_TAP_SIZE: usize = 4096; // size of the 4KiB page -pub struct TeeAttestationPayload { +pub struct AttestationPayload { pub lockboxes: Vec, - pub digests: Vec, - pub secrets: Vec, + pub digests: Vec, + pub secrets: Vec, } pub struct Lockbox { pub name: u64, - pub algorithm: TapLockboxAlgorithm, + pub algorithm: LockboxAlgorithm, pub value: Vec, } #[repr(u16)] #[derive(Debug)] -pub enum TapLockboxAlgorithm { +pub enum LockboxAlgorithm { Debug = 0, Rsa2048Sha256Oasp = 1, } -impl TapLockboxAlgorithm { +impl LockboxAlgorithm { pub fn from_u16(value: u16) -> Result { match value { 0 => Ok(Self::Debug), 1 => Ok(Self::Rsa2048Sha256Oasp), - v => Err(TapError::UnsupportedTapLockboxAlgorithm(v)), + v => Err(TapError::UnsupportedLockboxAlgorithm(v)), } } } -pub struct TapDigest { +pub struct Digest { pub pcr_id: u16, - pub algorithm: TapDigestAlgorithm, + pub algorithm: DigestAlgorithm, pub value: Vec, } -impl TapDigest { +impl Digest { pub fn value_in_hex(&self) -> alloc::string::String { use crate::alloc::string::ToString; self.value.iter().map(|b| alloc::format!("{:02x}", b).to_string()).collect::>().join("") @@ -56,18 +57,18 @@ impl TapDigest { } #[repr(u16)] -#[derive(Debug)] -pub enum TapDigestAlgorithm { +#[derive(Debug, PartialEq, Eq)] +pub enum DigestAlgorithm { Debug = 0, Sha512 = 1, } -impl TapDigestAlgorithm { +impl DigestAlgorithm { pub fn from_u16(value: u16) -> Result { match value { 0 => Ok(Self::Debug), 1 => Ok(Self::Sha512), - v => Err(TapError::UnsupportedTapDigestAlgorithm(v)), + v => Err(TapError::UnsupportedDigestAlgorithm(v)), } } @@ -79,24 +80,24 @@ impl TapDigestAlgorithm { } } -pub struct TapSecret { +pub struct Secret { pub name: u64, pub value: Vec, } #[repr(u16)] #[derive(Debug)] -pub enum TapPayloadEncryptionAlgorithm { +pub enum PayloadEncryptionAlgorithm { Debug = 0, AesGcm256 = 1, } -impl TapPayloadEncryptionAlgorithm { +impl PayloadEncryptionAlgorithm { pub fn from_u16(value: u16) -> Result { match value { 0 => Ok(Self::Debug), 1 => Ok(Self::AesGcm256), - v => Err(TapError::UnsupportedTapPayloadEncryptionAlgorithm(v)), + v => Err(TapError::UnsupportedPayloadEncryptionAlgorithm(v)), } } } \ No newline at end of file diff --git a/security-monitor/src/confidential_flow/handlers/attestation/retrieve_secret.rs b/security-monitor/src/confidential_flow/handlers/attestation/retrieve_secret.rs index 4c9009e1..78470abc 100644 --- a/security-monitor/src/confidential_flow/handlers/attestation/retrieve_secret.rs +++ b/security-monitor/src/confidential_flow/handlers/attestation/retrieve_secret.rs @@ -25,22 +25,20 @@ impl RetrieveSecretRequest { pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! { let transformation = ControlDataStorage::try_confidential_vm(confidential_flow.confidential_vm_id(), |ref mut confidential_vm| { - ensure!(self.output_buffer_address.is_aligned_to(PageSize::Size4KiB.in_bytes()), Error::AddressNotAligned())?; + // ensure!(self.output_buffer_address.is_aligned_to(PageSize::Size4KiB.in_bytes()), Error::AddressNotAligned())?; ensure!(self.output_buffer_size <= PageSize::Size4KiB.in_bytes(), Error::AddressNotAligned())?; - let secret = confidential_vm.secret(0); + let secret = confidential_vm.secret(0)?; ensure!(secret.len() <= self.output_buffer_size, Error::AddressNotAligned())?; - - let number_of_usize_elements = secret.len().div_ceil(8); - for offset in 0..number_of_usize_elements { + let mut buffer = [0u8; 8]; + for offset in 0..secret.len().div_ceil(8) { let end_boundary = core::cmp::min(secret.len(), offset + 8); - let mut bytes: [u8; 8] = secret[offset..end_boundary].to_vec().try_into().unwrap(); - (end_boundary..8).for_each(|i| bytes[i] = 0u8); - let value = usize::from_le_bytes(bytes); + (0..end_boundary).for_each(|i| buffer[i] = secret[offset + i]); + (end_boundary..8).for_each(|i| buffer[i] = 0u8); let confidential_memory_address = confidential_vm.memory_protector().translate_address(&self.output_buffer_address.add(offset))?; - unsafe { confidential_memory_address.write_volatile(value) }; + unsafe { confidential_memory_address.write_volatile(usize::from_le_bytes(buffer)) }; } - Ok(SbiResponse::success()) + Ok(SbiResponse::success_with_code(secret.len())) }) .unwrap_or_else(|error| SbiResponse::error(error)); confidential_flow.apply_and_exit_to_confidential_hart(ApplyToConfidentialHart::SbiResponse(transformation)) diff --git a/security-monitor/src/core/control_data/confidential_vm.rs b/security-monitor/src/core/control_data/confidential_vm.rs index e2abdd6a..75e69524 100644 --- a/security-monitor/src/core/control_data/confidential_vm.rs +++ b/security-monitor/src/core/control_data/confidential_vm.rs @@ -11,13 +11,13 @@ use crate::error::Error; use alloc::collections::BTreeMap; use alloc::vec::Vec; use spin::{Mutex, MutexGuard}; -use tap::TapSecret; +use tap::Secret; pub struct ConfidentialVm { id: ConfidentialVmId, confidential_harts: Vec, _measurements: StaticMeasurements, - secrets: Vec, + secrets: Vec, remote_commands: BTreeMap>>, memory_protector: ConfidentialVmMemoryProtector, allowed_external_interrupts: usize, @@ -39,7 +39,7 @@ impl ConfidentialVm { /// /// The id of the confidential VM must be unique. pub fn new( - id: ConfidentialVmId, mut confidential_harts: Vec, _measurements: StaticMeasurements, secrets: Vec, + id: ConfidentialVmId, mut confidential_harts: Vec, _measurements: StaticMeasurements, secrets: Vec, mut memory_protector: ConfidentialVmMemoryProtector, ) -> Self { memory_protector.set_confidential_vm_id(id); @@ -74,8 +74,13 @@ impl ConfidentialVm { &mut self.memory_protector } - pub fn secret(&self, secret_id: usize) -> Vec { - self.secrets.iter().find(|&s| s.name == secret_id as u64).and_then(|s| Some(s.value.to_vec())).unwrap_or(alloc::vec![]) + pub fn secret(&self, secret_id: usize) -> Result, Error> { + for secret in self.secrets.iter() { + if secret.name == secret_id as u64 { + return Ok(secret.value.to_vec()); + } + } + Err(Error::InvalidParameter()) } pub(super) fn deallocate(self) { diff --git a/security-monitor/src/error.rs b/security-monitor/src/error.rs index 6d2ce65f..5e937194 100644 --- a/security-monitor/src/error.rs +++ b/security-monitor/src/error.rs @@ -49,6 +49,8 @@ pub enum Error { AttestationPayloadParsingError(#[from] tap::TapError), #[error("Local attestation failed. Invalid measurements")] LocalAttestationFailed(), + #[error("Local attestation failed. Not supported digest type")] + LocalAttestationNotSupportedDigest(), /* SBI invalid address */ #[error("Address is not aligned")] 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 aefffe5c..cc5572cc 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 @@ -14,7 +14,7 @@ use crate::non_confidential_flow::handlers::supervisor_binary_interface::SbiResp use crate::non_confidential_flow::{ApplyToHypervisorHart, NonConfidentialFlow}; use alloc::vec::Vec; use flattened_device_tree::FlattenedDeviceTree; -use tap::{TapSecret, TeeAttestationPayload, TeeAttestationPayloadParser}; +use tap::{AttestationPayload, AttestationPayloadParser, Secret}; /// Creates a confidential VM in a single-step. This handler implements the Promote to TVM call defined by the COVH ABI in the CoVE /// specification. With this call, the hypervisor presents a state of a virtual machine, requesting the security monitor to promote it to a @@ -28,7 +28,7 @@ use tap::{TapSecret, TeeAttestationPayload, TeeAttestationPayloadParser}; /// * The address of the authentication blob must be either `0` or aligned to 8 bytes. pub struct PromoteToConfidentialVm { fdt_address: ConfidentialVmPhysicalAddress, - tee_attestation_payload_address: Option, + attestation_payload_address: Option, program_counter: usize, hgatp: Hgatp, } @@ -40,13 +40,13 @@ impl PromoteToConfidentialVm { pub fn from_hypervisor_hart(hypervisor_hart: &HypervisorHart) -> Self { let fdt_address = ConfidentialVmPhysicalAddress::new(hypervisor_hart.gprs().read(GeneralPurposeRegister::a0)); - let tee_attestation_payload_address = match hypervisor_hart.gprs().read(GeneralPurposeRegister::a1) { + let attestation_payload_address = match hypervisor_hart.gprs().read(GeneralPurposeRegister::a1) { 0 => None, address => Some(ConfidentialVmPhysicalAddress::new(address)), }; let program_counter = hypervisor_hart.gprs().read(GeneralPurposeRegister::a2); let hgatp = Hgatp::from(hypervisor_hart.csrs().hgatp.read()); - Self { fdt_address, tee_attestation_payload_address, program_counter, hgatp } + Self { fdt_address, attestation_payload_address, program_counter, hgatp } } pub fn handle(self, non_confidential_flow: NonConfidentialFlow) -> ! { @@ -83,14 +83,11 @@ impl PromoteToConfidentialVm { }) .collect(); - let tee_attestation_payload = self.read_tee_attestation_payload(&memory_protector)?; - - let measured_pages_digest = memory_protector.measure()?; - let confidential_hart_digest = confidential_harts[Self::BOOT_HART_ID].measure(); - let measurements = StaticMeasurements::new(measured_pages_digest, confidential_hart_digest); + let attestation_payload = self.read_attestation_payload(&memory_protector).unwrap_or(None); + let measurements = StaticMeasurements::new(memory_protector.measure()?, confidential_harts[Self::BOOT_HART_ID].measure()); debug!("VM measurements: {:?}", measurements); - let secrets = self.authenticate_and_authorize_vm(tee_attestation_payload, &measurements)?; + let secrets = self.authenticate_and_authorize_vm(attestation_payload, &measurements).unwrap_or(alloc::vec![]); ControlDataStorage::try_write(|control_data| { // We have a write lock on the entire control data! Spend here as little time as possible because we are @@ -101,7 +98,7 @@ impl PromoteToConfidentialVm { } fn process_device_tree(&self, memory_protector: &ConfidentialVmMemoryProtector) -> Result { - debug!("Reading flatten device tree (FDT) at memory address 0x{:?}", self.fdt_address); + debug!("Reading flatten device tree (FDT) at memory address {:?}", self.fdt_address); 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). @@ -131,35 +128,30 @@ impl PromoteToConfidentialVm { Ok(number_of_confidential_harts) } - fn read_tee_attestation_payload( - &self, memory_protector: &ConfidentialVmMemoryProtector, - ) -> Result, Error> { - match self.tee_attestation_payload_address { - Some(tee_attestation_payload_address) => { - debug!("Reading TEE attestation payload (TAP) at memory address {:?}", tee_attestation_payload_address); - let address_in_confidential_memory = memory_protector.translate_address(&tee_attestation_payload_address)?; + fn read_attestation_payload(&self, memory_protector: &ConfidentialVmMemoryProtector) -> Result, Error> { + match self.attestation_payload_address { + Some(attestation_payload_address) => { + debug!("Reading TEE attestation payload (TAP) at memory address {:?}", attestation_payload_address); + let address_in_confidential_memory = memory_protector.translate_address(&attestation_payload_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 + // within the page boundary. These 8 bytes should contain the `magic` (first 4 bytes) and `size` (next 2 // bytes). 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 header: u64 = unsafe { address_in_confidential_memory.read_volatile().try_into().map_err(|_| Error::AuthBlobNotAlignedTo32Bits())? }; - let total_size = ((header >> 16) & 0xFFFF) as usize + tap::ACE_HEADER_SIZE; + let total_size = tap::ACE_HEADER_SIZE + ((header >> 32) & 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!(total_size.div_ceil(PageSize::Size2MiB.in_bytes()) == 1, Error::AuthBlobInvalidSize())?; - let large_page = Self::relocate(memory_protector, &tee_attestation_payload_address, total_size, true)?; - + let large_page = Self::relocate(memory_protector, &attestation_payload_address, total_size, true)?; // TODO: we should parse to the blob key that will allow to unlock the lockbox. - let mut parser = unsafe { TeeAttestationPayloadParser::from_raw_pointer(large_page.address().to_ptr(), total_size)? }; - let tee_attestation_payload = parser.parse_and_verify()?; - + let mut parser = unsafe { AttestationPayloadParser::from_raw_pointer(large_page.address().to_ptr(), total_size)? }; + let attestation_payload = parser.parse_and_verify()?; // Clean up, deallocate pages PageAllocator::release_pages(alloc::vec![large_page.deallocate()]); - - Ok(Some(tee_attestation_payload)) + Ok(Some(attestation_payload)) } None => Ok(None), } @@ -168,20 +160,20 @@ impl PromoteToConfidentialVm { /// Performs local attestation. It decides if the VM can be promote into a confidential VM and decrypts the attestation secret intended /// for this confidential VM. fn authenticate_and_authorize_vm( - &self, tee_attestation_payload: Option, measurements: &StaticMeasurements, - ) -> Result, Error> { + &self, attestation_payload: Option, measurements: &StaticMeasurements, + ) -> Result, Error> { use crate::core::control_data::MeasurementDigest; - match tee_attestation_payload { - Some(tee_attestation_payload) => { - debug!("Authenticating and authorizing the confidential VM"); - for digest in tee_attestation_payload.digests.iter() { - debug!("Reference measurement: {:?}={:?}=0x{}", digest.pcr_id, digest.algorithm, digest.value_in_hex()); - // TODO: make sure we compare digests of the same algorithm... + match attestation_payload { + Some(attestation_payload) => { + ensure!(attestation_payload.digests.len() > 1, Error::LocalAttestationFailed())?; + for digest in attestation_payload.digests.iter() { + debug!("Reference PCR{:?}={:?}=0x{}", digest.pcr_id, digest.algorithm, digest.value_in_hex()); + ensure!(digest.algorithm == tap::DigestAlgorithm::Sha512, Error::LocalAttestationNotSupportedDigest())?; let pcr_value = MeasurementDigest::clone_from_slice(&digest.value); ensure!(measurements.compare(digest.pcr_id() as usize, pcr_value)?, Error::LocalAttestationFailed())?; } - debug!("Attestation successful, read {} secrets", tee_attestation_payload.secrets.len()); - Ok(tee_attestation_payload.secrets) + debug!("Attestation succeeded, read {} secrets", attestation_payload.secrets.len()); + Ok(attestation_payload.secrets) } None => Ok(alloc::vec![]), } diff --git a/tools/local_attestation/Cargo.toml b/tools/cove_tap_tool/Cargo.toml similarity index 88% rename from tools/local_attestation/Cargo.toml rename to tools/cove_tap_tool/Cargo.toml index a65d5204..17086406 100644 --- a/tools/local_attestation/Cargo.toml +++ b/tools/cove_tap_tool/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "ace-tap" +name = "cove-tap-tool" version = "0.1.0" authors = ["Wojciech Ozga "] -description = "Tool to create TEE attestation payload for a confidential VM" +description = "Tool to create TVM attestation payload for CoVE's confidential VM" edition = "2021" [dependencies] diff --git a/tools/local_attestation/Makefile b/tools/cove_tap_tool/Makefile similarity index 90% rename from tools/local_attestation/Makefile rename to tools/cove_tap_tool/Makefile index 7a1a8fa7..3d011ced 100644 --- a/tools/local_attestation/Makefile +++ b/tools/cove_tap_tool/Makefile @@ -7,21 +7,21 @@ RELEASE = --release CHAIN=riscv64gc-unknown-linux-gnu TARGET = --target=$(CHAIN) RUSTFLAGS = -C linker=riscv64-unknown-linux-gnu-gcc -EXEC_NAME = ace-tap +EXEC_NAME = cove-tap-tool MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) MAKEFILE_SOURCE_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) ACE_DIR := $(if $(ACE_DIR),$(ACE_DIR),$(MAKEFILE_SOURCE_DIR)../build/target/) -WORK_DIR=$(ACE_DIR)/local_attestation +WORK_DIR=$(ACE_DIR)/cove_tap_tool TOOLS_DIR=$(ACE_DIR)/tools -PLATFORM_RISCV_ABI ?= lp64d +PLATFORM_RISCV_ABI ?= lp64d PLATFORM_RISCV_ISA ?= rv64gc -PLATFORM_RISCV_XLEN ?= 64 +PLATFORM_RISCV_XLEN ?= 64 CROSS_COMPILE ?= riscv64-unknown-linux-gnu- -all: build +all: build build: fmt echo "Building Local Attestation utility" ;\ diff --git a/tools/local_attestation/README.md b/tools/cove_tap_tool/README.md similarity index 100% rename from tools/local_attestation/README.md rename to tools/cove_tap_tool/README.md diff --git a/tools/cove_tap_tool/src/attach.rs b/tools/cove_tap_tool/src/attach.rs new file mode 100644 index 00000000..ecada79c --- /dev/null +++ b/tools/cove_tap_tool/src/attach.rs @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2024 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use crate::error::Error; +use byteorder::WriteBytesExt; +use std::fs::OpenOptions; +use std::io::BufReader; +use std::io::Read; +use std::io::Seek; +use std::io::SeekFrom; + +pub fn attach_tap( + input_file: String, + tap_file_name: String, + output_file: Option, +) -> Result<(), Error> { + let output_file_name = match output_file { + Some(f) if input_file != f => { + std::fs::copy(input_file, f.clone())?; + f + } + Some(f) => f, + None => input_file, + }; + let offset = find_placehoder(&output_file_name)?; + // clear the placeholder + let mut output_file = OpenOptions::new().write(true).open(output_file_name)?; + output_file.seek(SeekFrom::Start(offset))?; + (tap::ACE_HEADER_SIZE..tap::ACE_MAX_TAP_SIZE).try_for_each(|_| output_file.write_u8(0u8))?; + // write expected TAP from the beginning of the offset + output_file.seek(SeekFrom::Start(offset))?; + let mut tap_file = OpenOptions::new().read(true).open(tap_file_name)?; + std::io::copy(&mut tap_file, &mut output_file)?; + println!("TAP attached successfully"); + Ok(()) +} + +fn find_placehoder(output_file_name: &str) -> Result { + let output_file = OpenOptions::new().read(true).open(output_file_name)?; + let mut buf = BufReader::new(output_file); + let mut buffer = [0u8; 4]; + let mut offset = 0u64; + while let Ok(bytes_read) = buf.read(&mut buffer) { + if bytes_read == 0 { + break; + } + if u32::from_le_bytes(buffer) == tap::ACE_MAGIC_TAP_START { + println!("Found TAP placeholder at offset: {:x}", offset); + return Ok(offset); + } + offset += bytes_read as u64; + } + Err(Error::PlaceholderError()) +} diff --git a/tools/local_attestation/src/error.rs b/tools/cove_tap_tool/src/error.rs similarity index 94% rename from tools/local_attestation/src/error.rs rename to tools/cove_tap_tool/src/error.rs index 781c7781..6dee00fe 100644 --- a/tools/local_attestation/src/error.rs +++ b/tools/cove_tap_tool/src/error.rs @@ -20,6 +20,8 @@ pub enum Error { CannotOpenFile(String), #[error("Could not parse int")] IntParseError(#[from] core::num::ParseIntError), + #[error("Placeholder error")] + PlaceholderError(), } impl From for Error { diff --git a/tools/local_attestation/src/generate.rs b/tools/cove_tap_tool/src/generate.rs similarity index 58% rename from tools/local_attestation/src/generate.rs rename to tools/cove_tap_tool/src/generate.rs index 83138c83..b6829e3b 100644 --- a/tools/local_attestation/src/generate.rs +++ b/tools/cove_tap_tool/src/generate.rs @@ -5,17 +5,17 @@ use crate::ensure; use crate::error::Error; use std::fs::OpenOptions; use std::io::Write; +use tap::AttestationPayload; +use tap::AttestationPayloadSerializer; +use tap::Digest; +use tap::DigestAlgorithm; use tap::Lockbox; -use tap::TapDigest; -use tap::TapDigestAlgorithm; -use tap::TapLockboxAlgorithm; -use tap::TapSecret; -use tap::TeeAttestationPayload; -use tap::TeeAttestationPayloadSerializer; +use tap::LockboxAlgorithm; +use tap::Secret; pub fn generate_tap( pcrs: Vec<(u16, Vec)>, - confidential_vm_secrets: Vec<(usize, Vec)>, + confidential_vm_secrets: Vec<(u64, Vec)>, tee_public_keys_files: Vec, output_file: String, ) -> Result<(), Error> { @@ -25,7 +25,7 @@ pub fn generate_tap( )?; ensure!( tee_public_keys_files.len() < 1024, - Error::InvalidParameter(format!("Confidential VM TAP support max 1024 lockboxes")) + Error::InvalidParameter(format!("Confidential VM TAP supports max 1024 lockboxes")) )?; // let symmetric_key = [0u8; 32]; @@ -33,15 +33,15 @@ pub fn generate_tap( let mut lockboxes = vec![]; lockboxes.push(Lockbox { name: 0u64, - algorithm: TapLockboxAlgorithm::Debug, + algorithm: LockboxAlgorithm::Debug, value: [0xFF; 16].to_vec(), }); let mut digests = vec![]; for (pcr_id, pcr_value) in pcrs.into_iter() { - let tap_digest = TapDigest { + let tap_digest = Digest { pcr_id, - algorithm: TapDigestAlgorithm::Sha512, + algorithm: DigestAlgorithm::Sha512, value: pcr_value, }; println!("Writing PCR{}={}", pcr_id, tap_digest.value_in_hex()); @@ -49,18 +49,22 @@ pub fn generate_tap( } let mut secrets = vec![]; - secrets.push(TapSecret { - name: 0, - value: [0xFF; 16].to_vec(), - }); + for (secret_name, secret_value) in confidential_vm_secrets.into_iter() { + let secret = Secret { + name: secret_name, + value: secret_value, + }; + println!("Writing secret {}", secret_name); + secrets.push(secret); + } - let tap = TeeAttestationPayload { + let tap = AttestationPayload { lockboxes, digests, secrets, }; - let serializer = TeeAttestationPayloadSerializer::new(); + let serializer = AttestationPayloadSerializer::new(); let serialized = serializer.serialize(tap)?; // write the entire TAP to the output file @@ -73,20 +77,5 @@ pub fn generate_tap( .map_err(|_| Error::CannotOpenFile(output_file.clone()))?; output.write(&serialized)?; - // test if everything went well - use std::io::Read; - use std::io::{Seek, SeekFrom}; - output.seek(SeekFrom::End(-1 * (serialized.len() as i64)))?; - let mut test_data: Vec = vec![0u8; serialized.len()]; - output.read_exact(&mut test_data)?; - let mut parser = tap::TeeAttestationPayloadParser::from_raw_pointer( - test_data.as_mut_ptr() as *const u8, - test_data.len() - 4, - )?; - let tap = parser.parse_and_verify()?; - for digest in tap.digests.iter() { - println!("Read PCR{}={}", digest.pcr_id, digest.value_in_hex()); - } - Ok(()) } diff --git a/tools/local_attestation/src/main.rs b/tools/cove_tap_tool/src/main.rs similarity index 89% rename from tools/local_attestation/src/main.rs rename to tools/cove_tap_tool/src/main.rs index c02382a9..d3f24abd 100644 --- a/tools/local_attestation/src/main.rs +++ b/tools/cove_tap_tool/src/main.rs @@ -4,7 +4,7 @@ use crate::error::Error; use clap::{Parser, Subcommand}; -mod append; +mod attach; mod error; mod generate; @@ -17,7 +17,7 @@ struct Args { #[derive(Subcommand, Debug, Clone)] enum Commands { - Append { + Attach { #[arg(short, long)] input_file: String, #[arg(short, long)] @@ -28,8 +28,8 @@ enum Commands { Generate { #[arg(short='p', long="pcrs", value_parser = parse_key_val::, value_delimiter = ',', required=true)] pcrs: Vec<(u16, Vec)>, - #[arg(long="secrets", value_parser = parse_key_val::, value_delimiter = ',')] - confidential_vm_secrets: Vec<(usize, Vec)>, + #[arg(long="secrets", value_parser = parse_key_val::, value_delimiter = ',')] + confidential_vm_secrets: Vec<(u64, Vec)>, #[clap(short, long, value_delimiter = ' ', num_args = 1..)] tee_public_keys_files: Vec, #[arg(short, long)] @@ -69,11 +69,11 @@ pub fn decode_hex(s: &str) -> Result, core::num::ParseIntError> { fn main() -> Result<(), Error> { Ok(match Args::parse().cmd { - Commands::Append { + Commands::Attach { input_file, tap_file, output_file, - } => append::append_tap(input_file, tap_file, output_file), + } => attach::attach_tap(input_file, tap_file, output_file), Commands::Generate { pcrs, confidential_vm_secrets, diff --git a/tools/local_attestation/src/append.rs b/tools/local_attestation/src/append.rs deleted file mode 100644 index aa81193f..00000000 --- a/tools/local_attestation/src/append.rs +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IBM Corporation -// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich -// SPDX-License-Identifier: Apache-2.0 -use crate::error::Error; -use byteorder::WriteBytesExt; -use std::fs::OpenOptions; -use std::io::Read; -use std::io::Seek; -use std::io::SeekFrom; - -pub fn append_tap( - input_file: String, - tap_file_name: String, - output_file: Option, -) -> Result<(), Error> { - let output_file_name = match output_file { - Some(f) if input_file != f => { - std::fs::copy(input_file, f.clone())?; - f - } - Some(f) => f, - None => input_file, - }; - let mut output_file_size = file_size(&output_file_name)?; - - let mut output_file = OpenOptions::new() - .read(true) - .write(true) - .append(true) - .open(output_file_name)?; - - // check first if there is a TAP already appended - output_file.seek(SeekFrom::End(-4))?; - let mut footer = [0u8; 4]; - output_file.read_exact(&mut footer)?; - if u16::from_le_bytes([footer[0], footer[1]]) == tap::spec::ACE_MAGIC_TAP_END { - println!("Removing old TAP"); - let old_tap_size = u16::from_le_bytes([footer[2], footer[3]]) as u64; - output_file.set_len(output_file_size - old_tap_size)?; - output_file.seek(SeekFrom::End(0))?; - output_file_size -= old_tap_size; - } - - // 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; - println!( - "Padding with {} bytes to align to u64", - padding_size_in_bytes - ); - (0..padding_size_in_bytes).try_for_each(|_| 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)?; - - println!("TAP appended successfully"); - Ok(()) -} - -fn file_size(file_name: &str) -> Result { - Ok(OpenOptions::new() - .read(true) - .open(file_name)? - .seek(SeekFrom::End(0))?) -} From 7b6477cb3c3b6c059a3fe7a7c6643876f19700a0 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Mon, 7 Oct 2024 10:39:54 -0500 Subject: [PATCH 12/18] updated patches for Linux kernel Signed-off-by: Wojciech Ozga --- .../patches/linux/6.3-rc4/0002-ace.patch | 256 ++++++++++++------ 1 file changed, 178 insertions(+), 78 deletions(-) diff --git a/hypervisor/patches/linux/6.3-rc4/0002-ace.patch b/hypervisor/patches/linux/6.3-rc4/0002-ace.patch index 779ce8d0..60dfdaed 100644 --- a/hypervisor/patches/linux/6.3-rc4/0002-ace.patch +++ b/hypervisor/patches/linux/6.3-rc4/0002-ace.patch @@ -13,22 +13,32 @@ index 047679554453..04fefcbe7c7e 100644 prot_virt= [S390] enable hosting protected virtual machines diff --git a/arch/riscv/cove/core.c b/arch/riscv/cove/core.c -index 582feb1c6c8d..732434279b34 100644 +index 582feb1c6c8d..46fb91279a9c 100644 --- a/arch/riscv/cove/core.c +++ b/arch/riscv/cove/core.c -@@ -38,3 +38,24 @@ void riscv_cove_sbi_init(void) +@@ -12,6 +12,9 @@ + #include + #include + #include ++#include ++ ++extern char __cove_tap_start[]; + + static bool is_tvm; + +@@ -38,3 +41,24 @@ void riscv_cove_sbi_init(void) if (sbi_probe_extension(SBI_EXT_COVG) > 0) is_tvm = true; } + -+int promote_to_cove_guest(char *boot_command_line, unsigned long fdt_address) ++int promote_to_cove_guest() +{ + struct sbiret ret; + int rc = 0; + + if (strstr(boot_command_line, "promote_to_cove_guest")) { -+ ret = sbi_ecall(SBI_EXT_COVH, SBI_EXT_COVH_PROMOTE_TO_TVM, fdt_address, -+ 0, 0, 0, 0, 0); ++ ret = sbi_ecall(SBI_EXT_COVH, SBI_EXT_COVH_PROMOTE_TO_TVM, dtb_early_pa, ++ __pa(__cove_tap_start), 0, 0, 0, 0); + if (ret.error) { + rc = sbi_err_map_linux_errno(ret.error); + goto done; @@ -43,45 +53,52 @@ index 582feb1c6c8d..732434279b34 100644 +} \ No newline at end of file diff --git a/arch/riscv/include/asm/cove.h b/arch/riscv/include/asm/cove.h -index c4d609d64150..59aba7f9061f 100644 +index c4d609d64150..f7fd85200715 100644 --- a/arch/riscv/include/asm/cove.h +++ b/arch/riscv/include/asm/cove.h @@ -14,6 +14,7 @@ #ifdef CONFIG_RISCV_COVE_GUEST void riscv_cove_sbi_init(void); bool is_cove_guest(void); -+int promote_to_cove_guest(char *boot_command_line, unsigned long fdt_address); ++int promote_to_cove_guest(void); #else /* CONFIG_RISCV_COVE_GUEST */ static inline bool is_cove_guest(void) { -@@ -22,6 +23,11 @@ static inline bool is_cove_guest(void) +@@ -22,6 +23,10 @@ static inline bool is_cove_guest(void) static inline void riscv_cove_sbi_init(void) { } -+static inline int promote_to_cove_guest(char *boot_command_line, -+ unsigned long fdt_address) -+{ ++static inline int promote_to_cove_guest(void) ++{ + return 0; +} #endif /* CONFIG_RISCV_COVE_GUEST */ #endif /* __RISCV_COVE_H__ */ diff --git a/arch/riscv/include/asm/kvm_cove.h b/arch/riscv/include/asm/kvm_cove.h -index afaea7c621bb..561da58376ac 100644 +index afaea7c621bb..f9ce258dfab8 100644 --- a/arch/riscv/include/asm/kvm_cove.h +++ b/arch/riscv/include/asm/kvm_cove.h -@@ -19,6 +19,10 @@ +@@ -19,6 +19,13 @@ #include #include -+DECLARE_STATIC_KEY_FALSE(kvm_riscv_covi_available); -+#define kvm_riscv_covi_available() \ -+ static_branch_unlikely(&kvm_riscv_covi_available) ++#define KVM_COVE_TSM_CAP_PROMOTE_TVM 0x0 ++#define KVM_COVE_TSM_CAP_ATTESTATION_LOCAL 0x1 ++#define KVM_COVE_TSM_CAP_ATTESTATION_REMOTE 0x2 ++#define KVM_COVE_TSM_CAP_AIA 0x3 ++#define KVM_COVE_TSM_CAP_MRIF 0x4 ++#define KVM_COVE_TSM_CAP_MEMORY_ALLOCATION 0x5 + #define KVM_COVE_PAGE_SIZE_4K (1UL << 12) #define KVM_COVE_PAGE_SIZE_2MB (1UL << 21) #define KVM_COVE_PAGE_SIZE_1GB (1UL << 30) -@@ -130,7 +134,10 @@ int kvm_riscv_cove_init(void); +@@ -126,11 +133,15 @@ static inline bool is_cove_vcpu(struct kvm_vcpu *vcpu) + #ifdef CONFIG_RISCV_COVE_HOST + + bool kvm_riscv_cove_enabled(void); ++bool kvm_riscv_cove_capability(unsigned long cap); + int kvm_riscv_cove_init(void); /* TVM related functions */ void kvm_riscv_cove_vm_destroy(struct kvm *kvm); @@ -93,7 +110,14 @@ index afaea7c621bb..561da58376ac 100644 /* TVM VCPU related functions */ void kvm_riscv_cove_vcpu_destroy(struct kvm_vcpu *vcpu); -@@ -164,7 +171,13 @@ static inline int kvm_riscv_cove_hardware_enable(void) {return 0; } +@@ -158,13 +169,20 @@ int kvm_riscv_cove_aia_convert_imsic(struct kvm_vcpu *vcpu, phys_addr_t imsic_pa + int kvm_riscv_cove_vcpu_imsic_addr(struct kvm_vcpu *vcpu); + #else + static inline bool kvm_riscv_cove_enabled(void) {return false; }; ++static inline bool kvm_riscv_cove_capability(unsigned long cap) { return false; }; + static inline int kvm_riscv_cove_init(void) { return -1; } + static inline void kvm_riscv_cove_hardware_disable(void) {} + static inline int kvm_riscv_cove_hardware_enable(void) {return 0; } /* TVM related functions */ static inline void kvm_riscv_cove_vm_destroy(struct kvm *kvm) {} @@ -136,7 +160,7 @@ index 5b37a12337b1..763a931407f3 100644 #endif /* __RISCV_KVM_VCPU_SBI_H__ */ diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h -index 03b0cc871242..c48fa25a24b4 100644 +index 03b0cc871242..01e9e5b1d7a2 100644 --- a/arch/riscv/include/asm/sbi.h +++ b/arch/riscv/include/asm/sbi.h @@ -374,6 +374,7 @@ enum sbi_ext_covh_fid { @@ -147,6 +171,22 @@ index 03b0cc871242..c48fa25a24b4 100644 }; enum sbi_ext_covi_fid { +@@ -410,9 +411,15 @@ struct sbi_cove_tsm_info { + /* Current state of the TSM */ + enum sbi_cove_tsm_state tstate; + ++ /* TSM implementation identifier */ ++ uint32_t impl_id; ++ + /* Version of the loaded TSM */ + uint32_t version; + ++ /* Capabilities of the TSM */ ++ unsigned long capabilities; ++ + /* Number of 4K pages required per TVM */ + unsigned long tvm_pages_needed; + diff --git a/arch/riscv/include/uapi/asm/kvm.h b/arch/riscv/include/uapi/asm/kvm.h index 2a2434136e39..679a6727a143 100644 --- a/arch/riscv/include/uapi/asm/kvm.h @@ -160,7 +200,7 @@ index 2a2434136e39..679a6727a143 100644 }; diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c -index 20b028090cb1..440b4f838342 100644 +index 20b028090cb1..343fe4d51a21 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -36,6 +36,7 @@ @@ -175,10 +215,55 @@ index 20b028090cb1..440b4f838342 100644 void __init setup_arch(char **cmdline_p) { parse_dtb(); -+ promote_to_cove_guest(boot_command_line, dtb_early_pa); ++ promote_to_cove_guest(); setup_initial_init_mm(_stext, _etext, _edata, _end); *cmdline_p = boot_command_line; +diff --git a/arch/riscv/kernel/vmlinux-xip.lds.S b/arch/riscv/kernel/vmlinux-xip.lds.S +index eab9edc3b631..e8b14457d2be 100644 +--- a/arch/riscv/kernel/vmlinux-xip.lds.S ++++ b/arch/riscv/kernel/vmlinux-xip.lds.S +@@ -58,6 +58,17 @@ SECTIONS + } + _exiprom = .; /* End of XIP ROM area */ + ++#ifdef CONFIG_RISCV_COVE_GUEST ++ . = ALIGN(4096); ++ .cove_tvm_attestation_payload : { ++ __cove_tap_start = .; ++ LONG(0xace0ace0) ++ SHORT(0x0FFA) ++ FILL(0x00) ++ . += 4090; ++ __cove_tap_end = .; ++ } ++#endif + + /* + * From this point, stuff is considered writable and will be copied to RAM +diff --git a/arch/riscv/kernel/vmlinux.lds.S b/arch/riscv/kernel/vmlinux.lds.S +index 53a8ad65b255..a2af65f95034 100644 +--- a/arch/riscv/kernel/vmlinux.lds.S ++++ b/arch/riscv/kernel/vmlinux.lds.S +@@ -113,6 +113,18 @@ SECTIONS + } + __init_end = .; + ++#ifdef CONFIG_RISCV_COVE_GUEST ++ . = ALIGN(4096); ++ .cove_tvm_attestation_payload : { ++ __cove_tap_start = .; ++ LONG(0xace0ace0) ++ SHORT(0x0FFA) ++ FILL(0x00) ++ . += 4090; ++ __cove_tap_end = .; ++ } ++#endif ++ + /* Start of data section */ + _sdata = .; + RO_DATA(SECTION_ALIGN) diff --git a/arch/riscv/kvm/Makefile b/arch/riscv/kvm/Makefile index 31f4dbd97b03..fba7ebd0cd72 100644 --- a/arch/riscv/kvm/Makefile @@ -189,23 +274,23 @@ index 31f4dbd97b03..fba7ebd0cd72 100644 kvm-y += aia_imsic.o -kvm-$(CONFIG_RISCV_COVE_HOST) += cove_sbi.o cove.o vcpu_sbi_covg.o +kvm-$(CONFIG_RISCV_COVE_HOST) += cove_sbi.o cove.o vcpu_sbi_covg.o vcpu_sbi_covh.o -diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c -index 88b91b5d5837..3259d53197ac 100644 ---- a/arch/riscv/kvm/aia.c -+++ b/arch/riscv/kvm/aia.c -@@ -30,6 +30,7 @@ static int hgei_parent_irq; - unsigned int kvm_riscv_aia_nr_hgei; - unsigned int kvm_riscv_aia_max_ids; - DEFINE_STATIC_KEY_FALSE(kvm_riscv_aia_available); -+DEFINE_STATIC_KEY_FALSE(kvm_riscv_covi_available); - - static int aia_find_hgei(struct kvm_vcpu *owner) - { diff --git a/arch/riscv/kvm/cove.c b/arch/riscv/kvm/cove.c -index ba596b7f2240..8dca1b951c39 100644 +index ba596b7f2240..abaaffa07212 100644 --- a/arch/riscv/kvm/cove.c +++ b/arch/riscv/kvm/cove.c -@@ -589,9 +589,9 @@ void noinstr kvm_riscv_cove_vcpu_switchto(struct kvm_vcpu *vcpu, struct kvm_cpu_ +@@ -150,6 +150,11 @@ __always_inline bool kvm_riscv_cove_enabled(void) + return riscv_cove_enabled; + } + ++__always_inline bool kvm_riscv_cove_capability(unsigned long cap) ++{ ++ return tinfo.capabilities & BIT(cap); ++} ++ + static void kvm_cove_imsic_clone(void *info) + { + int rc; +@@ -589,9 +594,9 @@ void noinstr kvm_riscv_cove_vcpu_switchto(struct kvm_vcpu *vcpu, struct kvm_cpu_ /* * Bind the vsfile here instead during the new vsfile allocation because @@ -213,11 +298,11 @@ index ba596b7f2240..8dca1b951c39 100644 + * COVI bind call requires the TVM to be in finalized state. */ - if (tvcpuc->imsic.bind_required) { -+ if (likely(kvm_riscv_covi_available()) && tvcpuc->imsic.bind_required) { ++ if (kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_AIA) && tvcpuc->imsic.bind_required) { tvcpuc->imsic.bind_required = false; rc = kvm_riscv_cove_vcpu_imsic_bind(vcpu, BIT(tvcpuc->imsic.vsfile_hgei)); if (rc) { -@@ -628,12 +628,12 @@ void kvm_riscv_cove_vcpu_destroy(struct kvm_vcpu *vcpu) +@@ -628,12 +633,12 @@ void kvm_riscv_cove_vcpu_destroy(struct kvm_vcpu *vcpu) int kvm_riscv_cove_vcpu_init(struct kvm_vcpu *vcpu) { @@ -233,7 +318,7 @@ index ba596b7f2240..8dca1b951c39 100644 if (!vcpu) return -EINVAL; -@@ -654,36 +654,38 @@ int kvm_riscv_cove_vcpu_init(struct kvm_vcpu *vcpu) +@@ -654,36 +659,39 @@ int kvm_riscv_cove_vcpu_init(struct kvm_vcpu *vcpu) if (!tvcpuc) return -ENOMEM; @@ -252,8 +337,9 @@ index ba596b7f2240..8dca1b951c39 100644 - rc = cove_convert_pages(vcpus_phys_addr, tvcpuc->vcpu_state.npages, true); - if (rc) - goto convert_failed; -+ if (tinfo.tvcpu_pages_needed > 0) { -+ vcpus_page = alloc_pages(GFP_KERNEL | __GFP_ZERO, get_order_num_pages(tinfo.tvcpu_pages_needed)); ++ if (kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_MEMORY_ALLOCATION)) { ++ vcpus_page = alloc_pages(GFP_KERNEL | __GFP_ZERO, ++ get_order_num_pages(tinfo.tvcpu_pages_needed)); + if (!vcpus_page) { + rc = -ENOMEM; + goto alloc_page_failed; @@ -279,17 +365,17 @@ index ba596b7f2240..8dca1b951c39 100644 vcpu_create_failed: /* Reclaim all the pages or return to the confidential page pool */ - sbi_covh_tsm_reclaim_pages(vcpus_phys_addr, tvcpuc->vcpu_state.npages); -+ if (tinfo.tvcpu_pages_needed > 0) ++ if (kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_MEMORY_ALLOCATION)) + sbi_covh_tsm_reclaim_pages(vcpus_phys_addr, tvcpuc->vcpu_state.npages); convert_failed: - __free_pages(vcpus_page, get_order_num_pages(tinfo.tvcpu_pages_needed)); -+ if (tinfo.tvcpu_pages_needed > 0) ++ if (kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_MEMORY_ALLOCATION)) + __free_pages(vcpus_page, get_order_num_pages(tinfo.tvcpu_pages_needed)); alloc_page_failed: kfree(tvcpuc); -@@ -877,7 +879,7 @@ void kvm_riscv_cove_vm_destroy(struct kvm *kvm) +@@ -877,7 +885,7 @@ void kvm_riscv_cove_vm_destroy(struct kvm *kvm) kvm_err("Memory reclaim failed with rc %d\n", rc); } @@ -298,7 +384,18 @@ index ba596b7f2240..8dca1b951c39 100644 { struct kvm_cove_tvm_context *tvmc; struct page *tvms_page, *pgt_page; -@@ -980,6 +982,64 @@ int kvm_riscv_cove_vm_init(struct kvm *kvm) +@@ -885,6 +893,10 @@ int kvm_riscv_cove_vm_init(struct kvm *kvm) + unsigned long gstage_pgd_size = kvm_riscv_gstage_pgd_size(); + int rc = 0; + ++ // Multi-step TVM creation requires TSM that supports dynamic page conversion ++ if (!kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_MEMORY_ALLOCATION)) ++ return -EOPNOTSUPP; ++ + tvmc = kzalloc(sizeof(*tvmc), GFP_KERNEL); + if (!tvmc) + return -ENOMEM; +@@ -980,6 +992,67 @@ int kvm_riscv_cove_vm_init(struct kvm *kvm) return rc; } @@ -313,6 +410,9 @@ index ba596b7f2240..8dca1b951c39 100644 + void *nshmem = nacl_shmem(); + int rc = 0, gpr_id, offset; + ++ if (!kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_PROMOTE_TVM)) ++ return -EOPNOTSUPP; ++ + tvmc = kzalloc(sizeof(*tvmc), GFP_KERNEL); + if (!tvmc) + return -ENOMEM; @@ -363,17 +463,6 @@ index ba596b7f2240..8dca1b951c39 100644 int kvm_riscv_cove_init(void) { int rc; -@@ -988,6 +1048,10 @@ int kvm_riscv_cove_init(void) - if (sbi_probe_extension(SBI_EXT_COVH) <= 0 || !kvm_riscv_nacl_available()) - return -EOPNOTSUPP; - -+ if (sbi_probe_extension(SBI_EXT_COVI) > 0) { -+ static_branch_enable(&kvm_riscv_covi_available); -+ } -+ - rc = sbi_covh_tsm_get_info(&tinfo); - if (rc < 0) - return -EINVAL; diff --git a/arch/riscv/kvm/cove_sbi.c b/arch/riscv/kvm/cove_sbi.c index 4759b4920226..2325ee0f2a15 100644 --- a/arch/riscv/kvm/cove_sbi.c @@ -403,7 +492,7 @@ index 4759b4920226..2325ee0f2a15 100644 + return rc; +} diff --git a/arch/riscv/kvm/main.c b/arch/riscv/kvm/main.c -index a05941420307..bba0d87c7aa8 100644 +index a05941420307..bb5562ecc97c 100644 --- a/arch/riscv/kvm/main.c +++ b/arch/riscv/kvm/main.c @@ -31,12 +31,11 @@ int kvm_arch_hardware_enable(void) @@ -419,7 +508,7 @@ index a05941420307..bba0d87c7aa8 100644 + * mode, we need to initialize other CSRs as well for legacy VMs. */ - if (unlikely(kvm_riscv_cove_enabled())) -+ if (unlikely(kvm_riscv_cove_enabled()) && kvm_riscv_covi_available()) ++ if (unlikely(kvm_riscv_cove_enabled()) && kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_AIA)) goto enable_aia; hedeleg = 0; @@ -482,7 +571,7 @@ index b007c027baed..5a3ef6ea01e9 100644 d.addr, d.size, d.order); else diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c -index 005c7c93536d..4d8a01385ed4 100644 +index 005c7c93536d..62153d6ca579 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -730,8 +730,8 @@ long kvm_arch_vcpu_async_ioctl(struct file *filp, @@ -492,7 +581,7 @@ index 005c7c93536d..4d8a01385ed4 100644 - /* We do not support user space emulated IRQCHIP for TVMs yet */ - if (is_cove_vcpu(vcpu)) + /* We do not support user space emulated IRQCHIP for TVMs that utilize AIA yet */ -+ if (is_cove_vcpu(vcpu) && kvm_riscv_aia_initialized(vcpu->kvm)) ++ if (is_cove_vcpu(vcpu) && kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_AIA)) return -ENXIO; if (copy_from_user(&irq, argp, sizeof(irq))) @@ -524,7 +613,7 @@ index 005c7c93536d..4d8a01385ed4 100644 + * Do not update HVIP CSR for TVMs with AIA because AIA + * provides alternative method to inject interrupts. + */ -+ if (!is_cove_vcpu(vcpu) || !kvm_riscv_covi_available()) ++ if (!is_cove_vcpu(vcpu) || !kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_AIA)) kvm_riscv_update_hvip(vcpu); if (ret <= 0 || @@ -614,10 +703,10 @@ index 44a3b06d0593..42f3571361a0 100644 } diff --git a/arch/riscv/kvm/vcpu_sbi_covh.c b/arch/riscv/kvm/vcpu_sbi_covh.c new file mode 100644 -index 000000000000..17e8331bb404 +index 000000000000..ef3e255732b4 --- /dev/null +++ b/arch/riscv/kvm/vcpu_sbi_covh.c -@@ -0,0 +1,85 @@ +@@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 IBM. @@ -645,14 +734,13 @@ index 000000000000..17e8331bb404 + unsigned long hva, fault_addr, page; + struct kvm_memory_slot *memslot; + bool writable; ++ int bkt; + -+ memslot = search_memslots(kvm_memslots(vcpu->kvm), -+ kernel_map.phys_addr, true); -+ if (memslot) { -+ for (page = 0; page < memslot->npages; page++) { -+ fault_addr = gfn_to_gpa(memslot->base_gfn) + -+ page * PAGE_SIZE; -+ hva = gfn_to_hva_memslot_prot(memslot, ++ kvm_for_each_memslot(memslot, bkt, kvm_memslots(vcpu->kvm)) { ++ for (page = 0; page < memslot->npages; page++) { ++ fault_addr = gfn_to_gpa(memslot->base_gfn) + ++ page * PAGE_SIZE; ++ hva = gfn_to_hva_memslot_prot(memslot, + gpa_to_gfn(fault_addr), + &writable); + if (!kvm_is_error_hva(hva)) @@ -664,7 +752,7 @@ index 000000000000..17e8331bb404 + return 0; +} + -+static int kvm_riscv_cove_promote_to_tvm(struct kvm_vcpu *vcpu, ++static int kvm_riscv_cove_promote_to_tvm(struct kvm_vcpu *vcpu, + unsigned long fdt_address, + unsigned long tap_addr) { + int rc; @@ -704,10 +792,15 @@ index 000000000000..17e8331bb404 + .handler = kvm_sbi_ext_covh_handler, +}; diff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c -index 8a1460dba76c..c9d8d2b86609 100644 +index 8a1460dba76c..6df73ea8e16f 100644 --- a/arch/riscv/kvm/vm.c +++ b/arch/riscv/kvm/vm.c -@@ -48,11 +48,11 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) +@@ -44,15 +44,15 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) + + if (unlikely(type == KVM_VM_TYPE_RISCV_COVE)) { + if (!kvm_riscv_cove_enabled()) { +- kvm_err("Unable to init CoVE VM because cove is not enabled\n"); ++ kvm_err("Unable to init CoVE VM because CoVE extension is not enabled\n"); return -EPERM; } @@ -773,15 +866,22 @@ index 8523c508c3a5..498fbf5c6c9b 100644 EXPORT_SYMBOL_GPL(set_memory_decrypted); diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c -index d1a68b6d03b3..3d586b02bb6b 100644 +index d1a68b6d03b3..46e2ce22c729 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c -@@ -1169,7 +1169,7 @@ int __init early_init_dt_scan_chosen(char *cmdline) +@@ -1167,6 +1167,7 @@ int __init early_init_dt_scan_chosen(char *cmdline) + early_init_dt_check_for_initrd(node); + early_init_dt_check_for_elfcorehdr(node); ++#ifndef CONFIG_RISCV_COVE_GUEST rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l); if (rng_seed && l > 0) { -- add_bootloader_randomness(rng_seed, l); -+ // add_bootloader_randomness(rng_seed, l); + add_bootloader_randomness(rng_seed, l); +@@ -1178,6 +1179,7 @@ int __init early_init_dt_scan_chosen(char *cmdline) + of_fdt_crc32 = crc32_be(~0, initial_boot_params, + fdt_totalsize(initial_boot_params)); + } ++#endif - /* try to clear seed so it won't be found. */ - fdt_nop_property(initial_boot_params, node, "rng-seed"); + /* Retrieve command line */ + p = of_get_flat_dt_prop(node, "bootargs", &l); From a49f9e80c7599d371b7948b0604afb842d7c6023 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Mon, 7 Oct 2024 11:22:03 -0500 Subject: [PATCH 13/18] fixing build process Signed-off-by: Wojciech Ozga --- Makefile | 5 ++--- confidential-vms/linux_vm/Makefile | 8 ++------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index e5ff42e1..c32edae1 100644 --- a/Makefile +++ b/Makefile @@ -51,8 +51,8 @@ hypervisor: setup devtools 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/ rootfs ;\ + 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 rootfs ;\ PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" ACE_DIR=$(ACE_DIR) $(MAKE) -C hypervisor rootfs; hypervisor_dev: @@ -79,7 +79,6 @@ emulator: setup devtools tools: setup mkdir -p $(TOOLS_WORK_DIR) - PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" ACE_DIR=$(ACE_DIR) CROSS_COMPILE=$(CROSS_COMPILE) $(MAKE) -C tools/cove_tap_tool build cp -rf $(TOOLS_SOURCE_DIR)/*.sh $(TOOLS_WORK_DIR)/ cp -rf $(TOOLS_SOURCE_DIR)/ace $(TOOLS_WORK_DIR)/ diff --git a/confidential-vms/linux_vm/Makefile b/confidential-vms/linux_vm/Makefile index 45a5b08b..69f2ab48 100644 --- a/confidential-vms/linux_vm/Makefile +++ b/confidential-vms/linux_vm/Makefile @@ -54,8 +54,6 @@ buildroot: setup $(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"; \ @@ -68,20 +66,18 @@ dev: overlay: setup mkdir -p $(LINUX_VM_OVERLAY_WORK_DIR) ;\ mkdir -p $(LINUX_VM_OVERLAY_WORK_ROOT_DIR); \ - cp -r $(LINUX_VM_OVERLAY_SOURCE_DIR)/* $(LINUX_VM_OVERLAY_WORK_DIR)/ ;\ PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" $(MAKE) -s -C $(LINUX_VM_OVERLAY_WORK_ROOT_DIR)/ace_module/ RISCV=$(RISCV_GNU_TOOLCHAIN_WORK_DIR) PATH=$(PATH) ARCH=riscv KDIR=$(LINUX_DIR) CROSS_COMPILE=$(CROSS_COMPILE) CC="riscv64-unknown-linux-gnu-gcc" O=$(LINUX_DIR) ;\ + cp -r $(LINUX_VM_OVERLAY_SOURCE_DIR)/* $(LINUX_VM_OVERLAY_WORK_DIR)/ ;\ mkdir -p $(HYPERVISOR_OVERLAY_ROOT_DIR) ;\ cp $(LINUX_VM_ROOTFS_SOURCE_DIR)/*.sh $(HYPERVISOR_OVERLAY_ROOT_DIR)/ ;\ rm -rf $(HYPERVISOR_OVERLAY_LINUX_VM_DIR) && 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 -r $(LINUX_VM_BUILDROOT_WORK_DIR)/images/rootfs.cpio $(HYPERVISOR_OVERLAY_LINUX_VM_DIR) ;\ + cp -r $(LINUX_VM_BUILDROOT_ROOTFS) $(HYPERVISOR_OVERLAY_LINUX_VM_DIR) rootfs: overlay PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" $(MAKE) -s -C $(LINUX_VM_BUILDROOT_SOURCE_DIR) RISCV=$(RISCV_GNU_TOOLCHAIN_WORK_DIR) PATH=$(PATH) ARCH=riscv64 KDIR=$(LINUX_DIR) CROSS_COMPILE=$(CROSS_COMPILE) O=$(LINUX_VM_BUILDROOT_WORK_DIR) rootfs-ext2; \ cp -r $(LINUX_VM_BUILDROOT_WORK_DIR)/images/rootfs.cpio $(HYPERVISOR_OVERLAY_LINUX_VM_DIR); - clean: rm -rf $(ACE_DIR) From 09a1eb7017bbbde266de765bfe71c14a59599dc7 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Wed, 9 Oct 2024 08:05:45 -0500 Subject: [PATCH 14/18] fix build process Signed-off-by: Wojciech Ozga --- confidential-vms/linux_vm/Makefile | 2 +- confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/confidential-vms/linux_vm/Makefile b/confidential-vms/linux_vm/Makefile index 69f2ab48..e310c00e 100644 --- a/confidential-vms/linux_vm/Makefile +++ b/confidential-vms/linux_vm/Makefile @@ -66,8 +66,8 @@ dev: overlay: setup mkdir -p $(LINUX_VM_OVERLAY_WORK_DIR) ;\ mkdir -p $(LINUX_VM_OVERLAY_WORK_ROOT_DIR); \ - PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" $(MAKE) -s -C $(LINUX_VM_OVERLAY_WORK_ROOT_DIR)/ace_module/ RISCV=$(RISCV_GNU_TOOLCHAIN_WORK_DIR) PATH=$(PATH) ARCH=riscv KDIR=$(LINUX_DIR) CROSS_COMPILE=$(CROSS_COMPILE) CC="riscv64-unknown-linux-gnu-gcc" O=$(LINUX_DIR) ;\ cp -r $(LINUX_VM_OVERLAY_SOURCE_DIR)/* $(LINUX_VM_OVERLAY_WORK_DIR)/ ;\ + PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" $(MAKE) -s -C $(LINUX_VM_OVERLAY_WORK_ROOT_DIR)/ace_module/ RISCV=$(RISCV_GNU_TOOLCHAIN_WORK_DIR) PATH=$(PATH) ARCH=riscv KDIR=$(LINUX_DIR) CROSS_COMPILE=$(CROSS_COMPILE) CC="riscv64-unknown-linux-gnu-gcc" O=$(LINUX_DIR) ;\ mkdir -p $(HYPERVISOR_OVERLAY_ROOT_DIR) ;\ cp $(LINUX_VM_ROOTFS_SOURCE_DIR)/*.sh $(HYPERVISOR_OVERLAY_ROOT_DIR)/ ;\ rm -rf $(HYPERVISOR_OVERLAY_LINUX_VM_DIR) && mkdir -p $(HYPERVISOR_OVERLAY_LINUX_VM_DIR) ;\ 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 3319a8dd..94cc7233 100755 --- a/confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh +++ b/confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh @@ -56,7 +56,7 @@ ${QEMU_CMD} ${DEBUG_OPTIONS} \ -kernel ${KERNEL} \ -seed 0 \ -global virtio-mmio.force-legacy=false \ - -append "console=ttyS0 ro swiotlb=mmnn,force promote_to_cove_guest" \ + -append "console=ttyS0 ro root=/dev/vda 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 \ From 2ff35eacc5699e959d19eee1514d033aabaf3c69 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Thu, 10 Oct 2024 09:59:05 -0500 Subject: [PATCH 15/18] 2nd approach to local attestation Signed-off-by: Wojciech Ozga --- .../flattened_device_tree/src/error.rs | 4 ++ .../flattened_device_tree/src/lib.rs | 20 ++++++- .../src/core/architecture/riscv/mmu/mod.rs | 2 + .../core/architecture/riscv/mmu/page_table.rs | 23 ++++++-- .../core/control_data/confidential_hart.rs | 8 ++- .../confidential_vm_measurement.rs | 31 ++++++++--- .../confidential_vm_memory_layout.rs | 32 +++++++++++ .../confidential_vm_memory_protector.rs | 9 ++-- .../src/core/memory_protector/mod.rs | 2 + .../src/core/page_allocator/page.rs | 8 +-- .../promote_to_confidential_vm.rs | 53 +++++++++++++------ tools/cove_tap_tool/src/main.rs | 6 +++ tools/cove_tap_tool/src/measure.rs | 45 ++++++++++++++++ 13 files changed, 201 insertions(+), 42 deletions(-) create mode 100644 security-monitor/src/core/memory_protector/confidential_vm_memory_layout.rs create mode 100644 tools/cove_tap_tool/src/measure.rs diff --git a/security-monitor/rust-crates/flattened_device_tree/src/error.rs b/security-monitor/rust-crates/flattened_device_tree/src/error.rs index e9c76622..6175cc6b 100644 --- a/security-monitor/rust-crates/flattened_device_tree/src/error.rs +++ b/security-monitor/rust-crates/flattened_device_tree/src/error.rs @@ -12,4 +12,8 @@ pub enum FdtError { FdtErrorParsing(#[from] DevTreeError), #[error("No memory node")] NoMemoryNode(), + #[error("Error when casting integers")] + FdtIntegerCasting(#[from] core::num::TryFromIntError), + #[error("Invalid integer value")] + FdtInvalidIntegerValue(), } \ No newline at end of file diff --git a/security-monitor/rust-crates/flattened_device_tree/src/lib.rs b/security-monitor/rust-crates/flattened_device_tree/src/lib.rs index 7046a103..a2bcd4a8 100644 --- a/security-monitor/rust-crates/flattened_device_tree/src/lib.rs +++ b/security-monitor/rust-crates/flattened_device_tree/src/lib.rs @@ -21,7 +21,7 @@ pub struct FlattenedDeviceTree<'a> { impl<'a> FlattenedDeviceTree<'a> { pub const FDT_HEADER_SIZE: usize = 40; - + /// Creates an instance of a wrapper over the library that parses the flattened device tree (FDT). Returns error if address is not a 8-bytes aligned pointer to the valid flattened device tree (FDT). /// /// # Safety @@ -69,6 +69,18 @@ impl<'a> FlattenedDeviceTree<'a> { Ok(FdtMemoryRegion { base: reg_prop.u64(0)?, size: reg_prop.u64(1)? }) } + + pub fn initrd(&self) -> Result<(usize, usize), FdtError> { + let initrd_start_prop = self.inner.props() + .find(|p| Ok(p.name()?.starts_with("linux,initrd-start")))? + .ok_or_else(|| FdtError::NoMemoryNode())?; + + let initrd_end_prop = self.inner.props() + .find(|p| Ok(p.name()?.starts_with("linux,initrd-end")))? + .ok_or_else(|| FdtError::NoMemoryNode())?; + + Ok((initrd_start_prop.u64(0)? as usize, initrd_end_prop.u64(0)? as usize )) + } } #[derive(Copy, Clone, Debug, Default)] @@ -77,6 +89,12 @@ pub struct FdtMemoryRegion { pub size: u64, } +impl FdtMemoryRegion { + pub fn into_range(&self) -> Result<(usize, usize), FdtError> { + Ok((usize::try_from(self.base)?, usize::try_from(self.base.checked_add(self.size).ok_or(FdtError::FdtInvalidIntegerValue())?)?)) + } +} + #[derive(Clone)] pub struct Hart<'a, 'dt> { inner: DevTreeNode<'a, 'dt>, diff --git a/security-monitor/src/core/architecture/riscv/mmu/mod.rs b/security-monitor/src/core/architecture/riscv/mmu/mod.rs index 772883c5..3fc53400 100644 --- a/security-monitor/src/core/architecture/riscv/mmu/mod.rs +++ b/security-monitor/src/core/architecture/riscv/mmu/mod.rs @@ -5,6 +5,8 @@ use crate::core::architecture::CSR; use crate::core::memory_layout::NonConfidentialMemoryAddress; use crate::error::Error; +use crate::core::control_data::MeasurementDigest; +use crate::core::memory_protector::ConfidentialVmMemoryLayout; pub use hgatp::{Hgatp, HgatpMode}; pub use page_size::PageSize; pub use page_table::PageTable; diff --git a/security-monitor/src/core/architecture/riscv/mmu/page_table.rs b/security-monitor/src/core/architecture/riscv/mmu/page_table.rs index 7dc35ddd..654edc81 100644 --- a/security-monitor/src/core/architecture/riscv/mmu/page_table.rs +++ b/security-monitor/src/core/architecture/riscv/mmu/page_table.rs @@ -7,8 +7,9 @@ use crate::core::architecture::mmu::page_table_level::PageTableLevel; use crate::core::architecture::mmu::paging_system::PagingSystem; use crate::core::architecture::mmu::HgatpMode; use crate::core::architecture::{PageSize, SharedPage}; -use crate::core::control_data::MeasurementDigest; +use crate::core::control_data::{MeasurementDigest, StaticMeasurements}; use crate::core::memory_layout::{ConfidentialMemoryAddress, ConfidentialVmPhysicalAddress, NonConfidentialMemoryAddress}; +use crate::core::memory_protector::ConfidentialVmMemoryLayout; use crate::core::page_allocator::{Allocated, Page, PageAllocator}; use crate::error::Error; use alloc::boxed::Box; @@ -228,13 +229,25 @@ impl PageTable { /// Recursively extends measurements of all data pages in the order from the page with the lowest to the highest guest physical address. /// Returns error if the page table is malformed, i.e., there is a shared page mapping. - pub fn measure(&self, digest: &mut MeasurementDigest, address: usize) -> Result<(), Error> { + pub fn finalize( + &mut self, measurements: &mut StaticMeasurements, vm_memory_layout: &ConfidentialVmMemoryLayout, address: usize, + ) -> Result<(), Error> { use sha2::Digest; - self.logical_representation.iter().enumerate().try_for_each(|(i, entry)| { + self.logical_representation.iter_mut().enumerate().try_for_each(|(i, entry)| { let guest_physical_address = address + i * self.paging_system.data_page_size(self.level).in_bytes(); match entry { - LogicalPageTableEntry::PointerToNextPageTable(next_page_table) => next_page_table.measure(digest, guest_physical_address), - LogicalPageTableEntry::PageWithConfidentialVmData(page) => Ok(page.measure(digest, guest_physical_address)), + LogicalPageTableEntry::PointerToNextPageTable(next_page_table) => { + next_page_table.finalize(measurements, vm_memory_layout, guest_physical_address) + } + LogicalPageTableEntry::PageWithConfidentialVmData(page) => Ok(if vm_memory_layout.is_fdt(guest_physical_address) { + page.measure(measurements.pcr_fdt_mut(), guest_physical_address) + } else if vm_memory_layout.is_initrd(guest_physical_address) { + page.measure(measurements.pcr_initrd_mut(), guest_physical_address) + } else if vm_memory_layout.is_kernel(guest_physical_address) { + page.measure(measurements.pcr_kernel_mut(), guest_physical_address) + } else { + page.clear() + }), LogicalPageTableEntry::PageSharedWithHypervisor(_) => Err(Error::PageTableConfiguration()), LogicalPageTableEntry::NotMapped => Ok(()), } diff --git a/security-monitor/src/core/control_data/confidential_hart.rs b/security-monitor/src/core/control_data/confidential_hart.rs index db1065e8..83ea109d 100644 --- a/security-monitor/src/core/control_data/confidential_hart.rs +++ b/security-monitor/src/core/control_data/confidential_hart.rs @@ -186,11 +186,9 @@ impl ConfidentialHart { } } - pub fn measure(&self) -> MeasurementDigest { - let mut measurement = MeasurementDigest::default(); - self.confidential_hart_state.gprs().measure(&mut measurement); - self.confidential_hart_state.csrs().measure(&mut measurement); - measurement + pub fn measure(&self, digest: &mut MeasurementDigest) { + self.confidential_hart_state.gprs().measure(digest); + self.confidential_hart_state.csrs().measure(digest); } pub fn address(&self) -> usize { diff --git a/security-monitor/src/core/control_data/confidential_vm_measurement.rs b/security-monitor/src/core/control_data/confidential_vm_measurement.rs index 71c3e853..6b038a4f 100644 --- a/security-monitor/src/core/control_data/confidential_vm_measurement.rs +++ b/security-monitor/src/core/control_data/confidential_vm_measurement.rs @@ -10,24 +10,41 @@ pub type MeasurementDigest = GenericArray Self { - let mut measurements = Self([MeasurementDigest::default(); NUMBER_OF_REGISTERS]); - measurements.0[TVM_CODE_AND_STATIC_DATA_REGISTER_ID] = measured_pages; - measurements.0[TVM_CONFIGURATION_REGISTER_ID] = configuration; - measurements + pub fn default() -> Self { + Self([MeasurementDigest::default(); NUMBER_OF_REGISTERS]) } pub fn compare(&self, pcr_id: usize, digest: MeasurementDigest) -> Result { ensure!(pcr_id < NUMBER_OF_REGISTERS, Error::InvalidParameter())?; Ok(self.0[pcr_id] == digest) } + + pub fn pcr_kernel_mut(&mut self) -> &mut MeasurementDigest { + &mut self.0[KERNEL_PCR_ID] + } + + pub fn pcr_initrd_mut(&mut self) -> &mut MeasurementDigest { + &mut self.0[INITRD_PCR_ID] + } + + pub fn pcr_fdt_mut(&mut self) -> &mut MeasurementDigest { + &mut self.0[FDT_PCR_ID] + } + + pub fn pcr_boot_hart_mut(&mut self) -> &mut MeasurementDigest { + &mut self.0[BOOT_HART_PCR_ID] + } } impl core::fmt::Debug for StaticMeasurements { diff --git a/security-monitor/src/core/memory_protector/confidential_vm_memory_layout.rs b/security-monitor/src/core/memory_protector/confidential_vm_memory_layout.rs new file mode 100644 index 00000000..3d44403e --- /dev/null +++ b/security-monitor/src/core/memory_protector/confidential_vm_memory_layout.rs @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 + +#[derive(Debug)] +pub struct ConfidentialVmMemoryLayout { + kernel: (usize, usize), + fdt: (usize, usize), + initrd: Option<(usize, usize)>, +} + +impl ConfidentialVmMemoryLayout { + pub fn new(kernel: (usize, usize), fdt: (usize, usize), initrd: Option<(usize, usize)>) -> Self { + Self { kernel, fdt, initrd } + } + + pub fn is_kernel(&self, address: usize) -> bool { + self.kernel.0 <= address && address < self.kernel.1 + } + + pub fn is_fdt(&self, address: usize) -> bool { + self.fdt.0 <= address && address < self.fdt.1 + } + + pub fn is_initrd(&self, address: usize) -> bool { + self.initrd.and_then(|o| Some(o.0 <= address && address < o.1)).unwrap_or(false) + } + + pub fn is_in_vm_address_space(&self, address: usize) -> bool { + self.is_kernel(address) || self.is_fdt(address) || self.is_initrd(address) + } +} diff --git a/security-monitor/src/core/memory_protector/confidential_vm_memory_protector.rs b/security-monitor/src/core/memory_protector/confidential_vm_memory_protector.rs index 09cf5512..140cc52e 100644 --- a/security-monitor/src/core/memory_protector/confidential_vm_memory_protector.rs +++ b/security-monitor/src/core/memory_protector/confidential_vm_memory_protector.rs @@ -4,8 +4,9 @@ use crate::core::architecture::mmu::{Hgatp, PageTable}; use crate::core::architecture::riscv::{mmu, pmp, tlb}; use crate::core::architecture::{PageSize, SharedPage}; -use crate::core::control_data::{ConfidentialVmId, MeasurementDigest}; +use crate::core::control_data::{ConfidentialVmId, MeasurementDigest, StaticMeasurements}; use crate::core::memory_layout::{ConfidentialMemoryAddress, ConfidentialVmPhysicalAddress, NonConfidentialMemoryAddress}; +use crate::core::memory_protector::ConfidentialVmMemoryLayout; use crate::error::Error; /// Exposes an interface to configure the hardware memory isolation component in a way that @@ -67,10 +68,8 @@ impl ConfidentialVmMemoryProtector { self.root_page_table.translate(address) } - pub fn measure(&self) -> Result { - let mut initial_digest = MeasurementDigest::default(); - self.root_page_table.measure(&mut initial_digest, 0)?; - Ok(initial_digest) + pub fn finalize(&mut self, measurements: &mut StaticMeasurements, vm_memory_layout: &ConfidentialVmMemoryLayout) -> Result<(), Error> { + Ok(self.root_page_table.finalize(measurements, vm_memory_layout, 0)?) } /// Reconfigures hardware to enable access initiated from this physical hart to memory regions owned by the diff --git a/security-monitor/src/core/memory_protector/mod.rs b/security-monitor/src/core/memory_protector/mod.rs index 711dac9f..595e7ba3 100644 --- a/security-monitor/src/core/memory_protector/mod.rs +++ b/security-monitor/src/core/memory_protector/mod.rs @@ -1,8 +1,10 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 +pub use confidential_vm_memory_layout::ConfidentialVmMemoryLayout; pub use confidential_vm_memory_protector::ConfidentialVmMemoryProtector; pub use hypervisor_memory_protector::HypervisorMemoryProtector; +mod confidential_vm_memory_layout; mod confidential_vm_memory_protector; mod hypervisor_memory_protector; diff --git a/security-monitor/src/core/page_allocator/page.rs b/security-monitor/src/core/page_allocator/page.rs index 6b22b387..b6396e2b 100644 --- a/security-monitor/src/core/page_allocator/page.rs +++ b/security-monitor/src/core/page_allocator/page.rs @@ -316,8 +316,10 @@ impl Page { // below unsafe is ok because the page has been initialized and it owns the entire memory region. // We are creating a slice of bytes, so the number of elements in the slice is the same as the size of the page. let slice: &[u8] = unsafe { core::slice::from_raw_parts(self.address().to_ptr(), self.size().in_bytes()) }; - hasher.update(&slice); - hasher.finalize_into(digest); + if slice.iter().find(|b| **b != 0).is_some() { + hasher.update(&slice); + hasher.finalize_into(digest); + } } /// Returns all usize-aligned offsets within the page. @@ -335,7 +337,7 @@ impl Page { #[rr::args("(#p, γ)")] /// Postcondition: The page has been zeroized. #[rr::observe("γ": "mk_page p.(page_loc) p.(page_sz) (zero_page p.(page_sz))")] - fn clear(&mut self) { + pub fn clear(&mut self) { // Safety: below unwrap() is fine because we iterate over page's offsets and thus always // request a write to an offset within the page. self.offsets().for_each( 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 cc5572cc..bd8a937b 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 @@ -7,7 +7,7 @@ use crate::core::control_data::{ ConfidentialHart, ConfidentialVm, ConfidentialVmId, ControlDataStorage, HypervisorHart, StaticMeasurements, }; use crate::core::memory_layout::ConfidentialVmPhysicalAddress; -use crate::core::memory_protector::ConfidentialVmMemoryProtector; +use crate::core::memory_protector::{ConfidentialVmMemoryLayout, ConfidentialVmMemoryProtector}; use crate::core::page_allocator::{Allocated, Page, PageAllocator}; use crate::error::Error; use crate::non_confidential_flow::handlers::supervisor_binary_interface::SbiResponse; @@ -66,11 +66,11 @@ impl PromoteToConfidentialVm { fn create_confidential_vm(&self, shared_memory: &NaclSharedMemory) -> Result { debug!("Promoting a VM to a confidential VM"); // Copying the entire VM's state to the confidential memory, recreating the MMU configuration - let memory_protector = ConfidentialVmMemoryProtector::from_vm_state(&self.hgatp)?; + let mut memory_protector = ConfidentialVmMemoryProtector::from_vm_state(&self.hgatp)?; // The pointer to the flattened device tree (FDT) as well as the entire FDT must be treated as an untrusted input, which measurement // is reflected during attestation. We can parse FDT only after moving VM's data (and the FDT) to the confidential memory. - let number_of_confidential_harts = self.process_device_tree(&memory_protector)?; + let (vm_memory_layout, number_of_confidential_harts) = self.process_device_tree(&memory_protector)?; // TODO: generate htimedelta let htimedelta = 0; @@ -84,8 +84,7 @@ impl PromoteToConfidentialVm { .collect(); let attestation_payload = self.read_attestation_payload(&memory_protector).unwrap_or(None); - let measurements = StaticMeasurements::new(memory_protector.measure()?, confidential_harts[Self::BOOT_HART_ID].measure()); - debug!("VM measurements: {:?}", measurements); + let measurements = self.measure(&mut memory_protector, &vm_memory_layout, &confidential_harts)?; let secrets = self.authenticate_and_authorize_vm(attestation_payload, &measurements).unwrap_or(alloc::vec![]); @@ -97,7 +96,18 @@ impl PromoteToConfidentialVm { }) } - fn process_device_tree(&self, memory_protector: &ConfidentialVmMemoryProtector) -> Result { + fn measure( + &self, memory_protector: &mut ConfidentialVmMemoryProtector, vm_memory_layout: &ConfidentialVmMemoryLayout, + confidential_harts: &Vec, + ) -> Result { + let mut measurements = StaticMeasurements::default(); + memory_protector.finalize(&mut measurements, vm_memory_layout)?; + confidential_harts[Self::BOOT_HART_ID].measure(measurements.pcr_boot_hart_mut()); + debug!("VM measurements: {:?}", measurements); + Ok(measurements) + } + + fn process_device_tree(&self, memory_protector: &ConfidentialVmMemoryProtector) -> Result<(ConfidentialVmMemoryLayout, usize), Error> { debug!("Reading flatten device tree (FDT) at memory address {:?}", self.fdt_address); 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 @@ -113,19 +123,25 @@ impl PromoteToConfidentialVm { let large_page = Self::relocate(memory_protector, &self.fdt_address, fdt_total_size, false)?; // Security note: We parse untrusted FDT using an external library. A vulnerability in this library might blow up our security - // guarantees! Below unsafe is ok because FDT address is at least size of the FDT header and all FDT is in a continuous chunk of - // memory. See the safety requirements of `FlattenedDeviceTree::from_raw_pointer`. - let number_of_confidential_harts = match unsafe { FlattenedDeviceTree::from_raw_pointer(large_page.address().to_ptr()) } { - Ok(device_tree) => device_tree.harts().count(), - Err(_) => 0, - }; + // guarantees! Below unsafe is ok because the FDT address aligned to (at least) the size of the FDT header and all FDT is in a + // continuous chunk of memory. See the safety requirements of `FlattenedDeviceTree::from_raw_pointer`. + let device_tree = unsafe { FlattenedDeviceTree::from_raw_pointer(large_page.address().to_ptr()) }?; + + let number_of_confidential_harts = device_tree.harts().count(); + let ram_memory = device_tree.memory().and_then(|r| r.into_range())?; + let initrd = device_tree.initrd().ok(); + + let vm_memory_layout = + ConfidentialVmMemoryLayout::new(ram_memory, (self.fdt_address.usize(), self.fdt_address.usize() + fdt_total_size), initrd); + debug!("Virtual machine's memory layout: {:?}", vm_memory_layout); // Clean up, deallocate pages PageAllocator::release_pages(alloc::vec![large_page.deallocate()]); ensure!(number_of_confidential_harts > 0, Error::InvalidNumberOfHartsInFdt())?; ensure!(number_of_confidential_harts < ConfidentialVm::MAX_NUMBER_OF_HARTS_PER_VM, Error::InvalidNumberOfHartsInFdt())?; - Ok(number_of_confidential_harts) + + Ok((vm_memory_layout, number_of_confidential_harts)) } fn read_attestation_payload(&self, memory_protector: &ConfidentialVmMemoryProtector) -> Result, Error> { @@ -172,17 +188,22 @@ impl PromoteToConfidentialVm { let pcr_value = MeasurementDigest::clone_from_slice(&digest.value); ensure!(measurements.compare(digest.pcr_id() as usize, pcr_value)?, Error::LocalAttestationFailed())?; } - debug!("Attestation succeeded, read {} secrets", attestation_payload.secrets.len()); + debug!("Attestation succeeded, fetched {} secrets", attestation_payload.secrets.len()); Ok(attestation_payload.secrets) } None => Ok(alloc::vec![]), } } - /// Copies a buffer into a single large page. The input buffer is continuous across guest physical pages with G-stage address + /// Copies a buffer into a single large page. + /// + /// Why do we need this function? The input buffer is continuous across guest physical pages with G-stage address /// translation enabled but might not be continuous across the real physical pages. The output buffer is continous accross real /// physical pages. Returns error if (1) the buffer to copy is larger than 2 MiB page, or (2) the base address is not aligned to - /// 8-bytes. The caller is responsible for deallocating the page. + /// 8-bytes. + /// + /// Safety: + /// * The caller of this function is responsible for deallocating the page returned from this function. fn relocate( memory_protector: &ConfidentialVmMemoryProtector, base_address: &ConfidentialVmPhysicalAddress, number_of_bytes_to_copy: usize, clear: bool, diff --git a/tools/cove_tap_tool/src/main.rs b/tools/cove_tap_tool/src/main.rs index d3f24abd..a99731bf 100644 --- a/tools/cove_tap_tool/src/main.rs +++ b/tools/cove_tap_tool/src/main.rs @@ -7,6 +7,7 @@ use clap::{Parser, Subcommand}; mod attach; mod error; mod generate; +mod measure; #[derive(Parser)] #[command(author, version, about, long_about = None)] @@ -35,6 +36,10 @@ enum Commands { #[arg(short, long)] output_file: String, }, + Measure { + #[arg(short, long)] + kernel_file: String, + }, } /// Parse a single key-value pair @@ -85,5 +90,6 @@ fn main() -> Result<(), Error> { tee_public_keys_files, output_file, ), + Commands::Measure { kernel_file } => measure::measure(kernel_file), }?) } diff --git a/tools/cove_tap_tool/src/measure.rs b/tools/cove_tap_tool/src/measure.rs new file mode 100644 index 00000000..57aa0568 --- /dev/null +++ b/tools/cove_tap_tool/src/measure.rs @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2024 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use crate::error::Error; +use std::fs::OpenOptions; + +use sha2::digest::crypto_common::generic_array::GenericArray; +pub type DigestType = sha2::Sha384; +pub type MeasurementDigest = + GenericArray::OutputSize>; + +pub fn measure(kernel_file: String) -> Result<(), Error> { + use std::io::BufReader; + use std::io::Read; + + let mut digest = MeasurementDigest::default(); + + let kernel = OpenOptions::new().read(true).open(kernel_file)?; + let mut buf = BufReader::new(kernel); + let mut buffer = [0u8; 4096]; // 1 4KiB page + let mut address = 0x80000000 as u64; + while let Ok(bytes_read) = buf.read(&mut buffer) { + if bytes_read == 0 { + break; + } + let header = [buffer[0], buffer[1], buffer[2], buffer[3]]; + if u32::from_le_bytes(header) == tap::ACE_MAGIC_TAP_START { + (0..4096).for_each(|i| buffer[i] = 0); // security monitor will clear it + } + if buffer.iter().find(|b| **b != 0).is_some() { + use sha2::Digest; + let mut hasher = DigestType::new_with_prefix(digest.clone()); + hasher.update(address.to_le_bytes()); + hasher.update(&buffer); + hasher.finalize_into(&mut digest); + println!("Page 0x{:x} {:100x}", address, digest); + } else { + // println!("Page 0x{:x} empty", address); + } + address += 4096; + (0..4096).for_each(|i| buffer[i] = 0); + } + println!("Digest {:100x}", digest); + Ok(()) +} From 400143c8f45a6a031f9277ab9b82a4f3c0d913c8 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Thu, 17 Oct 2024 11:34:40 -0500 Subject: [PATCH 16/18] Promote VM to confidential VM earlier in the boot process. CVM integrity measurements can be now obtained from the kernel image. Signed-off-by: Wojciech Ozga --- .../linux_vm/configurations/linux64-defconfig | 1 + .../qemu_riscv64_virt_defconfig | 22 +- .../patches/linux/6.3-rc4/0002-ace.patch | 245 ++++++++---------- .../rust-crates/cove_tap/src/serializer.rs | 2 - .../promote_to_confidential_vm.rs | 4 +- tools/cove_tap_tool/src/measure.rs | 4 +- 6 files changed, 115 insertions(+), 163 deletions(-) diff --git a/confidential-vms/linux_vm/configurations/linux64-defconfig b/confidential-vms/linux_vm/configurations/linux64-defconfig index cddf085f..ccf7a1f0 100644 --- a/confidential-vms/linux_vm/configurations/linux64-defconfig +++ b/confidential-vms/linux_vm/configurations/linux64-defconfig @@ -326,6 +326,7 @@ CONFIG_FPU=y # CONFIG_RISCV_COVE_HOST is not set # CONFIG_RISCV_COVE_GUEST is not set CONFIG_RISCV_COVE_GUEST=y +CONFIG_RISCV_COVE_GUEST_PROMOTE=y # end of Confidential VM Extension(CoVE) Support # end of Platform type diff --git a/confidential-vms/linux_vm/configurations/qemu_riscv64_virt_defconfig b/confidential-vms/linux_vm/configurations/qemu_riscv64_virt_defconfig index 203d1d72..2eb3eef3 100644 --- a/confidential-vms/linux_vm/configurations/qemu_riscv64_virt_defconfig +++ b/confidential-vms/linux_vm/configurations/qemu_riscv64_virt_defconfig @@ -76,7 +76,7 @@ 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_INITRAMFS=y BR2_TARGET_ROOTFS_CPIO=y BR2_PACKAGE_BUSYBOX=y BR2_INIT_BUSYBOX=y @@ -87,23 +87,3 @@ 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 - - diff --git a/hypervisor/patches/linux/6.3-rc4/0002-ace.patch b/hypervisor/patches/linux/6.3-rc4/0002-ace.patch index 60dfdaed..043b81ed 100644 --- a/hypervisor/patches/linux/6.3-rc4/0002-ace.patch +++ b/hypervisor/patches/linux/6.3-rc4/0002-ace.patch @@ -1,80 +1,59 @@ -diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt -index 047679554453..04fefcbe7c7e 100644 ---- a/Documentation/admin-guide/kernel-parameters.txt -+++ b/Documentation/admin-guide/kernel-parameters.txt -@@ -4544,6 +4544,9 @@ - Param: - step/bucket size as a power of 2 for - statistical time based profiling. - -+ promote_to_cove_guest= [RISCV] Isolate VM from the hypervisor. -+ Format: -+ - prompt_ramdisk= [RAM] [Deprecated] - - prot_virt= [S390] enable hosting protected virtual machines +diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig +index 2ca9e01ad0e8..317e0f41ac9b 100644 +--- a/arch/riscv/Kconfig ++++ b/arch/riscv/Kconfig +@@ -526,6 +526,13 @@ config RISCV_COVE_GUEST + help + Enables support for running TVMs on platforms supporting CoVE. + ++config RISCV_COVE_GUEST_PROMOTE ++ bool "Automatic promotion of VM to TVM for Confidential VM Extension(CoVE)" ++ default n ++ select RISCV_COVE_GUEST ++ help ++ VM requests promotion to TVM during early boot on platforms supporting CoVE. ++ + endmenu # "Confidential VM Extension(CoVE) Support" + + endmenu # "Platform type" diff --git a/arch/riscv/cove/core.c b/arch/riscv/cove/core.c -index 582feb1c6c8d..46fb91279a9c 100644 +index 582feb1c6c8d..16d607113366 100644 --- a/arch/riscv/cove/core.c +++ b/arch/riscv/cove/core.c -@@ -12,6 +12,9 @@ - #include +@@ -13,6 +13,8 @@ #include #include -+#include -+ -+extern char __cove_tap_start[]; ++extern char __cove_tap_start[]; ++ static bool is_tvm; -@@ -38,3 +41,24 @@ void riscv_cove_sbi_init(void) - if (sbi_probe_extension(SBI_EXT_COVG) > 0) - is_tvm = true; - } + bool is_cove_guest(void) +diff --git a/arch/riscv/include/asm/cove_promote.h b/arch/riscv/include/asm/cove_promote.h +new file mode 100644 +index 000000000000..1472cbaf071f +--- /dev/null ++++ b/arch/riscv/include/asm/cove_promote.h +@@ -0,0 +1,19 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Definitions for promotion of VM to TVM. ++ * ++ * Copyright (c) 2024 IBM Corp. ++ * ++ * Authors: ++ * Wojciech Ozga ++ */ + -+int promote_to_cove_guest() -+{ -+ struct sbiret ret; -+ int rc = 0; ++#ifndef __RISCV_COVE_PROMOTE_H__ ++#define __RISCV_COVE_PROMOTE_H__ + -+ if (strstr(boot_command_line, "promote_to_cove_guest")) { -+ ret = sbi_ecall(SBI_EXT_COVH, SBI_EXT_COVH_PROMOTE_TO_TVM, dtb_early_pa, -+ __pa(__cove_tap_start), 0, 0, 0, 0); -+ if (ret.error) { -+ rc = sbi_err_map_linux_errno(ret.error); -+ goto done; -+ } -+ } -+ pr_info("Promotion to CoVE guest succeeded\n"); -+ return rc; ++#ifdef CONFIG_RISCV_COVE_GUEST_PROMOTE ++#define COVE_PROMOTE_SBI_EXT_ID 0x434F5648 ++#define COVE_PROMOTE_SBI_FID 0x15 ++#endif /* CONFIG_RISCV_COVE_GUEST_PROMOTE */ + -+done: -+ pr_err("Promotion to CoVE guest failed %d\n", rc); -+ return rc; -+} -\ No newline at end of file -diff --git a/arch/riscv/include/asm/cove.h b/arch/riscv/include/asm/cove.h -index c4d609d64150..f7fd85200715 100644 ---- a/arch/riscv/include/asm/cove.h -+++ b/arch/riscv/include/asm/cove.h -@@ -14,6 +14,7 @@ - #ifdef CONFIG_RISCV_COVE_GUEST - void riscv_cove_sbi_init(void); - bool is_cove_guest(void); -+int promote_to_cove_guest(void); - #else /* CONFIG_RISCV_COVE_GUEST */ - static inline bool is_cove_guest(void) - { -@@ -22,6 +23,10 @@ static inline bool is_cove_guest(void) - static inline void riscv_cove_sbi_init(void) - { - } -+static inline int promote_to_cove_guest(void) -+{ -+ return 0; -+} - #endif /* CONFIG_RISCV_COVE_GUEST */ - - #endif /* __RISCV_COVE_H__ */ ++#endif /* __RISCV_COVE_PROMOTE_H__ */ diff --git a/arch/riscv/include/asm/kvm_cove.h b/arch/riscv/include/asm/kvm_cove.h index afaea7c621bb..f9ce258dfab8 100644 --- a/arch/riscv/include/asm/kvm_cove.h @@ -199,26 +178,37 @@ index 2a2434136e39..679a6727a143 100644 KVM_RISCV_SBI_EXT_MAX, }; -diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c -index 20b028090cb1..343fe4d51a21 100644 ---- a/arch/riscv/kernel/setup.c -+++ b/arch/riscv/kernel/setup.c -@@ -36,6 +36,7 @@ - #include - #include - #include -+#include - - #include "head.h" - -@@ -267,6 +268,7 @@ static void __init parse_dtb(void) - void __init setup_arch(char **cmdline_p) - { - parse_dtb(); -+ promote_to_cove_guest(); - setup_initial_init_mm(_stext, _etext, _edata, _end); - - *cmdline_p = boot_command_line; +diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S +index 4bf6c449d78b..bc7b9aacea51 100644 +--- a/arch/riscv/kernel/head.S ++++ b/arch/riscv/kernel/head.S +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include "efi-header.S" + + __HEAD +@@ -198,6 +199,18 @@ ENTRY(_start_kernel) + csrw CSR_IE, zero + csrw CSR_IP, zero + ++#ifdef CONFIG_RISCV_COVE_GUEST_PROMOTE ++ mv s1, a0 ++ mv s2, a1 ++ li a7, COVE_PROMOTE_SBI_EXT_ID ++ li a6, COVE_PROMOTE_SBI_FID ++ mv a0, a1 ++ la a1, __cove_tap_start ++ ecall ++ mv a0, s1 ++ mv a1, s2 ++#endif ++ + #ifdef CONFIG_RISCV_M_MODE + /* flush the instruction cache */ + fence.i diff --git a/arch/riscv/kernel/vmlinux-xip.lds.S b/arch/riscv/kernel/vmlinux-xip.lds.S index eab9edc3b631..e8b14457d2be 100644 --- a/arch/riscv/kernel/vmlinux-xip.lds.S @@ -275,7 +265,7 @@ index 31f4dbd97b03..fba7ebd0cd72 100644 -kvm-$(CONFIG_RISCV_COVE_HOST) += cove_sbi.o cove.o vcpu_sbi_covg.o +kvm-$(CONFIG_RISCV_COVE_HOST) += cove_sbi.o cove.o vcpu_sbi_covg.o vcpu_sbi_covh.o diff --git a/arch/riscv/kvm/cove.c b/arch/riscv/kvm/cove.c -index ba596b7f2240..abaaffa07212 100644 +index ba596b7f2240..ed7dc0fbf08b 100644 --- a/arch/riscv/kvm/cove.c +++ b/arch/riscv/kvm/cove.c @@ -150,6 +150,11 @@ __always_inline bool kvm_riscv_cove_enabled(void) @@ -318,64 +308,49 @@ index ba596b7f2240..abaaffa07212 100644 if (!vcpu) return -EINVAL; -@@ -654,36 +659,39 @@ int kvm_riscv_cove_vcpu_init(struct kvm_vcpu *vcpu) +@@ -654,15 +659,20 @@ int kvm_riscv_cove_vcpu_init(struct kvm_vcpu *vcpu) if (!tvcpuc) return -ENOMEM; - vcpus_page = alloc_pages(GFP_KERNEL | __GFP_ZERO, -- get_order_num_pages(tinfo.tvcpu_pages_needed)); -- if (!vcpus_page) { -- rc = -ENOMEM; -- goto alloc_page_failed; -- } ++ tvcpuc->vcpu = vcpu; ++ tvcpuc->vcpu_state.npages = tinfo.tvcpu_pages_needed; ++ vcpu->arch.tc = tvcpuc; ++ ++ if (!kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_MEMORY_ALLOCATION)) { ++ return 0; ++ } ++ ++ vcpus_page = alloc_pages(GFP_KERNEL | __GFP_ZERO, + get_order_num_pages(tinfo.tvcpu_pages_needed)); + if (!vcpus_page) { + rc = -ENOMEM; + goto alloc_page_failed; + } - - tvcpuc->vcpu = vcpu; - tvcpuc->vcpu_state.npages = tinfo.tvcpu_pages_needed; -- tvcpuc->vcpu_state.page = vcpus_page; -- vcpus_phys_addr = page_to_phys(vcpus_page); - -- rc = cove_convert_pages(vcpus_phys_addr, tvcpuc->vcpu_state.npages, true); -- if (rc) -- goto convert_failed; -+ if (kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_MEMORY_ALLOCATION)) { -+ vcpus_page = alloc_pages(GFP_KERNEL | __GFP_ZERO, -+ get_order_num_pages(tinfo.tvcpu_pages_needed)); -+ if (!vcpus_page) { -+ rc = -ENOMEM; -+ goto alloc_page_failed; -+ } -+ tvcpuc->vcpu_state.page = vcpus_page; -+ vcpus_phys_addr = page_to_phys(vcpus_page); +- tvcpuc->vcpu = vcpu; +- tvcpuc->vcpu_state.npages = tinfo.tvcpu_pages_needed; + tvcpuc->vcpu_state.page = vcpus_page; + vcpus_phys_addr = page_to_phys(vcpus_page); -- rc = sbi_covh_create_tvm_vcpu(tvmc->tvm_guest_id, vcpu->vcpu_idx, vcpus_phys_addr); -- if (rc) -- goto vcpu_create_failed; -+ rc = cove_convert_pages(vcpus_phys_addr, tvcpuc->vcpu_state.npages, true); -+ if (rc) -+ goto convert_failed; - -+ rc = sbi_covh_create_tvm_vcpu(tvmc->tvm_guest_id, vcpu->vcpu_idx, vcpus_phys_addr); -+ if (rc) -+ goto vcpu_create_failed; -+ } - vcpu->arch.tc = tvcpuc; +@@ -674,8 +684,6 @@ int kvm_riscv_cove_vcpu_init(struct kvm_vcpu *vcpu) + if (rc) + goto vcpu_create_failed; +- vcpu->arch.tc = tvcpuc; +- return 0; vcpu_create_failed: - /* Reclaim all the pages or return to the confidential page pool */ -- sbi_covh_tsm_reclaim_pages(vcpus_phys_addr, tvcpuc->vcpu_state.npages); -+ if (kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_MEMORY_ALLOCATION)) -+ sbi_covh_tsm_reclaim_pages(vcpus_phys_addr, tvcpuc->vcpu_state.npages); - - convert_failed: -- __free_pages(vcpus_page, get_order_num_pages(tinfo.tvcpu_pages_needed)); -+ if (kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_MEMORY_ALLOCATION)) -+ __free_pages(vcpus_page, get_order_num_pages(tinfo.tvcpu_pages_needed)); +@@ -686,6 +694,7 @@ int kvm_riscv_cove_vcpu_init(struct kvm_vcpu *vcpu) + __free_pages(vcpus_page, get_order_num_pages(tinfo.tvcpu_pages_needed)); alloc_page_failed: ++ vcpu->arch.tc = NULL; kfree(tvcpuc); -@@ -877,7 +885,7 @@ void kvm_riscv_cove_vm_destroy(struct kvm *kvm) + return rc; + } +@@ -877,7 +886,7 @@ void kvm_riscv_cove_vm_destroy(struct kvm *kvm) kvm_err("Memory reclaim failed with rc %d\n", rc); } @@ -384,7 +359,7 @@ index ba596b7f2240..abaaffa07212 100644 { struct kvm_cove_tvm_context *tvmc; struct page *tvms_page, *pgt_page; -@@ -885,6 +893,10 @@ int kvm_riscv_cove_vm_init(struct kvm *kvm) +@@ -885,6 +894,10 @@ int kvm_riscv_cove_vm_init(struct kvm *kvm) unsigned long gstage_pgd_size = kvm_riscv_gstage_pgd_size(); int rc = 0; @@ -395,7 +370,7 @@ index ba596b7f2240..abaaffa07212 100644 tvmc = kzalloc(sizeof(*tvmc), GFP_KERNEL); if (!tvmc) return -ENOMEM; -@@ -980,6 +992,67 @@ int kvm_riscv_cove_vm_init(struct kvm *kvm) +@@ -980,6 +993,65 @@ int kvm_riscv_cove_vm_init(struct kvm *kvm) return rc; } @@ -442,8 +417,6 @@ index ba596b7f2240..abaaffa07212 100644 + goto vcpus_allocated; + + target_vcpu->requests = 0; -+ if (target_vcpu->vcpu_idx != 0) -+ kvm_riscv_vcpu_power_off(target_vcpu); + } + + tvmc->finalized_done = true; diff --git a/security-monitor/rust-crates/cove_tap/src/serializer.rs b/security-monitor/rust-crates/cove_tap/src/serializer.rs index a5b21c35..43297efa 100644 --- a/security-monitor/rust-crates/cove_tap/src/serializer.rs +++ b/security-monitor/rust-crates/cove_tap/src/serializer.rs @@ -28,8 +28,6 @@ impl AttestationPayloadSerializer { 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) } 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 bd8a937b..17d03ce4 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 @@ -128,11 +128,11 @@ impl PromoteToConfidentialVm { let device_tree = unsafe { FlattenedDeviceTree::from_raw_pointer(large_page.address().to_ptr()) }?; let number_of_confidential_harts = device_tree.harts().count(); - let ram_memory = device_tree.memory().and_then(|r| r.into_range())?; + let mut kernel = device_tree.memory().and_then(|r| r.into_range())?; let initrd = device_tree.initrd().ok(); let vm_memory_layout = - ConfidentialVmMemoryLayout::new(ram_memory, (self.fdt_address.usize(), self.fdt_address.usize() + fdt_total_size), initrd); + ConfidentialVmMemoryLayout::new(kernel, (self.fdt_address.usize(), self.fdt_address.usize() + fdt_total_size), initrd); debug!("Virtual machine's memory layout: {:?}", vm_memory_layout); // Clean up, deallocate pages diff --git a/tools/cove_tap_tool/src/measure.rs b/tools/cove_tap_tool/src/measure.rs index 57aa0568..3fec16c7 100644 --- a/tools/cove_tap_tool/src/measure.rs +++ b/tools/cove_tap_tool/src/measure.rs @@ -33,13 +33,13 @@ pub fn measure(kernel_file: String) -> Result<(), Error> { hasher.update(address.to_le_bytes()); hasher.update(&buffer); hasher.finalize_into(&mut digest); - println!("Page 0x{:x} {:100x}", address, digest); + // println!("Page 0x{:x} {:100x}", address, digest); } else { // println!("Page 0x{:x} empty", address); } address += 4096; (0..4096).for_each(|i| buffer[i] = 0); } - println!("Digest {:100x}", digest); + println!("Digest 0x{:100x}", digest); Ok(()) } From f16a216fde9afba562aa5fb14a3ada82b6c8bfa3 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Fri, 29 Nov 2024 09:40:35 +0000 Subject: [PATCH 17/18] renaming cove tap tools Signed-off-by: Wojciech Ozga --- .../linux_vm/configurations/linux64-defconfig | 2 +- security-monitor/Cargo.toml | 10 ++++---- security-monitor/Makefile | 6 ++--- security-monitor/rust-crates/opensbi.sh | 4 ++-- .../{cove_tap => riscv_cove_tap}/Cargo.toml | 8 +++---- .../{cove_tap => riscv_cove_tap}/src/error.rs | 4 +--- .../{cove_tap => riscv_cove_tap}/src/lib.rs | 2 +- .../src/parser.rs | 0 .../src/serializer.rs | 0 .../{cove_tap => riscv_cove_tap}/src/spec.rs | 0 .../src/core/architecture/riscv/mmu/mod.rs | 3 +-- .../core/architecture/riscv/mmu/page_table.rs | 3 +-- .../src/core/control_data/confidential_vm.rs | 2 +- .../confidential_vm_memory_layout.rs | 4 ---- security-monitor/src/core/control_data/mod.rs | 2 ++ .../confidential_vm_memory_protector.rs | 6 +++-- .../src/core/memory_protector/mod.rs | 2 -- security-monitor/src/error.rs | 2 +- .../promote_to_confidential_vm.rs | 24 +++++++++++-------- tools/cove_tap_tool/Cargo.toml | 2 +- tools/cove_tap_tool/src/attach.rs | 4 ++-- tools/cove_tap_tool/src/error.rs | 6 ++--- tools/cove_tap_tool/src/generate.rs | 14 +++++------ tools/cove_tap_tool/src/measure.rs | 2 +- 24 files changed, 55 insertions(+), 57 deletions(-) rename security-monitor/rust-crates/{cove_tap => riscv_cove_tap}/Cargo.toml (77%) rename security-monitor/rust-crates/{cove_tap => riscv_cove_tap}/src/error.rs (99%) rename security-monitor/rust-crates/{cove_tap => riscv_cove_tap}/src/lib.rs (97%) rename security-monitor/rust-crates/{cove_tap => riscv_cove_tap}/src/parser.rs (100%) rename security-monitor/rust-crates/{cove_tap => riscv_cove_tap}/src/serializer.rs (100%) rename security-monitor/rust-crates/{cove_tap => riscv_cove_tap}/src/spec.rs (100%) rename security-monitor/src/core/{memory_protector => control_data}/confidential_vm_memory_layout.rs (84%) diff --git a/confidential-vms/linux_vm/configurations/linux64-defconfig b/confidential-vms/linux_vm/configurations/linux64-defconfig index ccf7a1f0..93653739 100644 --- a/confidential-vms/linux_vm/configurations/linux64-defconfig +++ b/confidential-vms/linux_vm/configurations/linux64-defconfig @@ -411,7 +411,7 @@ CONFIG_HAVE_KVM_VCPU_ASYNC_IOCTL=y CONFIG_KVM_XFER_TO_GUEST_WORK=y CONFIG_KVM_GENERIC_HARDWARE_ENABLING=y CONFIG_VIRTUALIZATION=y -CONFIG_KVM=m +CONFIG_KVM=n CONFIG_ARCH_SUPPORTS_ACPI=y CONFIG_ACPI=y CONFIG_ACPI_GENERIC_GSI=y diff --git a/security-monitor/Cargo.toml b/security-monitor/Cargo.toml index 86f7f137..9bd2f520 100644 --- a/security-monitor/Cargo.toml +++ b/security-monitor/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" # and when promoting a VM to a confidential VM. flattened_device_tree = {path = "rust-crates/flattened_device_tree"} -# Memoffset automatize calculating offsets to internal fields of Rust structures. These offsets are used then by +# Memoffset automatize calculating offsets to internal fields of Rust structures. These offsets are used then by # assembly code in the context switch implementation. memoffset = { version = "0.9", default-features = false, features = ["unstable_const"] } @@ -22,19 +22,19 @@ opensbi-sys = {path = "rust-crates/opensbi-sys"} pointers_utility = {path = "rust-crates/pointers_utility"} # This crate decodes RISC-V instructions unfortunately it does not support the C-instructions yet -riscv-decode = "0.2" +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"]} +# This crates provides functionality to parse TVM attestation payload (TAP). +riscv_cove_tap = {path = "rust-crates/riscv_cove_tap", features = ["parser"]} # Used to measure confidential VM, required for attestation sha2 = { version = "0.10", default-features = false } # provides macros that help removing boilerplate code in the rust error handling -thiserror-no-std = "2.0" +thiserror-no-std = "2.0" [lib] name="ace" diff --git a/security-monitor/Makefile b/security-monitor/Makefile index 5aff0fb2..1acae82c 100644 --- a/security-monitor/Makefile +++ b/security-monitor/Makefile @@ -6,7 +6,7 @@ CARGO = cargo RELEASE = --release CHAIN=riscv64gc-unknown-none-elf TARGET = --target=$(CHAIN) -RUSTFLAGS = -Clink-arg=-Tsrc/platform/qemu/virt.lds -Crelocation-model=pie #-Ccode-model=medium +RUSTFLAGS = -Clink-arg=-Tsrc/platform/qemu/virt.lds -Crelocation-model=pie #-Ccode-model=medium EXEC_NAME = libace.a MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) MAKEFILE_SOURCE_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) @@ -20,9 +20,9 @@ OPENSBI_WORK_DIR=$(SM_WORK_DIR)/opensbi OPENSBI_SYS_WORK_DIR=$(SM_WORK_DIR)/opensbi_bindings OPENSBI_PATCH = $(PLATFORM_SOURCE_DIR)/opensbi_v1.4.patch -PLATFORM_RISCV_ABI ?= lp64d +PLATFORM_RISCV_ABI ?= lp64d PLATFORM_RISCV_ISA ?= rv64gc -PLATFORM_RISCV_XLEN ?= 64 +PLATFORM_RISCV_XLEN ?= 64 CROSS_COMPILE ?= riscv64-unknown-linux-gnu- all: audit opensbi_bindings build diff --git a/security-monitor/rust-crates/opensbi.sh b/security-monitor/rust-crates/opensbi.sh index 85dc6708..9f6eb514 100755 --- a/security-monitor/rust-crates/opensbi.sh +++ b/security-monitor/rust-crates/opensbi.sh @@ -32,7 +32,7 @@ mkdir -p ${WORK_DIR} cd ${OPENSBI_SOURCE_DIR}/ -make CROSS_COMPILE=${CROSS_COMPILE} O=${WORK_DIR} PLATFORM=generic -j$(nproc) -make CROSS_COMPILE=${CROSS_COMPILE} I=${WORK_DIR} PLATFORM=generic install -j$(nproc) +make CROSS_COMPILE=${CROSS_COMPILE} O=${WORK_DIR} PLATFORM=generic -j$(nproc) +make CROSS_COMPILE=${CROSS_COMPILE} I=${WORK_DIR} PLATFORM=generic install -j$(nproc) echo "export PATH=${WORK_DIR}/lib:${WORK_DIR}/bin:\${PATH}" >> ${INSTALL_DIR}/env diff --git a/security-monitor/rust-crates/cove_tap/Cargo.toml b/security-monitor/rust-crates/riscv_cove_tap/Cargo.toml similarity index 77% rename from security-monitor/rust-crates/cove_tap/Cargo.toml rename to security-monitor/rust-crates/riscv_cove_tap/Cargo.toml index f5843899..08154365 100644 --- a/security-monitor/rust-crates/cove_tap/Cargo.toml +++ b/security-monitor/rust-crates/riscv_cove_tap/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "tap" +name = "riscv_cove_tap" version = "0.1.0" authors = ["Wojciech Ozga "] -description = "Library to parse the CoVE's TEE attestation payload" +description = "Library to parse the RISC-V CoVE's TEE attestation payload" edition = "2021" [dependencies] @@ -10,10 +10,10 @@ edition = "2021" #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"]} +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" +thiserror-no-std = "2.0" [features] parser = [] diff --git a/security-monitor/rust-crates/cove_tap/src/error.rs b/security-monitor/rust-crates/riscv_cove_tap/src/error.rs similarity index 99% rename from security-monitor/rust-crates/cove_tap/src/error.rs rename to security-monitor/rust-crates/riscv_cove_tap/src/error.rs index 0928a1ba..4b92ffd0 100644 --- a/security-monitor/rust-crates/cove_tap/src/error.rs +++ b/security-monitor/rust-crates/riscv_cove_tap/src/error.rs @@ -17,8 +17,6 @@ pub enum TapError { 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/riscv_cove_tap/src/lib.rs similarity index 97% rename from security-monitor/rust-crates/cove_tap/src/lib.rs rename to security-monitor/rust-crates/riscv_cove_tap/src/lib.rs index f148f209..9d22d3e4 100644 --- a/security-monitor/rust-crates/cove_tap/src/lib.rs +++ b/security-monitor/rust-crates/riscv_cove_tap/src/lib.rs @@ -19,4 +19,4 @@ pub use serializer::AttestationPayloadSerializer; pub use spec::*; pub use error::*; -pub mod spec; \ No newline at end of file +pub mod spec; diff --git a/security-monitor/rust-crates/cove_tap/src/parser.rs b/security-monitor/rust-crates/riscv_cove_tap/src/parser.rs similarity index 100% rename from security-monitor/rust-crates/cove_tap/src/parser.rs rename to security-monitor/rust-crates/riscv_cove_tap/src/parser.rs diff --git a/security-monitor/rust-crates/cove_tap/src/serializer.rs b/security-monitor/rust-crates/riscv_cove_tap/src/serializer.rs similarity index 100% rename from security-monitor/rust-crates/cove_tap/src/serializer.rs rename to security-monitor/rust-crates/riscv_cove_tap/src/serializer.rs diff --git a/security-monitor/rust-crates/cove_tap/src/spec.rs b/security-monitor/rust-crates/riscv_cove_tap/src/spec.rs similarity index 100% rename from security-monitor/rust-crates/cove_tap/src/spec.rs rename to security-monitor/rust-crates/riscv_cove_tap/src/spec.rs diff --git a/security-monitor/src/core/architecture/riscv/mmu/mod.rs b/security-monitor/src/core/architecture/riscv/mmu/mod.rs index 3fc53400..a47f4877 100644 --- a/security-monitor/src/core/architecture/riscv/mmu/mod.rs +++ b/security-monitor/src/core/architecture/riscv/mmu/mod.rs @@ -5,8 +5,7 @@ use crate::core::architecture::CSR; use crate::core::memory_layout::NonConfidentialMemoryAddress; use crate::error::Error; -use crate::core::control_data::MeasurementDigest; -use crate::core::memory_protector::ConfidentialVmMemoryLayout; +use crate::core::control_data::{ConfidentialVmMemoryLayout, MeasurementDigest}; pub use hgatp::{Hgatp, HgatpMode}; pub use page_size::PageSize; pub use page_table::PageTable; diff --git a/security-monitor/src/core/architecture/riscv/mmu/page_table.rs b/security-monitor/src/core/architecture/riscv/mmu/page_table.rs index 654edc81..e3c156bc 100644 --- a/security-monitor/src/core/architecture/riscv/mmu/page_table.rs +++ b/security-monitor/src/core/architecture/riscv/mmu/page_table.rs @@ -7,9 +7,8 @@ use crate::core::architecture::mmu::page_table_level::PageTableLevel; use crate::core::architecture::mmu::paging_system::PagingSystem; use crate::core::architecture::mmu::HgatpMode; use crate::core::architecture::{PageSize, SharedPage}; -use crate::core::control_data::{MeasurementDigest, StaticMeasurements}; +use crate::core::control_data::{ConfidentialVmMemoryLayout, MeasurementDigest, StaticMeasurements}; use crate::core::memory_layout::{ConfidentialMemoryAddress, ConfidentialVmPhysicalAddress, NonConfidentialMemoryAddress}; -use crate::core::memory_protector::ConfidentialVmMemoryLayout; use crate::core::page_allocator::{Allocated, Page, PageAllocator}; use crate::error::Error; use alloc::boxed::Box; diff --git a/security-monitor/src/core/control_data/confidential_vm.rs b/security-monitor/src/core/control_data/confidential_vm.rs index 75e69524..113e7fd5 100644 --- a/security-monitor/src/core/control_data/confidential_vm.rs +++ b/security-monitor/src/core/control_data/confidential_vm.rs @@ -10,8 +10,8 @@ use crate::core::memory_protector::ConfidentialVmMemoryProtector; use crate::error::Error; use alloc::collections::BTreeMap; use alloc::vec::Vec; +use riscv_cove_tap::Secret; use spin::{Mutex, MutexGuard}; -use tap::Secret; pub struct ConfidentialVm { id: ConfidentialVmId, diff --git a/security-monitor/src/core/memory_protector/confidential_vm_memory_layout.rs b/security-monitor/src/core/control_data/confidential_vm_memory_layout.rs similarity index 84% rename from security-monitor/src/core/memory_protector/confidential_vm_memory_layout.rs rename to security-monitor/src/core/control_data/confidential_vm_memory_layout.rs index 3d44403e..9aca4770 100644 --- a/security-monitor/src/core/memory_protector/confidential_vm_memory_layout.rs +++ b/security-monitor/src/core/control_data/confidential_vm_memory_layout.rs @@ -25,8 +25,4 @@ impl ConfidentialVmMemoryLayout { pub fn is_initrd(&self, address: usize) -> bool { self.initrd.and_then(|o| Some(o.0 <= address && address < o.1)).unwrap_or(false) } - - pub fn is_in_vm_address_space(&self, address: usize) -> bool { - self.is_kernel(address) || self.is_fdt(address) || self.is_initrd(address) - } } diff --git a/security-monitor/src/core/control_data/mod.rs b/security-monitor/src/core/control_data/mod.rs index f9a60fe5..432ba113 100644 --- a/security-monitor/src/core/control_data/mod.rs +++ b/security-monitor/src/core/control_data/mod.rs @@ -6,6 +6,7 @@ pub use confidential_hart_remote_command::{ConfidentialHartRemoteCommand, Confid pub use confidential_vm::ConfidentialVm; pub use confidential_vm_id::ConfidentialVmId; pub use confidential_vm_measurement::{DigestType, MeasurementDigest, StaticMeasurements}; +pub use confidential_vm_memory_layout::ConfidentialVmMemoryLayout; pub use confidential_vm_mmio_region::ConfidentialVmMmioRegion; pub use hardware_hart::{HardwareHart, HART_STACK_ADDRESS_OFFSET}; pub use hypervisor_hart::HypervisorHart; @@ -17,6 +18,7 @@ mod confidential_hart_remote_command; mod confidential_vm; mod confidential_vm_id; mod confidential_vm_measurement; +mod confidential_vm_memory_layout; mod confidential_vm_mmio_region; mod hardware_hart; mod hypervisor_hart; diff --git a/security-monitor/src/core/memory_protector/confidential_vm_memory_protector.rs b/security-monitor/src/core/memory_protector/confidential_vm_memory_protector.rs index 140cc52e..0968288a 100644 --- a/security-monitor/src/core/memory_protector/confidential_vm_memory_protector.rs +++ b/security-monitor/src/core/memory_protector/confidential_vm_memory_protector.rs @@ -4,9 +4,8 @@ use crate::core::architecture::mmu::{Hgatp, PageTable}; use crate::core::architecture::riscv::{mmu, pmp, tlb}; use crate::core::architecture::{PageSize, SharedPage}; -use crate::core::control_data::{ConfidentialVmId, MeasurementDigest, StaticMeasurements}; +use crate::core::control_data::{ConfidentialVmId, ConfidentialVmMemoryLayout, StaticMeasurements}; use crate::core::memory_layout::{ConfidentialMemoryAddress, ConfidentialVmPhysicalAddress, NonConfidentialMemoryAddress}; -use crate::core::memory_protector::ConfidentialVmMemoryLayout; use crate::error::Error; /// Exposes an interface to configure the hardware memory isolation component in a way that @@ -68,6 +67,9 @@ impl ConfidentialVmMemoryProtector { self.root_page_table.translate(address) } + /// Cryptographically measures the confidential VM's memory. This function should be called during the confidential VM creation + /// procedure when the confidential VM's memory image creation has completed. These measurements will be used during attestation to + /// verify confidential VM's integrity and authenticity. pub fn finalize(&mut self, measurements: &mut StaticMeasurements, vm_memory_layout: &ConfidentialVmMemoryLayout) -> Result<(), Error> { Ok(self.root_page_table.finalize(measurements, vm_memory_layout, 0)?) } diff --git a/security-monitor/src/core/memory_protector/mod.rs b/security-monitor/src/core/memory_protector/mod.rs index 595e7ba3..711dac9f 100644 --- a/security-monitor/src/core/memory_protector/mod.rs +++ b/security-monitor/src/core/memory_protector/mod.rs @@ -1,10 +1,8 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -pub use confidential_vm_memory_layout::ConfidentialVmMemoryLayout; pub use confidential_vm_memory_protector::ConfidentialVmMemoryProtector; pub use hypervisor_memory_protector::HypervisorMemoryProtector; -mod confidential_vm_memory_layout; mod confidential_vm_memory_protector; mod hypervisor_memory_protector; diff --git a/security-monitor/src/error.rs b/security-monitor/src/error.rs index 5e937194..ead5c078 100644 --- a/security-monitor/src/error.rs +++ b/security-monitor/src/error.rs @@ -46,7 +46,7 @@ pub enum Error { #[error("Authentication payload size is invalid.")] AuthBlobInvalidSize(), #[error("Error when parsing TEE attestation payload: {0}")] - AttestationPayloadParsingError(#[from] tap::TapError), + AttestationPayloadParsingError(#[from] riscv_cove_tap::TapError), #[error("Local attestation failed. Invalid measurements")] LocalAttestationFailed(), #[error("Local attestation failed. Not supported digest type")] 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 17d03ce4..21bccf66 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 @@ -4,17 +4,17 @@ use crate::core::architecture::riscv::sbi::NaclSharedMemory; use crate::core::architecture::{GeneralPurposeRegister, Hgatp, PageSize}; use crate::core::control_data::{ - ConfidentialHart, ConfidentialVm, ConfidentialVmId, ControlDataStorage, HypervisorHart, StaticMeasurements, + ConfidentialHart, ConfidentialVm, ConfidentialVmId, ConfidentialVmMemoryLayout, ControlDataStorage, HypervisorHart, StaticMeasurements, }; use crate::core::memory_layout::ConfidentialVmPhysicalAddress; -use crate::core::memory_protector::{ConfidentialVmMemoryLayout, ConfidentialVmMemoryProtector}; +use crate::core::memory_protector::ConfidentialVmMemoryProtector; use crate::core::page_allocator::{Allocated, Page, PageAllocator}; use crate::error::Error; use crate::non_confidential_flow::handlers::supervisor_binary_interface::SbiResponse; use crate::non_confidential_flow::{ApplyToHypervisorHart, NonConfidentialFlow}; use alloc::vec::Vec; use flattened_device_tree::FlattenedDeviceTree; -use tap::{AttestationPayload, AttestationPayloadParser, Secret}; +use riscv_cove_tap::{AttestationPayload, AttestationPayloadParser, Secret}; /// Creates a confidential VM in a single-step. This handler implements the Promote to TVM call defined by the COVH ABI in the CoVE /// specification. With this call, the hypervisor presents a state of a virtual machine, requesting the security monitor to promote it to a @@ -83,10 +83,14 @@ impl PromoteToConfidentialVm { }) .collect(); - let attestation_payload = self.read_attestation_payload(&memory_protector).unwrap_or(None); + let attestation_payload = + self.read_attestation_payload(&memory_protector).inspect_err(|e| debug!("TAP reading failed: {:?}", e)).unwrap_or(None); let measurements = self.measure(&mut memory_protector, &vm_memory_layout, &confidential_harts)?; - let secrets = self.authenticate_and_authorize_vm(attestation_payload, &measurements).unwrap_or(alloc::vec![]); + let secrets = self + .authenticate_and_authorize_vm(attestation_payload, &measurements) + .inspect_err(|e| debug!("Local attestation failed: {:?}", e)) + .unwrap_or(alloc::vec![]); ControlDataStorage::try_write(|control_data| { // We have a write lock on the entire control data! Spend here as little time as possible because we are @@ -118,7 +122,7 @@ impl PromoteToConfidentialVm { let fdt_total_size = unsafe { FlattenedDeviceTree::total_size(address_in_confidential_memory.to_ptr())? }; ensure!(fdt_total_size >= FlattenedDeviceTree::FDT_HEADER_SIZE, Error::FdtInvalidSize())?; - // To work with FDT, we must have it as a continous chunk of memory. We accept only FDTs that fit within 2MiB + // To work with FDT, we must have it as a continous chunk of memory. We accept only FDTs that fit within 2 MiB ensure!(fdt_total_size.div_ceil(PageSize::Size2MiB.in_bytes()) == 1, Error::FdtInvalidSize())?; let large_page = Self::relocate(memory_protector, &self.fdt_address, fdt_total_size, false)?; @@ -128,7 +132,7 @@ impl PromoteToConfidentialVm { let device_tree = unsafe { FlattenedDeviceTree::from_raw_pointer(large_page.address().to_ptr()) }?; let number_of_confidential_harts = device_tree.harts().count(); - let mut kernel = device_tree.memory().and_then(|r| r.into_range())?; + let kernel = device_tree.memory().and_then(|r| r.into_range())?; let initrd = device_tree.initrd().ok(); let vm_memory_layout = @@ -157,7 +161,7 @@ impl PromoteToConfidentialVm { // not-yet-created confidential VM's and (2) there is only one physical hart executing this code. let header: u64 = unsafe { address_in_confidential_memory.read_volatile().try_into().map_err(|_| Error::AuthBlobNotAlignedTo32Bits())? }; - let total_size = tap::ACE_HEADER_SIZE + ((header >> 32) & 0xFFFF) as usize; + let total_size = riscv_cove_tap::ACE_HEADER_SIZE + ((header >> 32) & 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!(total_size.div_ceil(PageSize::Size2MiB.in_bytes()) == 1, Error::AuthBlobInvalidSize())?; @@ -181,10 +185,10 @@ impl PromoteToConfidentialVm { use crate::core::control_data::MeasurementDigest; match attestation_payload { Some(attestation_payload) => { - ensure!(attestation_payload.digests.len() > 1, Error::LocalAttestationFailed())?; + ensure!(attestation_payload.digests.len() > 0, Error::LocalAttestationFailed())?; for digest in attestation_payload.digests.iter() { debug!("Reference PCR{:?}={:?}=0x{}", digest.pcr_id, digest.algorithm, digest.value_in_hex()); - ensure!(digest.algorithm == tap::DigestAlgorithm::Sha512, Error::LocalAttestationNotSupportedDigest())?; + ensure!(digest.algorithm == riscv_cove_tap::DigestAlgorithm::Sha512, Error::LocalAttestationNotSupportedDigest())?; let pcr_value = MeasurementDigest::clone_from_slice(&digest.value); ensure!(measurements.compare(digest.pcr_id() as usize, pcr_value)?, Error::LocalAttestationFailed())?; } diff --git a/tools/cove_tap_tool/Cargo.toml b/tools/cove_tap_tool/Cargo.toml index 17086406..07a76e12 100644 --- a/tools/cove_tap_tool/Cargo.toml +++ b/tools/cove_tap_tool/Cargo.toml @@ -15,6 +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=["parser", "serializer"]} +riscv_cove_tap = {path="../../security-monitor/rust-crates/riscv_cove_tap", features=["parser", "serializer"]} thiserror = "1.0" # provides macros that help removing boilerplate code in the rust error handling diff --git a/tools/cove_tap_tool/src/attach.rs b/tools/cove_tap_tool/src/attach.rs index ecada79c..85e0078b 100644 --- a/tools/cove_tap_tool/src/attach.rs +++ b/tools/cove_tap_tool/src/attach.rs @@ -26,7 +26,7 @@ pub fn attach_tap( // clear the placeholder let mut output_file = OpenOptions::new().write(true).open(output_file_name)?; output_file.seek(SeekFrom::Start(offset))?; - (tap::ACE_HEADER_SIZE..tap::ACE_MAX_TAP_SIZE).try_for_each(|_| output_file.write_u8(0u8))?; + (riscv_cove_tap::ACE_HEADER_SIZE..riscv_cove_tap::ACE_MAX_TAP_SIZE).try_for_each(|_| output_file.write_u8(0u8))?; // write expected TAP from the beginning of the offset output_file.seek(SeekFrom::Start(offset))?; let mut tap_file = OpenOptions::new().read(true).open(tap_file_name)?; @@ -44,7 +44,7 @@ fn find_placehoder(output_file_name: &str) -> Result { if bytes_read == 0 { break; } - if u32::from_le_bytes(buffer) == tap::ACE_MAGIC_TAP_START { + if u32::from_le_bytes(buffer) == riscv_cove_tap::ACE_MAGIC_TAP_START { println!("Found TAP placeholder at offset: {:x}", offset); return Ok(offset); } diff --git a/tools/cove_tap_tool/src/error.rs b/tools/cove_tap_tool/src/error.rs index 6dee00fe..35bb72f9 100644 --- a/tools/cove_tap_tool/src/error.rs +++ b/tools/cove_tap_tool/src/error.rs @@ -13,7 +13,7 @@ pub enum Error { #[error("Invalid parameter")] InvalidParameter(String), #[error("Tap Error")] - TapSerializerError(tap::TapError), + TapSerializerError(riscv_cove_tap::TapError), #[error("Int casting Error")] TryFromIntErr(#[from] std::num::TryFromIntError), #[error("Cannot open file {0}")] @@ -24,8 +24,8 @@ pub enum Error { PlaceholderError(), } -impl From for Error { - fn from(err: tap::TapError) -> Self { +impl From for Error { + fn from(err: riscv_cove_tap::TapError) -> Self { Error::TapSerializerError(err) } } diff --git a/tools/cove_tap_tool/src/generate.rs b/tools/cove_tap_tool/src/generate.rs index b6829e3b..c8d3fd7f 100644 --- a/tools/cove_tap_tool/src/generate.rs +++ b/tools/cove_tap_tool/src/generate.rs @@ -5,13 +5,13 @@ use crate::ensure; use crate::error::Error; use std::fs::OpenOptions; use std::io::Write; -use tap::AttestationPayload; -use tap::AttestationPayloadSerializer; -use tap::Digest; -use tap::DigestAlgorithm; -use tap::Lockbox; -use tap::LockboxAlgorithm; -use tap::Secret; +use riscv_cove_tap::AttestationPayload; +use riscv_cove_tap::AttestationPayloadSerializer; +use riscv_cove_tap::Digest; +use riscv_cove_tap::DigestAlgorithm; +use riscv_cove_tap::Lockbox; +use riscv_cove_tap::LockboxAlgorithm; +use riscv_cove_tap::Secret; pub fn generate_tap( pcrs: Vec<(u16, Vec)>, diff --git a/tools/cove_tap_tool/src/measure.rs b/tools/cove_tap_tool/src/measure.rs index 3fec16c7..c3470c26 100644 --- a/tools/cove_tap_tool/src/measure.rs +++ b/tools/cove_tap_tool/src/measure.rs @@ -24,7 +24,7 @@ pub fn measure(kernel_file: String) -> Result<(), Error> { break; } let header = [buffer[0], buffer[1], buffer[2], buffer[3]]; - if u32::from_le_bytes(header) == tap::ACE_MAGIC_TAP_START { + if u32::from_le_bytes(header) == riscv_cove_tap::ACE_MAGIC_TAP_START { (0..4096).for_each(|i| buffer[i] = 0); // security monitor will clear it } if buffer.iter().find(|b| **b != 0).is_some() { From d36c84c4d00020719dff6cdde0e630d0bcfb2757 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Fri, 29 Nov 2024 09:57:56 -0600 Subject: [PATCH 18/18] simplifying the buildroot configuration Signed-off-by: Wojciech Ozga --- .../qemu_riscv64_virt_defconfig | 52 ++++++++----------- .../qemu_riscv64_virt_defconfig | 29 ++++------- 2 files changed, 30 insertions(+), 51 deletions(-) diff --git a/confidential-vms/linux_vm/configurations/qemu_riscv64_virt_defconfig b/confidential-vms/linux_vm/configurations/qemu_riscv64_virt_defconfig index 2eb3eef3..f5606e5e 100644 --- a/confidential-vms/linux_vm/configurations/qemu_riscv64_virt_defconfig +++ b/confidential-vms/linux_vm/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 @@ -28,7 +28,7 @@ BR2_TARGET_GENERIC_GETTY=y BR2_TARGET_GENERIC_GETTY_PORT="ttyS0" BR2_TARGET_GENERIC_ROOT_PASSWD="passwd" -# Filesystem +# Filesystem BR2_ROOTFS_OVERLAY="" BR2_TARGET_ROOTFS_EXT2=y BR2_TARGET_ROOTFS_EXT2_2=n @@ -61,29 +61,19 @@ BR2_PACKAGE_DROPBEAR=y BR2_TARGET_OPENSBI=y BR2_TARGET_OPENSBI_PLAT="generic" -### -# BR2_TOOLCHAIN_BUILDROOT_GLIBC=y -# BR2_TOOLCHAIN_BUILDROOT_CXX=y -# BR2_PACKAGE_HOST_GDB=y -# BR2_PACKAGE_HOST_GDB_TUI=y -# BR2_PACKAGE_HOST_GDB_PYTHON3=y -# BR2_CCACHE=y -# BR2_CCACHE_INITIAL_SETUP="-M0 -F0" -# BR2_SSP_NONE=y - BR2_PER_PACKAGE_DIRECTORIES=y BR2_VERBOSE=0 BR2_PACKAGE_NVME=y # Initramfs -BR2_TARGET_ROOTFS_INITRAMFS=y -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 +# BR2_TARGET_ROOTFS_INITRAMFS=y +# 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 diff --git a/hypervisor/configurations/qemu_riscv64_virt_defconfig b/hypervisor/configurations/qemu_riscv64_virt_defconfig index faad6e8a..f126e806 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 @@ -73,17 +73,6 @@ BR2_PACKAGE_QEMU_BLOBS=y # below not needed? BR2_TARGET_OPENSBI=y BR2_TARGET_OPENSBI_PLAT="generic" -#BR2_PACKAGE_DEVMEM2=y - -### -# BR2_TOOLCHAIN_BUILDROOT_GLIBC=y -# BR2_TOOLCHAIN_BUILDROOT_CXX=y -# BR2_PACKAGE_HOST_GDB=y -# BR2_PACKAGE_HOST_GDB_TUI=y -# BR2_PACKAGE_HOST_GDB_PYTHON3=y -# BR2_CCACHE=y -# BR2_CCACHE_INITIAL_SETUP="-M0 -F0" -# BR2_SSP_NONE=y BR2_PER_PACKAGE_DIRECTORIES=y BR2_VERBOSE=0