Skip to content

Commit

Permalink
Add policy for application keys
Browse files Browse the repository at this point in the history
Policy is not implemented yes, since we need to decide on how we are
going to integrate the session binding key into the authentication
scheme.

Bug: 368030563
Bug: 399885537
Change-Id: I96f1308b8fba1ee4886bf6d8eea7597155e77c92
  • Loading branch information
ipetr0v committed Mar 7, 2025
1 parent 8817480 commit aef2554
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 21 deletions.
4 changes: 2 additions & 2 deletions oak_attestation_explain/src/json_serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,15 @@ pub fn serialize_container_layer_data(instance: &ContainerLayerData) -> serde_js
let ContainerLayerData {
bundle,
config,
encryption_public_key,
hybrid_encryption_public_key,
signing_public_key,
session_binding_public_key,
} = instance;

json!({
"bundle": bundle.as_ref().map(serialize_raw_digest),
"config": config.as_ref().map(serialize_raw_digest),
"encryption_public_key": encryption_public_key,
"hybrid_encryption_public_key": hybrid_encryption_public_key,
"signing_public_key": signing_public_key,
"session_binding_public_key": session_binding_public_key,
})
Expand Down
4 changes: 2 additions & 2 deletions oak_attestation_verification/src/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ fn extract_container_layer_data(claims: &ClaimsSet) -> anyhow::Result<ContainerL
bundle,
config,
// TODO: b/384476430 - Extract public keys from the event.
encryption_public_key: vec![],
hybrid_encryption_public_key: vec![],
signing_public_key: vec![],
session_binding_public_key: vec![],
})
Expand Down Expand Up @@ -627,7 +627,7 @@ fn oak_containers_orchestrator_measurements_to_container_layer_data(
bundle: measurements.container_image,
config: measurements.config,
// TODO: b/384476430 - Extract public keys from the event.
encryption_public_key: vec![],
hybrid_encryption_public_key: vec![],
signing_public_key: vec![],
session_binding_public_key: vec![],
}
Expand Down
10 changes: 6 additions & 4 deletions oak_attestation_verification/src/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,18 @@
//

pub mod application;
pub mod application_keys;
pub mod binary;
pub mod container;
pub mod firmware;
pub mod kernel;
pub mod platform;
pub mod system;

/// Public key used by the client to establish an HPKE session with the enclave.
pub const ENCRYPTION_PUBLIC_KEY_ID: &str = "oak-encryption-public-key:X25519";
/// Public key used to verify artifacts signed by the enclave.
pub const SIGNING_PUBLIC_KEY_ID: &str = "oak-signing-public-key:ecdsa-p256";
/// Public key used to verify that the Noise handshake transcript signature.
pub const SESSION_BINDING_PUBLIC_KEY_ID: &str = "oak-session-binding-public-key:ecdsa-p256";
/// Key used to encrypt a single message with hybrid encryption before sending
/// it to the enclave.
pub const HYBRID_ENCRYPTION_PUBLIC_KEY_ID: &str = "oak-hybrid-encryption-public-key:X25519";
/// Key used to verify artifacts generated and signed by the enclave.
pub const SIGNING_PUBLIC_KEY_ID: &str = "oak-signing-public-key:ecdsa-p256";
85 changes: 85 additions & 0 deletions oak_attestation_verification/src/policy/application_keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//
// Copyright 2025 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use alloc::{
collections::BTreeMap,
string::{String, ToString},
vec::Vec,
};

use oak_attestation_verification_types::policy::Policy;
use oak_proto_rust::oak::{
attestation::v1::{
ApplicationKeysData, ApplicationKeysReferenceValues, EventAttestationResults,
},
Variant,
};

use crate::{
policy::{
HYBRID_ENCRYPTION_PUBLIC_KEY_ID, SESSION_BINDING_PUBLIC_KEY_ID, SIGNING_PUBLIC_KEY_ID,
},
util::decode_event_proto,
};

pub struct ApplicationKeysPolicy {
_reference_values: ApplicationKeysReferenceValues,
}

impl ApplicationKeysPolicy {
pub fn new(reference_values: &ApplicationKeysReferenceValues) -> Self {
Self { _reference_values: reference_values.clone() }
}
}

// We have to use [`Policy<[u8]>`] instead of [`EventPolicy`], because
// Rust doesn't yet support implementing trait aliases.
// <https://github.com/rust-lang/rfcs/blob/master/text/1733-trait-alias.md>
impl Policy<[u8]> for ApplicationKeysPolicy {
fn verify(
&self,
encoded_event: &[u8],
_encoded_endorsement: &Variant,
_milliseconds_since_epoch: i64,
) -> anyhow::Result<EventAttestationResults> {
let event = decode_event_proto::<ApplicationKeysData>(
"type.googleapis.com/oak.attestation.v1.ApplicationKeysData",
encoded_event,
)?;

// TODO: b/399885537 - Verify that the key is signed by the CA.

let mut artifacts = BTreeMap::<String, Vec<u8>>::new();
if !event.session_binding_public_key.is_empty() {
artifacts.insert(
SESSION_BINDING_PUBLIC_KEY_ID.to_string(),
event.session_binding_public_key.to_vec(),
);
}
if !event.hybrid_encryption_public_key.is_empty() {
artifacts.insert(
HYBRID_ENCRYPTION_PUBLIC_KEY_ID.to_string(),
event.hybrid_encryption_public_key,
);
}
if !event.signing_public_key.is_empty() {
artifacts.insert(SIGNING_PUBLIC_KEY_ID.to_string(), event.signing_public_key);
}

// TODO: b/356631062 - Return detailed attestation results.
Ok(EventAttestationResults { artifacts })
}
}
27 changes: 19 additions & 8 deletions oak_attestation_verification/src/policy/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ use oak_proto_rust::oak::{
use crate::{
compare::compare_container_layer_measurement_digests,
expect::acquire_container_event_expected_values,
policy::{ENCRYPTION_PUBLIC_KEY_ID, SESSION_BINDING_PUBLIC_KEY_ID, SIGNING_PUBLIC_KEY_ID},
policy::{
HYBRID_ENCRYPTION_PUBLIC_KEY_ID, SESSION_BINDING_PUBLIC_KEY_ID, SIGNING_PUBLIC_KEY_ID,
},
util::decode_event_proto,
};

Expand Down Expand Up @@ -74,13 +76,22 @@ impl Policy<[u8]> for ContainerPolicy {
compare_container_layer_measurement_digests(&event, &expected_values)
.context("couldn't verify container event")?;

let artifacts = [
(ENCRYPTION_PUBLIC_KEY_ID.to_string(), event.encryption_public_key.to_vec()),
(SIGNING_PUBLIC_KEY_ID.to_string(), event.signing_public_key.to_vec()),
(SESSION_BINDING_PUBLIC_KEY_ID.to_string(), event.session_binding_public_key.to_vec()),
]
.into_iter()
.collect::<BTreeMap<String, Vec<u8>>>();
let mut artifacts = BTreeMap::<String, Vec<u8>>::new();
if !event.session_binding_public_key.is_empty() {
artifacts.insert(
SESSION_BINDING_PUBLIC_KEY_ID.to_string(),
event.session_binding_public_key.to_vec(),
);
}
if !event.hybrid_encryption_public_key.is_empty() {
artifacts.insert(
HYBRID_ENCRYPTION_PUBLIC_KEY_ID.to_string(),
event.hybrid_encryption_public_key,
);
}
if !event.signing_public_key.is_empty() {
artifacts.insert(SIGNING_PUBLIC_KEY_ID.to_string(), event.signing_public_key);
}

// TODO: b/356631062 - Return detailed attestation results.
Ok(EventAttestationResults { artifacts })
Expand Down
2 changes: 1 addition & 1 deletion oak_containers/attestation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ pub fn create_container_event<A: Buf, B: Buf>(
value: oak_proto_rust::oak::attestation::v1::ContainerLayerData {
bundle: Some(container_digest),
config: Some(config_digest),
encryption_public_key: instance_public_keys.encryption_public_key.to_vec(),
hybrid_encryption_public_key: instance_public_keys.encryption_public_key.to_vec(),
signing_public_key: instance_public_keys
.signing_public_key
.to_sec1_bytes()
Expand Down
65 changes: 63 additions & 2 deletions oak_proto_rust/generated/oak.attestation.v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,35 @@ pub struct FileReferenceValue {
#[prost(string, tag = "2")]
pub path: ::prost::alloc::string::String,
}
/// Reference value that provides a CA public key which can be used to verify CA
/// certificates.
/// TODO: b/399885537 - Implement Certificate Authority reference value.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost_derive::Message)]
pub struct CertificateAuthorityReferenceValue {}
/// Reference value for a public key.
/// - \[`SkipVerification`\] means that client doesn't have any predefined
/// expectations about the public key. Though it can still be signed with the
/// evidence (for example using DICE).
/// - \[`CertificateAuthorityReferenceValue`\] means that the client expects the
/// public key to be signed by the Certificate Authority.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost_derive::Message)]
pub struct PublicKeyReferenceValue {
#[prost(oneof = "public_key_reference_value::Type", tags = "1, 2")]
pub r#type: ::core::option::Option<public_key_reference_value::Type>,
}
/// Nested message and enum types in `PublicKeyReferenceValue`.
pub mod public_key_reference_value {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost_derive::Oneof)]
pub enum Type {
#[prost(message, tag = "1")]
Skip(super::SkipVerification),
#[prost(message, tag = "2")]
Ca(super::CertificateAuthorityReferenceValue),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost_derive::Message)]
pub struct Regex {
Expand Down Expand Up @@ -596,6 +625,18 @@ pub struct EventReferenceValues {
#[prost(message, optional, tag = "1")]
pub event: ::core::option::Option<BinaryReferenceValue>,
}
/// Represents expectations about public keys generated by the enclave
/// application.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost_derive::Message)]
pub struct ApplicationKeysReferenceValues {
#[prost(message, optional, tag = "1")]
pub session_binding_public_key: ::core::option::Option<PublicKeyReferenceValue>,
#[prost(message, optional, tag = "2")]
pub hybrid_encryption_public_key: ::core::option::Option<PublicKeyReferenceValue>,
#[prost(message, optional, tag = "3")]
pub signing_public_key: ::core::option::Option<PublicKeyReferenceValue>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost_derive::Message)]
pub struct OakRestrictedKernelReferenceValues {
Expand Down Expand Up @@ -1248,16 +1289,36 @@ pub struct ContainerLayerData {
/// Measurement RawDigest of the configuration used by the container.
#[prost(message, optional, tag = "2")]
pub config: ::core::option::Option<super::super::RawDigest>,
/// Key used to initialize HPKE encryption from the client.
/// Key used to encrypt a single message with hybrid encryption before sending
/// it to the enclave. This key must not be used to establish streaming
/// sessions, and each new message must use a random nonce.
#[prost(bytes = "vec", tag = "3")]
pub encryption_public_key: ::prost::alloc::vec::Vec<u8>,
pub hybrid_encryption_public_key: ::prost::alloc::vec::Vec<u8>,
/// Key used to verify artifacts signed by the TEE.
#[prost(bytes = "vec", tag = "4")]
pub signing_public_key: ::prost::alloc::vec::Vec<u8>,
/// Key used to verify that Noise session is bound to the evidence.
#[prost(bytes = "vec", tag = "5")]
pub session_binding_public_key: ::prost::alloc::vec::Vec<u8>,
}
/// Event that contains public keys corresponding to key pairs generated at the
/// application layer.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost_derive::Message)]
pub struct ApplicationKeysData {
/// Key used to verify that an encrypted session is bound to the enclave's
/// evidence.
#[prost(bytes = "vec", tag = "1")]
pub session_binding_public_key: ::prost::alloc::vec::Vec<u8>,
/// Key used to encrypt a single message with hybrid encryption before sending
/// it to the enclave. This key must not be used to establish streaming
/// sessions, and each new message must use a random nonce.
#[prost(bytes = "vec", tag = "2")]
pub hybrid_encryption_public_key: ::prost::alloc::vec::Vec<u8>,
/// Key used to verify artifacts generated and signed by the enclave.
#[prost(bytes = "vec", tag = "3")]
pub signing_public_key: ::prost::alloc::vec::Vec<u8>,
}
/// Values extracted from the evidence that represents an event.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost_derive::Message)]
Expand Down
26 changes: 26 additions & 0 deletions proto/attestation/reference_value.proto
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,24 @@ message FileReferenceValue {
string path = 2;
}

// Reference value that provides a CA public key which can be used to verify CA
// certificates.
// TODO: b/399885537 - Implement Certificate Authority reference value.
message CertificateAuthorityReferenceValue {}

// Reference value for a public key.
// - [`SkipVerification`] means that client doesn't have any predefined
// expectations about the public key. Though it can still be signed with the
// evidence (for example using DICE).
// - [`CertificateAuthorityReferenceValue`] means that the client expects the
// public key to be signed by the Certificate Authority.
message PublicKeyReferenceValue {
oneof type {
SkipVerification skip = 1;
CertificateAuthorityReferenceValue ca = 2;
}
}

message Regex {
string value = 1;
}
Expand Down Expand Up @@ -279,6 +297,14 @@ message EventReferenceValues {
BinaryReferenceValue event = 1;
}

// Represents expectations about public keys generated by the enclave
// application.
message ApplicationKeysReferenceValues {
PublicKeyReferenceValue session_binding_public_key = 1;
PublicKeyReferenceValue hybrid_encryption_public_key = 2;
PublicKeyReferenceValue signing_public_key = 3;
}

message OakRestrictedKernelReferenceValues {
RootLayerReferenceValues root_layer = 1;
KernelLayerReferenceValues kernel_layer = 2;
Expand Down
22 changes: 20 additions & 2 deletions proto/attestation/verification.proto
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,10 @@ message ContainerLayerData {
// Measurement RawDigest of the configuration used by the container.
RawDigest config = 2;

// Key used to initialize HPKE encryption from the client.
bytes encryption_public_key = 3;
// Key used to encrypt a single message with hybrid encryption before sending
// it to the enclave. This key must not be used to establish streaming
// sessions, and each new message must use a random nonce.
bytes hybrid_encryption_public_key = 3;

// Key used to verify artifacts signed by the TEE.
bytes signing_public_key = 4;
Expand All @@ -235,6 +237,22 @@ message ContainerLayerData {
bytes session_binding_public_key = 5;
}

// Event that contains public keys corresponding to key pairs generated at the
// application layer.
message ApplicationKeysData {
// Key used to verify that an encrypted session is bound to the enclave's
// evidence.
bytes session_binding_public_key = 1;

// Key used to encrypt a single message with hybrid encryption before sending
// it to the enclave. This key must not be used to establish streaming
// sessions, and each new message must use a random nonce.
bytes hybrid_encryption_public_key = 2;

// Key used to verify artifacts generated and signed by the enclave.
bytes signing_public_key = 3;
}

// Values extracted from the evidence that represents an event.
message EventData {
// Measurement RawDigest of an event.
Expand Down

0 comments on commit aef2554

Please sign in to comment.