From 6a8fdcfaada3b4e68c15f80e5e41827515338fcc Mon Sep 17 00:00:00 2001 From: jul-sh Date: Fri, 16 Feb 2024 09:45:59 -0500 Subject: [PATCH 1/3] Generate empty attestation config (#4808) * Test that oak_attestation_verification verifies mock evidence created by the restricted kernel SDK * add missing dep * verify evidence with endorsements and references * add assert to ensure that it correctly extract mock restricted kernel evidence as OakRestrictedKernel --- Cargo.lock | 2 + oak_attestation_verification/Cargo.toml | 4 + .../tests/verifier_tests.rs | 94 +++++++++++++++++-- oak_restricted_kernel_dice/src/lib.rs | 35 +++++-- 4 files changed, 118 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1dfa894067..36b442d99a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2210,8 +2210,10 @@ dependencies = [ "ecdsa", "getrandom", "hex", + "oak_attestation", "oak_dice", "oak_proto_rust", + "oak_restricted_kernel_sdk", "oak_sev_snp_attestation_report", "p256", "p384", diff --git a/oak_attestation_verification/Cargo.toml b/oak_attestation_verification/Cargo.toml index e0a2a1196bf..189af4ada3c 100644 --- a/oak_attestation_verification/Cargo.toml +++ b/oak_attestation_verification/Cargo.toml @@ -45,3 +45,7 @@ zerocopy = "*" [dev-dependencies] prost = { workspace = true } +oak_restricted_kernel_sdk = { workspace = true, features = [ + "mock_attestation" +] } +oak_attestation = { workspace = true } diff --git a/oak_attestation_verification/tests/verifier_tests.rs b/oak_attestation_verification/tests/verifier_tests.rs index e3a19608dbd..4df04ad2919 100644 --- a/oak_attestation_verification/tests/verifier_tests.rs +++ b/oak_attestation_verification/tests/verifier_tests.rs @@ -16,19 +16,22 @@ use std::fs; +use oak_attestation::dice::evidence_to_proto; use oak_attestation_verification::{ util::convert_pem_to_raw, - verifier::{to_attestation_results, verify}, + verifier::{to_attestation_results, verify, verify_dice_chain}, }; use oak_proto_rust::oak::attestation::v1::{ - attestation_results::Status, binary_reference_value, reference_values, AmdSevReferenceValues, - BinaryReferenceValue, ContainerLayerEndorsements, ContainerLayerReferenceValues, - EndorsementReferenceValue, Endorsements, Evidence, InsecureReferenceValues, - KernelLayerEndorsements, KernelLayerReferenceValues, OakContainersEndorsements, - OakContainersReferenceValues, ReferenceValues, RootLayerEndorsements, RootLayerReferenceValues, - SkipVerification, StringReferenceValue, SystemLayerEndorsements, SystemLayerReferenceValues, - TransparentReleaseEndorsement, + attestation_results::Status, binary_reference_value, endorsements, reference_values, + AmdSevReferenceValues, ApplicationLayerReferenceValues, BinaryReferenceValue, + ContainerLayerEndorsements, ContainerLayerReferenceValues, EndorsementReferenceValue, + Endorsements, Evidence, InsecureReferenceValues, KernelLayerEndorsements, + KernelLayerReferenceValues, OakContainersEndorsements, OakContainersReferenceValues, + OakRestrictedKernelEndorsements, OakRestrictedKernelReferenceValues, ReferenceValues, + RootLayerEndorsements, RootLayerReferenceValues, SkipVerification, StringReferenceValue, + SystemLayerEndorsements, SystemLayerReferenceValues, TransparentReleaseEndorsement, }; +use oak_restricted_kernel_sdk::EvidenceProvider; use prost::Message; const ENDORSEMENT_PATH: &str = "testdata/endorsement.json"; @@ -181,6 +184,81 @@ fn verify_succeeds() { assert!(p.status() == Status::Success); } +#[test] +fn verify_mock_dice_chain() { + let mock_evidence_provider = + oak_restricted_kernel_sdk::mock_attestation::MockEvidenceProvider::create() + .expect("failed to create mock provider"); + let mock_evidence = mock_evidence_provider.get_evidence(); + + let result = verify_dice_chain( + &evidence_to_proto(mock_evidence.clone()).expect("could not convert evidence to proto"), + ); + + assert!(result.is_ok()); + let evidence_values: oak_proto_rust::oak::attestation::v1::extracted_evidence::EvidenceValues = + result.unwrap().evidence_values.unwrap(); + assert!(matches!(evidence_values, oak_proto_rust::oak::attestation::v1::extracted_evidence::EvidenceValues::OakRestrictedKernel{..})) +} + +#[test] +fn verify_mock_evidence() { + let mock_evidence_provider = + oak_restricted_kernel_sdk::mock_attestation::MockEvidenceProvider::create() + .expect("failed to create mock provider"); + let evidence = evidence_to_proto(mock_evidence_provider.get_evidence().clone()) + .expect("failed to convert evidence to proto"); + + let endorsements = Endorsements { + r#type: Some(endorsements::Type::OakRestrictedKernel( + OakRestrictedKernelEndorsements { + root_layer: Some(RootLayerEndorsements::default()), + ..Default::default() + }, + )), + }; + + // reference values that skip everything. + let reference_values = { + let skip = BinaryReferenceValue { + r#type: Some(binary_reference_value::Type::Skip( + SkipVerification::default(), + )), + }; + ReferenceValues { + r#type: Some(reference_values::Type::OakRestrictedKernel( + OakRestrictedKernelReferenceValues { + root_layer: Some(RootLayerReferenceValues { + insecure: Some(InsecureReferenceValues::default()), + ..Default::default() + }), + kernel_layer: Some(KernelLayerReferenceValues { + kernel_image: Some(skip.clone()), + kernel_cmd_line: Some(skip.clone()), + kernel_setup_data: Some(skip.clone()), + init_ram_fs: Some(skip.clone()), + memory_map: Some(skip.clone()), + acpi: Some(skip.clone()), + }), + application_layer: Some(ApplicationLayerReferenceValues { + binary: Some(skip.clone()), + configuration: Some(skip.clone()), + }), + }, + )), + } + }; + + let r = verify(NOW_UTC_MILLIS, &evidence, &endorsements, &reference_values); + let p = to_attestation_results(&r); + + eprintln!("======================================"); + eprintln!("code={} reason={}", p.status as i32, p.reason); + eprintln!("======================================"); + assert!(r.is_ok()); + assert!(p.status() == Status::Success); +} + #[test] fn verify_fake_evidence() { let evidence = create_fake_evidence(); diff --git a/oak_restricted_kernel_dice/src/lib.rs b/oak_restricted_kernel_dice/src/lib.rs index 13156eb14ac..417ffc41112 100644 --- a/oak_restricted_kernel_dice/src/lib.rs +++ b/oak_restricted_kernel_dice/src/lib.rs @@ -23,10 +23,15 @@ extern crate alloc; +use alloc::vec; + use coset::{cbor::Value, cwt::ClaimName, CborSerializable}; use hkdf::Hkdf; use oak_crypto::encryption_key::generate_encryption_key_pair; -use oak_dice::cert::{ENCLAVE_APPLICATION_LAYER_ID, LAYER_2_CODE_MEASUREMENT_ID, SHA2_256_ID}; +use oak_dice::cert::{ + ENCLAVE_APPLICATION_LAYER_ID, FINAL_LAYER_CONFIG_MEASUREMENT_ID, LAYER_2_CODE_MEASUREMENT_ID, + SHA2_256_ID, +}; use sha2::{Digest, Sha256}; /// A derived sealing key. @@ -85,15 +90,27 @@ pub fn generate_dice_data( let (application_private_signing_key, application_public_verifying_key) = oak_dice::cert::generate_ecdsa_key_pair(); - let additional_claims = alloc::vec![( + let additional_claims = vec![( ClaimName::PrivateUse(ENCLAVE_APPLICATION_LAYER_ID), - Value::Map(alloc::vec![( - Value::Integer(LAYER_2_CODE_MEASUREMENT_ID.into()), - Value::Map(alloc::vec![( - Value::Integer(SHA2_256_ID.into()), - Value::Bytes(app_digest.into()), - )]), - ),]), + Value::Map(vec![ + ( + Value::Integer(LAYER_2_CODE_MEASUREMENT_ID.into()), + Value::Map(vec![( + Value::Integer(SHA2_256_ID.into()), + Value::Bytes(app_digest.into()), + )]), + ), + ( + Value::Integer(FINAL_LAYER_CONFIG_MEASUREMENT_ID.into()), + Value::Map(vec![( + Value::Integer(SHA2_256_ID.into()), + // There currently exists no application config for enclave + // applications. Hence this field should + // always be empty. + Value::Bytes([0; 0].into()), + )]), + ), + ]), )]; let application_signing_public_key_certificate = From bf5b0cee977b612e929462cd55c9fd7d477c5b57 Mon Sep 17 00:00:00 2001 From: jul-sh Date: Fri, 16 Feb 2024 10:46:36 -0500 Subject: [PATCH 2/3] Populate all kernel space entries of L4 page table (#4809) * Populate all kernel space entries of L4 page table This means all entries will point to a valid, pre-existing L3 page table. This allows us in the future swap between L4 page tables for different applications, since they will all point to the same memory / L3 tables in kernel space. Means we no longer have to iteratate over kernel space entries when switching applications. Will be useful for concurrent apps. * add type annotations to clarify frame size --- oak_restricted_kernel/src/lib.rs | 25 ++++++++++++++++--- oak_restricted_kernel/src/mm/mod.rs | 21 ++++++++++++++-- .../src/syscall/switch_process.rs | 13 ++++------ 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/oak_restricted_kernel/src/lib.rs b/oak_restricted_kernel/src/lib.rs index 1a79c243d0b..3e92d5dbc51 100644 --- a/oak_restricted_kernel/src/lib.rs +++ b/oak_restricted_kernel/src/lib.rs @@ -69,8 +69,8 @@ use core::{marker::Sync, option::Option, panic::PanicInfo, str::FromStr}; use linked_list_allocator::LockedHeap; use log::{error, info}; use mm::{ - frame_allocator::PhysicalMemoryAllocator, page_tables::CurrentRootPageTable, - virtual_address_allocator::VirtualAddressAllocator, + encrypted_mapper::MemoryEncryption, frame_allocator::PhysicalMemoryAllocator, + page_tables::CurrentRootPageTable, virtual_address_allocator::VirtualAddressAllocator, }; use oak_channel::Channel; use oak_core::sync::OnceCell; @@ -81,7 +81,7 @@ use strum::{EnumIter, EnumString, IntoEnumIterator}; #[cfg(not(feature = "initrd"))] use x86_64::structures::paging::{PageSize, Size4KiB}; use x86_64::{ - structures::paging::{Page, Size2MiB}, + structures::paging::{Page, PageTable, Size2MiB}, PhysAddr, VirtAddr, }; use zerocopy::FromBytes; @@ -105,6 +105,11 @@ pub static GUEST_HOST_HEAP: OnceCell = OnceCell::new(); pub static PAGE_TABLES: Spinlock = Spinlock::new(CurrentRootPageTable::empty()); +/// Level 4 page table that is free in application space, but has all entries for kernel space +/// populated. It can be used to create free page tables for applications, that maintain correct +/// mapping in kernel space. +pub static BASE_L4_PAGE_TABLE: OnceCell<(PageTable, MemoryEncryption)> = OnceCell::new(); + /// Allocator for long-lived pages in the kernel. pub static VMA_ALLOCATOR: Spinlock> = Spinlock::new(VirtualAddressAllocator::new(Page::range( @@ -187,6 +192,20 @@ pub fn start_kernel(info: &BootParams) -> ! { prev_page_table.is_none(), "there should be no previous page table during initialization" ); + + // Safety: We just created a page table at this location. + let pml4: &PageTable = unsafe { + &*(PAGE_TABLES + .lock() + .get() + .unwrap() + .translate_physical(PhysAddr::new(pml4_frame.start_address().as_u64())) + .expect("page table must map to virtual address")) + .as_ptr() + }; + BASE_L4_PAGE_TABLE + .set((pml4.clone(), encrypted)) + .expect("base pml4 not unset"); }; // Re-map boot params to the new virtual address. diff --git a/oak_restricted_kernel/src/mm/mod.rs b/oak_restricted_kernel/src/mm/mod.rs index e0e335adb70..0fd4904c8b7 100644 --- a/oak_restricted_kernel/src/mm/mod.rs +++ b/oak_restricted_kernel/src/mm/mod.rs @@ -27,7 +27,7 @@ use x86_64::{ structures::paging::{ mapper::{FlagUpdateError, MapToError, MapperFlush, UnmapError}, FrameAllocator, Page, PageSize, PageTable, PageTableFlags as BasePageTableFlags, PhysFrame, - Size2MiB, + Size2MiB, Size4KiB, }, PhysAddr, VirtAddr, }; @@ -290,7 +290,7 @@ pub fn initial_pml4( // Safety: this expects the frame allocator to be initialized and the memory region it's handing // memory out of to be identity mapped. This is true for the lower 2 GiB after we boot. // This reference will no longer be valid after we reload the page tables! - let pml4_frame = FRAME_ALLOCATOR + let pml4_frame: PhysFrame = FRAME_ALLOCATOR .lock() .allocate_frame() .ok_or("couldn't allocate a frame for PML4")?; @@ -307,6 +307,23 @@ pub fn initial_pml4( MemoryEncryption::NoEncryption }; + // Populate all the kernel space entries of the level 4 page table. This means that these + // entries should never change, memory allocation will happen on lower level page tables. This + // is done to permit the kernel to switch between different level 4 page tables in the future. + // All page tables that include these mappings will point to the same lower level page tables / + // memory in kernel space. + { + let mut fa = FRAME_ALLOCATOR.lock(); + for entry in pml4.iter_mut().skip(256) { + let pml3_frame: PhysFrame = fa + .allocate_frame() + .ok_or("couldn't allocate a frame for PML3")?; + let pml3 = unsafe { &mut *(pml3_frame.start_address().as_u64() as *mut PageTable) }; + pml3.zero(); + entry.set_frame(pml3_frame, PageTableFlags::PRESENT.into()); + } + }; + let mut page_table = EncryptedPageTable::new(pml4, VirtAddr::new(0), encrypted); // Safety: these operations are safe as they're not done on active page tables. diff --git a/oak_restricted_kernel/src/syscall/switch_process.rs b/oak_restricted_kernel/src/syscall/switch_process.rs index e9258787a81..306dfd97d32 100644 --- a/oak_restricted_kernel/src/syscall/switch_process.rs +++ b/oak_restricted_kernel/src/syscall/switch_process.rs @@ -39,14 +39,11 @@ pub fn syscall_unstable_switch_proccess(buf: *mut c_void, count: c_size_t) -> ! let application = crate::payload::Application::new(copied_elf_binary.into_boxed_slice()) .expect("failed to parse application"); - // Ensure the page table is not dropped. - let pml4 = Box::leak(Box::new(PageTable::new())); - let encryption = crate::PAGE_TABLES - .lock() + let (base_pml4, encrypted) = crate::BASE_L4_PAGE_TABLE .get() - .expect("page tables must exist to switch applications") - .inner() - .copy_kernel_space(pml4); + .expect("base l4 table should be set"); + // Ensure the new page table is not dropped. + let pml4 = Box::leak(Box::new(base_pml4.clone())); let pml4_frame = { let phys_addr = { @@ -61,7 +58,7 @@ pub fn syscall_unstable_switch_proccess(buf: *mut c_void, count: c_size_t) -> ! }; // Safety: the new page table maintains the same mappings for kernel space. - unsafe { crate::PAGE_TABLES.lock().replace(pml4_frame, encryption) }; + unsafe { crate::PAGE_TABLES.lock().replace(pml4_frame, *encrypted) }; // Safety: we've loaded the Restricted Application. Whether that's valid or not is no longer // under the kernel's control. unsafe { application.run() } From 5d1f9dc458d1301bc0b79b889e75841eaaa6bb31 Mon Sep 17 00:00:00 2001 From: Ivan Petrov Date: Fri, 16 Feb 2024 16:05:51 +0000 Subject: [PATCH 3/3] Make Orchestrator Key Provisioning service listen on all interfaces (#4810) --- oak_containers_orchestrator/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oak_containers_orchestrator/src/main.rs b/oak_containers_orchestrator/src/main.rs index 81992f90db4..0e64d057c92 100644 --- a/oak_containers_orchestrator/src/main.rs +++ b/oak_containers_orchestrator/src/main.rs @@ -32,7 +32,7 @@ struct Args { #[arg(default_value = "http://10.0.2.100:8080")] launcher_addr: String, - #[arg(default_value = "127.0.0.1:4000")] + #[arg(default_value = "0.0.0.0:4000")] orchestrator_addr: String, #[arg(long, default_value = "/oak_container")]