diff --git a/Cargo.lock b/Cargo.lock index 14c47222d4..490bc7b008 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3094,6 +3094,7 @@ dependencies = [ "mockall", "oak_crypto", "oak_proto_rust", + "prost", ] [[package]] diff --git a/oak_proto_rust/generated/oak.session.v1.rs b/oak_proto_rust/generated/oak.session.v1.rs index 9045e2282c..8c32f05cb7 100644 --- a/oak_proto_rust/generated/oak.session.v1.rs +++ b/oak_proto_rust/generated/oak.session.v1.rs @@ -82,33 +82,27 @@ pub struct NoiseHandshakeMessage { #[prost(bytes = "vec", tag = "3")] pub ciphertext: ::prost::alloc::vec::Vec, } -/// Message to be signed as part of the attestation binding. +/// Message that binds the Noise session (and optionally other data) to the +/// Attestation Evidence. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost_derive::Message)] -pub struct AttestationBindingMessage { +pub struct SessionBinding { + /// Representation the serialized message cryptographically bound to the + /// handshake and the associated data (e.g., a signature). #[prost(bytes = "vec", tag = "1")] - pub handshake_hash: ::prost::alloc::vec::Vec, - #[prost(bytes = "vec", tag = "2")] - pub endorsements_hash: ::prost::alloc::vec::Vec, - #[prost(bytes = "vec", tag = "3")] - pub peer_reference_values_hash: ::prost::alloc::vec::Vec, -} -/// Message that binds the Noise session (and optionally Attestation Endorsement -/// and peer Reference Values) to the Attestation Evidence. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost_derive::Message)] -pub struct AttestationBinding { - /// Signature of the serialized `AttestationBindingMessage` Protobuf message. - #[prost(bytes = "vec", tag = "1")] - pub signature: ::prost::alloc::vec::Vec, + pub binding: ::prost::alloc::vec::Vec, } /// Request message for the crypto handshake request needed to establish a set of /// session keys. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost_derive::Message)] pub struct HandshakeRequest { - #[prost(message, optional, tag = "2")] - pub attestation_binding: ::core::option::Option, + /// Bindings to the attestation evidence, per binding type. + #[prost(btree_map = "string, message", tag = "3")] + pub attestation_bindings: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + SessionBinding, + >, #[prost(oneof = "handshake_request::HandshakeType", tags = "1")] pub handshake_type: ::core::option::Option, } @@ -126,8 +120,12 @@ pub mod handshake_request { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost_derive::Message)] pub struct HandshakeResponse { - #[prost(message, optional, tag = "2")] - pub attestation_binding: ::core::option::Option, + /// Bindings to the attestation evidence, per binding type. + #[prost(btree_map = "string, message", tag = "3")] + pub attestation_bindings: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + SessionBinding, + >, #[prost(oneof = "handshake_response::HandshakeType", tags = "1")] pub handshake_type: ::core::option::Option, } diff --git a/oak_session/BUILD b/oak_session/BUILD index de15ddc8ca..9c7a28f54d 100644 --- a/oak_session/BUILD +++ b/oak_session/BUILD @@ -33,6 +33,7 @@ rust_library( "@oak_crates_index//:aead", "@oak_crates_index//:anyhow", "@oak_crates_index//:itertools", + "@oak_crates_index//:prost", ], ) diff --git a/oak_session/Cargo.toml b/oak_session/Cargo.toml index 9d64fcb1c0..03c9de3fa4 100644 --- a/oak_session/Cargo.toml +++ b/oak_session/Cargo.toml @@ -11,6 +11,7 @@ anyhow = { version = "*", default-features = false } itertools = { version = "*", default-features = false } oak_crypto = { workspace = true } oak_proto_rust = { workspace = true } +prost = { version = "*", default-features = false } [dev-dependencies] mockall = { version = "*", default-features = false } diff --git a/oak_session/src/attestation.rs b/oak_session/src/attestation.rs index 3d4857244b..5f41e0c440 100644 --- a/oak_session/src/attestation.rs +++ b/oak_session/src/attestation.rs @@ -17,7 +17,13 @@ //! This module provides an implementation of the Attestation Provider, which //! handles remote attestation between two parties. -use alloc::{boxed::Box, collections::BTreeMap, string::String}; +use alloc::{ + boxed::Box, + collections::BTreeMap, + string::{String, ToString}, + vec::Vec, +}; +use core::fmt::Display; use anyhow::{anyhow, Context, Error, Ok}; use itertools::{EitherOrBoth, Itertools}; @@ -28,7 +34,42 @@ use oak_proto_rust::oak::{ session::v1::{AttestRequest, AttestResponse, EndorsedEvidence}, }; -use crate::{config::AttestationProviderConfig, ProtocolEngine}; +use crate::{ + config::AttestationProviderConfig, session_binding::SessionBindingVerifier, ProtocolEngine, +}; + +pub struct AttestationSuccess { + // Binders allowing to bind different types of attestation to the session (keyed by the + // attestation type ID). + pub session_binding_verifiers: BTreeMap>, +} +#[derive(Debug)] +pub struct AttestationFailure { + pub reason: String, + // Per verifier error messages (keyed by the attestation type ID). + pub error_messages: BTreeMap, +} + +impl Display for AttestationFailure { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "Attestation failure: {}. Errors from individual verifiers: {}", + self.reason, + self.error_messages + .iter() + .map(|(id, error)| format!("Verifier ID: {}, error: {}", id, error)) + .collect::>() + .join(";") + ) + } +} + +impl From for AttestationFailure { + fn from(value: Error) -> Self { + AttestationFailure { reason: value.to_string(), error_messages: BTreeMap::new() } + } +} #[cfg_attr(test, automock)] pub trait Attester: Send { @@ -36,12 +77,18 @@ pub trait Attester: Send { } #[cfg_attr(test, automock)] +// Verifier for the particular type of the attestation. pub trait AttestationVerifier: Send { fn verify( &self, evidence: &Evidence, endorsements: &Endorsements, ) -> anyhow::Result; + + fn create_session_binding_verifier( + &self, + results: &AttestationResults, + ) -> anyhow::Result>; } /// Configuration of the attestation behavior that the AttestationProvider will @@ -60,15 +107,23 @@ pub enum AttestationType { Unattested, } +// Provider for the particular type of the attestation. pub trait AttestationProvider: Send { - fn take_attestation_report(&mut self) -> Option; + // Consume the attestation results when they're ready. Returns None if the + // attestation still is still pending the incoming peer's data. + fn take_attestation_result(&mut self) + -> Option>; } +// Aggregates the attestation result from multiple verifiers. Implementations of +// this trait define the logic of when the overall attestation step succeeds or +// fails. pub trait AttestationAggregator: Send { fn aggregate_attestation_results( &self, + verifiers: &BTreeMap>, results: BTreeMap, - ) -> Result; + ) -> Result; } pub struct DefaultAttestationAggregator {} @@ -76,13 +131,13 @@ pub struct DefaultAttestationAggregator {} impl AttestationAggregator for DefaultAttestationAggregator { fn aggregate_attestation_results( &self, + verifiers: &BTreeMap>, results: BTreeMap, - ) -> Result { + ) -> Result { if results.is_empty() { - return Ok(AttestationResults { - status: attestation_results::Status::GenericFailure.into(), - reason: String::from("No matching attestation results"), - ..Default::default() + return Err(AttestationFailure { + reason: "No matching attestation results".to_string(), + error_messages: BTreeMap::new(), }); }; let failures: BTreeMap<&String, &AttestationResults> = results @@ -90,18 +145,32 @@ impl AttestationAggregator for DefaultAttestationAggregator { .filter(|(_, v)| v.status == attestation_results::Status::GenericFailure.into()) .collect(); if !failures.is_empty() { - return Ok(AttestationResults { - status: attestation_results::Status::GenericFailure.into(), - reason: failures + return Err(AttestationFailure { + reason: "Verification failed".to_string(), + error_messages: failures .iter() - .map(|(id, v)| format!("Id: {}, error: {}; ", id, v.reason)) + .map(|(id, v)| ((*id).clone(), v.reason.clone())) .collect(), - ..Default::default() }); }; - // In case of multiple success matches we just return the first one. - // Using unwrap(), as we have already checked that results are not empty. - Ok(results.first_key_value().unwrap().1.clone()) + Ok(AttestationSuccess { + session_binding_verifiers: results + .iter() + .map(|(id, results)| { + Ok(( + id.clone(), + verifiers + .get(id) + .ok_or(anyhow!( + "Missing verifier for attestation result with ID {}", + id + ))? + .create_session_binding_verifier(results)?, + )) + }) + .collect::>, Error>>()?, + }) + .map_err(AttestationFailure::from) } } @@ -110,18 +179,20 @@ impl AttestationAggregator for DefaultAttestationAggregator { #[allow(dead_code)] pub struct ClientAttestationProvider { config: AttestationProviderConfig, - attestation_results: Option, + attestation_result: Option>, } impl ClientAttestationProvider { pub fn new(config: AttestationProviderConfig) -> Self { - Self { config, attestation_results: None } + Self { config, attestation_result: None } } } impl AttestationProvider for ClientAttestationProvider { - fn take_attestation_report(&mut self) -> Option { - self.attestation_results.take() + fn take_attestation_result( + &mut self, + ) -> Option> { + self.attestation_result.take() } } @@ -149,14 +220,15 @@ impl ProtocolEngine for ClientAttestationProvider &mut self, incoming_message: &AttestResponse, ) -> anyhow::Result> { - self.attestation_results = match self.config.attestation_type { + self.attestation_result = match self.config.attestation_type { AttestationType::Bidirectional | AttestationType::PeerUnidirectional => { Some(self.config.attestation_aggregator.aggregate_attestation_results( + &self.config.peer_verifiers, combine_attestation_results( &self.config.peer_verifiers, &incoming_message.endorsed_evidence, )?, - )?) + )) } AttestationType::SelfUnidirectional => None, AttestationType::Unattested => return Err(anyhow!("no attestation message expected'")), @@ -170,18 +242,20 @@ impl ProtocolEngine for ClientAttestationProvider #[allow(dead_code)] pub struct ServerAttestationProvider { config: AttestationProviderConfig, - attestation_results: Option, + attestation_result: Option>, } impl ServerAttestationProvider { pub fn new(config: AttestationProviderConfig) -> Self { - Self { config, attestation_results: None } + Self { config, attestation_result: None } } } impl AttestationProvider for ServerAttestationProvider { - fn take_attestation_report(&mut self) -> Option { - self.attestation_results.take() + fn take_attestation_result( + &mut self, + ) -> Option> { + self.attestation_result.take() } } @@ -209,14 +283,15 @@ impl ProtocolEngine for ServerAttestationProvider &mut self, incoming_message: &AttestRequest, ) -> anyhow::Result> { - self.attestation_results = match self.config.attestation_type { + self.attestation_result = match self.config.attestation_type { AttestationType::Bidirectional | AttestationType::PeerUnidirectional => { Some(self.config.attestation_aggregator.aggregate_attestation_results( + &self.config.peer_verifiers, combine_attestation_results( &self.config.peer_verifiers, &incoming_message.endorsed_evidence, )?, - )?) + )) } AttestationType::SelfUnidirectional => None, AttestationType::Unattested => return Err(anyhow!("no attestation message expected'")), diff --git a/oak_session/src/handshake.rs b/oak_session/src/handshake.rs index b00996567d..d1191c62ab 100644 --- a/oak_session/src/handshake.rs +++ b/oak_session/src/handshake.rs @@ -17,7 +17,7 @@ //! This module provides an implementation of the Handshaker, which //! handles cryptographic handshake and secure session creation. -use alloc::{boxed::Box, vec::Vec}; +use alloc::{boxed::Box, collections::BTreeMap, vec::Vec}; use core::convert::TryInto; use aead::Error; @@ -25,7 +25,7 @@ use anyhow::{anyhow, Context}; use oak_crypto::{ identity_key::IdentityKeyHandle, noise_handshake::{ - client::HandshakeInitiator, respond_kk, respond_nk, respond_nn, Crypter, NoiseMessage, + client::HandshakeInitiator, respond_kk, respond_nk, respond_nn, NoiseMessage, }, }; use oak_proto_rust::oak::{ @@ -38,8 +38,6 @@ use oak_proto_rust::oak::{ use crate::{config::HandshakerConfig, ProtocolEngine}; -const HANDSHAKE_HASH_LEN: usize = 32; - #[derive(Copy, Clone)] pub enum HandshakeType { NoiseKK, @@ -48,17 +46,23 @@ pub enum HandshakeType { NoiseNN, } +pub struct HandshakeResult { + pub session_keys: SessionKeys, + pub handshake_hash: Vec, +} + pub trait Handshaker: Send { - fn derive_session_keys(&mut self) -> Option; + // Consume the handshake result when it's ready. Returns None if the handshake + // is still in progress or its results have already been consumed. + fn take_handshake_result(&mut self) -> Option; } /// Client-side Handshaker that initiates the crypto handshake with the server. -#[allow(dead_code)] pub struct ClientHandshaker { handshake_type: HandshakeType, handshake_initiator: HandshakeInitiator, initial_message: Option, - handshake_result: Option<([u8; HANDSHAKE_HASH_LEN], Crypter)>, + handshake_result: Option, } impl ClientHandshaker { @@ -99,8 +103,8 @@ impl ClientHandshaker { } impl Handshaker for ClientHandshaker { - fn derive_session_keys(&mut self) -> Option { - self.handshake_result.take().map(|(_handshake_hash, crypter)| crypter.into()) + fn take_handshake_result(&mut self) -> Option { + self.handshake_result.take() } } @@ -119,7 +123,7 @@ impl ProtocolEngine for ClientHandshaker { ciphertext: initial_message.ciphertext, }, )), - attestation_binding: None, + attestation_bindings: BTreeMap::new(), })) } @@ -132,7 +136,11 @@ impl ProtocolEngine for ClientHandshaker { self.handshake_result = Some( self.handshake_initiator .process_response(&noise_message.into()) - .map_err(|e| anyhow!("Error processing response: {e:?}"))?, + .map_err(|e| anyhow!("Error processing response: {e:?}")) + .map(|(handshake_hash, crypter)| HandshakeResult { + session_keys: crypter.into(), + handshake_hash: handshake_hash.to_vec(), + })?, ); Ok(Some(())) } @@ -149,7 +157,7 @@ pub struct ServerHandshaker { self_identity_key: Option>, peer_public_key: Option>, handshake_response: Option, - handshake_result: Option, + handshake_result: Option, } impl ServerHandshaker { @@ -165,7 +173,7 @@ impl ServerHandshaker { } impl Handshaker for ServerHandshaker { - fn derive_session_keys(&mut self) -> Option { + fn take_handshake_result(&mut self) -> Option { self.handshake_result.take() } } @@ -210,7 +218,10 @@ impl ProtocolEngine for ServerHandshaker { } None => return Err(anyhow!("Missing handshake_type")), }; - self.handshake_result = Some(noise_response.crypter.into()); + self.handshake_result = Some(HandshakeResult { + session_keys: noise_response.crypter.into(), + handshake_hash: noise_response.handshake_hash.to_vec(), + }); self.handshake_response = Some(HandshakeResponse { r#handshake_type: Some(handshake_response::HandshakeType::NoiseHandshakeMessage( NoiseHandshakeMessage { @@ -224,7 +235,7 @@ impl ProtocolEngine for ServerHandshaker { ciphertext: noise_response.response.ciphertext, }, )), - attestation_binding: None, + attestation_bindings: BTreeMap::new(), }); Ok(Some(())) } diff --git a/oak_session/src/lib.rs b/oak_session/src/lib.rs index 079c88fbd1..6aca31a60b 100644 --- a/oak_session/src/lib.rs +++ b/oak_session/src/lib.rs @@ -27,6 +27,7 @@ pub mod config; pub mod encryptors; pub mod handshake; pub mod session; +pub mod session_binding; #[cfg(test)] mod tests; diff --git a/oak_session/src/session.rs b/oak_session/src/session.rs index d6f9d8e706..7d6875544a 100644 --- a/oak_session/src/session.rs +++ b/oak_session/src/session.rs @@ -17,20 +17,25 @@ //! This module provides an SDK for creating secure attested sessions between //! two parties. -use alloc::{boxed::Box, collections::VecDeque}; +use alloc::{ + boxed::Box, + collections::{BTreeMap, VecDeque}, + string::String, +}; -use anyhow::{anyhow, Context, Error}; +use anyhow::{anyhow, Context, Error, Ok}; use oak_crypto::encryptor::Encryptor; -use oak_proto_rust::oak::{ - attestation::v1::{attestation_results, AttestationResults}, - session::v1::{ - session_request::Request, session_response::Response, EncryptedMessage, PlaintextMessage, - SessionRequest, SessionResponse, - }, +use oak_proto_rust::oak::session::v1::{ + session_request::Request, session_response::Response, EncryptedMessage, PlaintextMessage, + SessionBinding, SessionRequest, SessionResponse, }; +use prost::Message; use crate::{ - attestation::{AttestationProvider, ClientAttestationProvider, ServerAttestationProvider}, + attestation::{ + AttestationFailure, AttestationProvider, AttestationSuccess, ClientAttestationProvider, + ServerAttestationProvider, + }, config::{EncryptorConfig, SessionConfig}, handshake::{ClientHandshaker, Handshaker, ServerHandshaker}, ProtocolEngine, @@ -72,7 +77,7 @@ pub struct ClientSession { handshaker: ClientHandshaker, // encryptor is initialized once the handshake is completed and the session becomes open encryptor_config: EncryptorConfig, - attestation_results: Option, + attestation_result: Option>, encryptor: Option>, outgoing_requests: VecDeque, incoming_responses: VecDeque, @@ -85,7 +90,7 @@ impl ClientSession { handshaker: ClientHandshaker::create(config.handshaker_config) .context("couldn't create the client handshaker")?, encryptor_config: config.encryptor_config, - attestation_results: None, + attestation_result: None, encryptor: None, outgoing_requests: VecDeque::new(), incoming_responses: VecDeque::new(), @@ -168,14 +173,8 @@ impl ProtocolEngine for ClientSession { "invalid session state: attest message received but attester doesn't expect any" ))?; - if let Some(attestation_results) = self.attester.take_attestation_report() { - self.attestation_results = Some(attestation_results.clone()); - if attestation_results.status != attestation_results::Status::Success.into() { - return Err(anyhow!( - "Attestation verification failed: {}", - attestation_results.reason - )); - } + if let Some(attestation_result) = self.attester.take_attestation_result() { + self.attestation_result = Some(attestation_result); } Ok(Some(())) } @@ -184,9 +183,16 @@ impl ProtocolEngine for ClientSession { "invalid session state: handshake message received but handshaker doesn't expect any" ))?; - if let Some(session_keys) = self.handshaker.derive_session_keys() { + if let Some(handshake_result) = self.handshaker.take_handshake_result() { + if let Some(attestation_result) = &self.attestation_result { + verify_session_binding( + &attestation_result, + &handshake_message.attestation_bindings, + handshake_result.handshake_hash.as_slice(), + )?; + } self.encryptor = Some( - (self.encryptor_config.encryptor_provider)(session_keys) + (self.encryptor_config.encryptor_provider)(handshake_result.session_keys) .context("couldn't create an encryptor from the session key")?, ) } @@ -213,7 +219,7 @@ pub struct ServerSession { handshaker: ServerHandshaker, // encryptor is initialized once the handshake is completed and the session becomes open encryptor_config: EncryptorConfig, - attestation_results: Option, + attestation_result: Option>, encryptor: Option>, outgoing_responses: VecDeque, incoming_requests: VecDeque, @@ -225,7 +231,7 @@ impl ServerSession { attester: ServerAttestationProvider::new(config.attestation_provider_config), handshaker: ServerHandshaker::new(config.handshaker_config), encryptor_config: config.encryptor_config, - attestation_results: None, + attestation_result: None, encryptor: None, outgoing_responses: VecDeque::new(), incoming_requests: VecDeque::new(), @@ -308,14 +314,8 @@ impl ProtocolEngine for ServerSession { "invalid session state: attest message received but attester doesn't expect any" ))?; - if let Some(attestation_results) = self.attester.take_attestation_report() { - self.attestation_results = Some(attestation_results.clone()); - if attestation_results.status != attestation_results::Status::Success.into() { - return Err(anyhow!( - "Attestation verification failed: {}", - attestation_results.reason - )); - } + if let Some(attestation_result) = self.attester.take_attestation_result() { + self.attestation_result = Some(attestation_result); } Ok(Some(())) } @@ -324,9 +324,16 @@ impl ProtocolEngine for ServerSession { "invalid session state: handshake message received but handshaker doesn't expect any" ))?; - if let Some(session_keys) = self.handshaker.derive_session_keys() { + if let Some(handshake_result) = self.handshaker.take_handshake_result() { + if let Some(attestation_result) = &self.attestation_result { + verify_session_binding( + &attestation_result, + &handshake_message.attestation_bindings, + handshake_result.handshake_hash.as_slice(), + )?; + } self.encryptor = Some( - (self.encryptor_config.encryptor_provider)(session_keys) + (self.encryptor_config.encryptor_provider)(handshake_result.session_keys) .context("couldn't create an encryptor from the session key")?, ) } @@ -346,3 +353,25 @@ impl ProtocolEngine for ServerSession { } } } + +fn verify_session_binding( + attestation_result: &Result, + bindings: &BTreeMap, + handshake_hash: &[u8], +) -> Result<(), Error> { + let attestation = attestation_result + .as_ref() + .map_err(|_| anyhow!("attempt to verify attestation binding to a failed attestation"))?; + + for (verifier_id, binding_verifier) in &attestation.session_binding_verifiers { + binding_verifier.verify_binding( + handshake_hash, + bindings + .get(verifier_id) + .ok_or(anyhow!("handshake message doesn't have a binding for ID {}", verifier_id))? + .encode_to_vec() + .as_slice(), + )?; + } + Ok(()) +} diff --git a/oak_session/src/session_binding.rs b/oak_session/src/session_binding.rs new file mode 100644 index 0000000000..c243de8784 --- /dev/null +++ b/oak_session/src/session_binding.rs @@ -0,0 +1,34 @@ +// +// Copyright 2024 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. +// + +//! This module provides traits that allow to bind the data to the session. +use alloc::vec::Vec; + +use anyhow::Error; +#[cfg(test)] +use mockall::automock; + +// Trait that allows binding session to the arbitrary data +pub trait SessionBinder: Send { + fn bind(&self, bound_data: &[u8]) -> Vec; +} + +// Trait that allows verifying the binding between the session and the arbitrary +// data. +#[cfg_attr(test, automock)] +pub trait SessionBindingVerifier: Send { + fn verify_binding(&self, bound_data: &[u8], binding: &[u8]) -> Result<(), Error>; +} diff --git a/oak_session/src/tests.rs b/oak_session/src/tests.rs index bebecefeb6..73a2522524 100644 --- a/oak_session/src/tests.rs +++ b/oak_session/src/tests.rs @@ -33,6 +33,7 @@ use crate::{ config::{AttestationProviderConfig, HandshakerConfig, SessionConfig}, encryptors::OrderedChannelEncryptor, handshake::{ClientHandshaker, HandshakeType, Handshaker, ServerHandshaker}, + session_binding::MockSessionBindingVerifier, ClientSession, ProtocolEngine, ServerSession, Session, }; @@ -96,20 +97,8 @@ fn attestation_verification_succeeds() { .is_ok() ); - let client_attestation_result = client_attestation_provider.take_attestation_report().unwrap(); - assert_eq!( - client_attestation_result.status, - attestation_results::Status::Success.into(), - "Client attestation verification failed: {}", - client_attestation_result.reason - ); - let server_attestation_result = server_attestation_provider.take_attestation_report().unwrap(); - assert_eq!( - server_attestation_result.status, - attestation_results::Status::Success.into(), - "Server attestation verification failed: {}", - server_attestation_result.reason - ); + client_attestation_provider.take_attestation_result().unwrap().unwrap(); + server_attestation_provider.take_attestation_result().unwrap().unwrap(); } #[test] @@ -159,16 +148,10 @@ fn attestation_verification_fails() { .is_ok() ); - let client_attestation_result = client_attestation_provider.take_attestation_report().unwrap(); - assert_eq!( - client_attestation_result.status, - attestation_results::Status::GenericFailure.into() - ); - let server_attestation_result = server_attestation_provider.take_attestation_report().unwrap(); - assert_eq!( - server_attestation_result.status, - attestation_results::Status::GenericFailure.into() - ); + let client_attestation_result = client_attestation_provider.take_attestation_result().unwrap(); + assert!(client_attestation_result.is_err()); + let server_attestation_result = server_attestation_provider.take_attestation_result().unwrap(); + assert!(server_attestation_result.is_err()); } fn create_mock_attester() -> Box { @@ -190,6 +173,9 @@ fn create_passing_mock_verifier() -> Box { ..Default::default() }) }); + verifier + .expect_create_session_binding_verifier() + .returning(|_| Ok(Box::new(MockSessionBindingVerifier::new()))); Box::new(verifier) } @@ -272,8 +258,8 @@ fn do_handshake(mut client_handshaker: ClientHandshaker, mut server_handshaker: client_handshaker .put_incoming_message(&response) .expect("Failed to process the client incoming message"); - let session_keys_client = client_handshaker.derive_session_keys().unwrap(); - let session_keys_server = server_handshaker.derive_session_keys().unwrap(); + let session_keys_client = client_handshaker.take_handshake_result().unwrap().session_keys; + let session_keys_server = server_handshaker.take_handshake_result().unwrap().session_keys; assert_eq!(session_keys_client.request_key, session_keys_server.response_key); assert_eq!(session_keys_server.request_key, session_keys_client.response_key); diff --git a/proto/session/session.proto b/proto/session/session.proto index a0adc6a599..a8540317d9 100644 --- a/proto/session/session.proto +++ b/proto/session/session.proto @@ -52,18 +52,12 @@ message NoiseHandshakeMessage { bytes ciphertext = 3; } -// Message to be signed as part of the attestation binding. -message AttestationBindingMessage { - bytes handshake_hash = 1; - bytes endorsements_hash = 2; - bytes peer_reference_values_hash = 3; -} - -// Message that binds the Noise session (and optionally Attestation Endorsement -// and peer Reference Values) to the Attestation Evidence. -message AttestationBinding { - // Signature of the serialized `AttestationBindingMessage` Protobuf message. - bytes signature = 1; +// Message that binds the Noise session (and optionally other data) to the +// Attestation Evidence. +message SessionBinding { + // Representation the serialized message cryptographically bound to the + // handshake and the associated data (e.g., a signature). + bytes binding = 1; } // Request message for the crypto handshake request needed to establish a set of @@ -72,7 +66,9 @@ message HandshakeRequest { oneof handshake_type { NoiseHandshakeMessage noise_handshake_message = 1; } - AttestationBinding attestation_binding = 2; + reserved 2; + // Bindings to the attestation evidence, per binding type. + map attestation_bindings = 3; } // Response message for the crypto handshake request needed to establish a set @@ -81,7 +77,9 @@ message HandshakeResponse { oneof handshake_type { NoiseHandshakeMessage noise_handshake_message = 1; } - AttestationBinding attestation_binding = 2; + reserved 2; + // Bindings to the attestation evidence, per binding type. + map attestation_bindings = 3; } // Message for encrypted data exchange after a secure session is established.