From 56a8067486af568d99b8e40960f1461498477da7 Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Mon, 29 Jan 2024 11:36:06 +0800 Subject: [PATCH] AA: add check_init_data API This api is used to check the current TEE evidence's initdata field, like TDX's MRCONFIGID, SNP's HOSTDATA, etc. If the one inside evidence is different from the one provided, the RPC will raise an error. The operation of getting current TEE's evidence should be network independent, because AA usually runs at an early stage of a guest, where network is usually not prepared. Signed-off-by: Xynnn007 --- .../app/src/rpc/attestation/mod.rs | 57 ++++ attestation-agent/attester/src/lib.rs | 4 + .../token_provider/aa/attestation_agent.rs | 266 +++++++++++++++++- .../aa/attestation_agent_ttrpc.rs | 22 ++ attestation-agent/lib/src/lib.rs | 20 ++ .../protos/attestation-agent.proto | 8 + 6 files changed, 369 insertions(+), 8 deletions(-) diff --git a/attestation-agent/app/src/rpc/attestation/mod.rs b/attestation-agent/app/src/rpc/attestation/mod.rs index 2e7e8e2a0..b39e27bb5 100644 --- a/attestation-agent/app/src/rpc/attestation/mod.rs +++ b/attestation-agent/app/src/rpc/attestation/mod.rs @@ -116,6 +116,35 @@ pub mod grpc { Result::Ok(Response::new(reply)) } + + async fn check_init_data( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + + let attestation_agent_mutex_clone = Arc::clone(&ASYNC_ATTESTATION_AGENT); + let mut attestation_agent = attestation_agent_mutex_clone.lock().await; + + debug!("Call AA to check init data ..."); + + attestation_agent + .check_init_data(request.init_data_digest, request.init_data_plaintext) + .await + .map_err(|e| { + error!("Call AA to check init data failed: {}", e); + Status::internal(format!( + "[ERROR:{}] AA check init data failed: {}", + AGENT_NAME, e + )) + })?; + + debug!("Extend runtime measurement successfully!"); + + let reply = ExtendRuntimeMeasurementResponse {}; + + Result::Ok(Response::new(reply)) + } } pub async fn start_grpc_service(socket: SocketAddr) -> Result<()> { @@ -236,6 +265,34 @@ pub mod ttrpc { let reply = attestation_agent::ExtendRuntimeMeasurementResponse::new(); ::ttrpc::Result::Ok(reply) } + + async fn check_init_data( + &self, + _ctx: &::ttrpc::r#async::TtrpcContext, + req: attestation_agent::CheckInitDataRequest, + ) -> ::ttrpc::Result { + debug!("Call AA to check initdata ..."); + + let attestation_agent_mutex_clone = ASYNC_ATTESTATION_AGENT.clone(); + let mut attestation_agent = attestation_agent_mutex_clone.lock().await; + + attestation_agent + .check_init_data(req.InitDataDigest, req.InitDataPlaintext) + .await + .map_err(|e| { + error!("Call AA to check initdata failed: {}", e); + let mut error_status = ::ttrpc::proto::Status::new(); + error_status.set_code(Code::INTERNAL); + error_status.set_message(format!( + "[ERROR:{}] AA check initdata failed: {}", + AGENT_NAME, e + )); + ::ttrpc::Error::RpcStatus(error_status) + })?; + + let reply = attestation_agent::CheckInitDataResponse::new(); + ::ttrpc::Result::Ok(reply) + } } pub fn start_ttrpc_service() -> Result> { diff --git a/attestation-agent/attester/src/lib.rs b/attestation-agent/attester/src/lib.rs index 741f18c71..45e8fbd16 100644 --- a/attestation-agent/attester/src/lib.rs +++ b/attestation-agent/attester/src/lib.rs @@ -74,6 +74,10 @@ pub trait Attester { ) -> Result<()> { bail!("Unimplemented") } + + async fn check_init_data(&mut self, _init_data: Vec) -> Result<()> { + bail!("Unimplemented"); + } } // Detect which TEE platform the KBC running environment is. diff --git a/attestation-agent/kbs_protocol/src/token_provider/aa/attestation_agent.rs b/attestation-agent/kbs_protocol/src/token_provider/aa/attestation_agent.rs index ff1163576..c22c33f20 100644 --- a/attestation-agent/kbs_protocol/src/token_provider/aa/attestation_agent.rs +++ b/attestation-agent/kbs_protocol/src/token_provider/aa/attestation_agent.rs @@ -756,6 +756,249 @@ impl ::protobuf::reflect::ProtobufValue for ExtendRuntimeMeasurementResponse { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; } +// @@protoc_insertion_point(message:attestation_agent.CheckInitDataRequest) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct CheckInitDataRequest { + // message fields + // @@protoc_insertion_point(field:attestation_agent.CheckInitDataRequest.InitDataDigest) + pub InitDataDigest: ::std::vec::Vec, + // @@protoc_insertion_point(field:attestation_agent.CheckInitDataRequest.InitDataPlaintext) + pub InitDataPlaintext: ::std::option::Option<::std::vec::Vec>, + // special fields + // @@protoc_insertion_point(special_field:attestation_agent.CheckInitDataRequest.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a CheckInitDataRequest { + fn default() -> &'a CheckInitDataRequest { + ::default_instance() + } +} + +impl CheckInitDataRequest { + pub fn new() -> CheckInitDataRequest { + ::std::default::Default::default() + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(2); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "InitDataDigest", + |m: &CheckInitDataRequest| { &m.InitDataDigest }, + |m: &mut CheckInitDataRequest| { &mut m.InitDataDigest }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "InitDataPlaintext", + |m: &CheckInitDataRequest| { &m.InitDataPlaintext }, + |m: &mut CheckInitDataRequest| { &mut m.InitDataPlaintext }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "CheckInitDataRequest", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for CheckInitDataRequest { + const NAME: &'static str = "CheckInitDataRequest"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + self.InitDataDigest = is.read_bytes()?; + }, + 18 => { + self.InitDataPlaintext = ::std::option::Option::Some(is.read_bytes()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if !self.InitDataDigest.is_empty() { + my_size += ::protobuf::rt::bytes_size(1, &self.InitDataDigest); + } + if let Some(v) = self.InitDataPlaintext.as_ref() { + my_size += ::protobuf::rt::bytes_size(2, &v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if !self.InitDataDigest.is_empty() { + os.write_bytes(1, &self.InitDataDigest)?; + } + if let Some(v) = self.InitDataPlaintext.as_ref() { + os.write_bytes(2, v)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> CheckInitDataRequest { + CheckInitDataRequest::new() + } + + fn clear(&mut self) { + self.InitDataDigest.clear(); + self.InitDataPlaintext = ::std::option::Option::None; + self.special_fields.clear(); + } + + fn default_instance() -> &'static CheckInitDataRequest { + static instance: CheckInitDataRequest = CheckInitDataRequest { + InitDataDigest: ::std::vec::Vec::new(), + InitDataPlaintext: ::std::option::Option::None, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for CheckInitDataRequest { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("CheckInitDataRequest").unwrap()).clone() + } +} + +impl ::std::fmt::Display for CheckInitDataRequest { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for CheckInitDataRequest { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +// @@protoc_insertion_point(message:attestation_agent.CheckInitDataResponse) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct CheckInitDataResponse { + // special fields + // @@protoc_insertion_point(special_field:attestation_agent.CheckInitDataResponse.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a CheckInitDataResponse { + fn default() -> &'a CheckInitDataResponse { + ::default_instance() + } +} + +impl CheckInitDataResponse { + pub fn new() -> CheckInitDataResponse { + ::std::default::Default::default() + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(0); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "CheckInitDataResponse", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for CheckInitDataResponse { + const NAME: &'static str = "CheckInitDataResponse"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> CheckInitDataResponse { + CheckInitDataResponse::new() + } + + fn clear(&mut self) { + self.special_fields.clear(); + } + + fn default_instance() -> &'static CheckInitDataResponse { + static instance: CheckInitDataResponse = CheckInitDataResponse { + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for CheckInitDataResponse { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("CheckInitDataResponse").unwrap()).clone() + } +} + +impl ::std::fmt::Display for CheckInitDataResponse { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for CheckInitDataResponse { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + static file_descriptor_proto_data: &'static [u8] = b"\ \n\x17attestation-agent.proto\x12\x11attestation_agent\"6\n\x12GetEviden\ ceRequest\x12\x20\n\x0bRuntimeData\x18\x01\x20\x01(\x0cR\x0bRuntimeData\ @@ -765,13 +1008,18 @@ static file_descriptor_proto_data: &'static [u8] = b"\ \x20\x01(\x0cR\x05Token\"v\n\x1fExtendRuntimeMeasurementRequest\x12\x16\ \n\x06Events\x18\x01\x20\x03(\x0cR\x06Events\x12)\n\rRegisterIndex\x18\ \x02\x20\x01(\x04H\0R\rRegisterIndex\x88\x01\x01B\x10\n\x0e_RegisterInde\ - x\"\"\n\x20ExtendRuntimeMeasurementResponse2\xd2\x02\n\x17AttestationAge\ - ntService\x12\\\n\x0bGetEvidence\x12%.attestation_agent.GetEvidenceReque\ - st\x1a&.attestation_agent.GetEvidenceResponse\x12S\n\x08GetToken\x12\".a\ - ttestation_agent.GetTokenRequest\x1a#.attestation_agent.GetTokenResponse\ - \x12\x83\x01\n\x18ExtendRuntimeMeasurement\x122.attestation_agent.Extend\ - RuntimeMeasurementRequest\x1a3.attestation_agent.ExtendRuntimeMeasuremen\ - tResponseb\x06proto3\ + x\"\"\n\x20ExtendRuntimeMeasurementResponse\"\x87\x01\n\x14CheckInitData\ + Request\x12&\n\x0eInitDataDigest\x18\x01\x20\x01(\x0cR\x0eInitDataDigest\ + \x121\n\x11InitDataPlaintext\x18\x02\x20\x01(\x0cH\0R\x11InitDataPlainte\ + xt\x88\x01\x01B\x14\n\x12_InitDataPlaintext\"\x17\n\x15CheckInitDataResp\ + onse2\xb6\x03\n\x17AttestationAgentService\x12\\\n\x0bGetEvidence\x12%.a\ + ttestation_agent.GetEvidenceRequest\x1a&.attestation_agent.GetEvidenceRe\ + sponse\x12S\n\x08GetToken\x12\".attestation_agent.GetTokenRequest\x1a#.a\ + ttestation_agent.GetTokenResponse\x12\x83\x01\n\x18ExtendRuntimeMeasurem\ + ent\x122.attestation_agent.ExtendRuntimeMeasurementRequest\x1a3.attestat\ + ion_agent.ExtendRuntimeMeasurementResponse\x12b\n\rCheckInitData\x12'.at\ + testation_agent.CheckInitDataRequest\x1a(.attestation_agent.CheckInitDat\ + aResponseb\x06proto3\ "; /// `FileDescriptorProto` object which was a source for this generated file @@ -789,13 +1037,15 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { file_descriptor.get(|| { let generated_file_descriptor = generated_file_descriptor_lazy.get(|| { let mut deps = ::std::vec::Vec::with_capacity(0); - let mut messages = ::std::vec::Vec::with_capacity(6); + let mut messages = ::std::vec::Vec::with_capacity(8); messages.push(GetEvidenceRequest::generated_message_descriptor_data()); messages.push(GetEvidenceResponse::generated_message_descriptor_data()); messages.push(GetTokenRequest::generated_message_descriptor_data()); messages.push(GetTokenResponse::generated_message_descriptor_data()); messages.push(ExtendRuntimeMeasurementRequest::generated_message_descriptor_data()); messages.push(ExtendRuntimeMeasurementResponse::generated_message_descriptor_data()); + messages.push(CheckInitDataRequest::generated_message_descriptor_data()); + messages.push(CheckInitDataResponse::generated_message_descriptor_data()); let mut enums = ::std::vec::Vec::with_capacity(0); ::protobuf::reflect::GeneratedFileDescriptor::new_generated( file_descriptor_proto(), diff --git a/attestation-agent/kbs_protocol/src/token_provider/aa/attestation_agent_ttrpc.rs b/attestation-agent/kbs_protocol/src/token_provider/aa/attestation_agent_ttrpc.rs index 5346928d1..2f917e3c4 100644 --- a/attestation-agent/kbs_protocol/src/token_provider/aa/attestation_agent_ttrpc.rs +++ b/attestation-agent/kbs_protocol/src/token_provider/aa/attestation_agent_ttrpc.rs @@ -46,6 +46,11 @@ impl AttestationAgentServiceClient { let mut cres = super::attestation_agent::ExtendRuntimeMeasurementResponse::new(); ::ttrpc::async_client_request!(self, ctx, req, "attestation_agent.AttestationAgentService", "ExtendRuntimeMeasurement", cres); } + + pub async fn check_init_data(&self, ctx: ttrpc::context::Context, req: &super::attestation_agent::CheckInitDataRequest) -> ::ttrpc::Result { + let mut cres = super::attestation_agent::CheckInitDataResponse::new(); + ::ttrpc::async_client_request!(self, ctx, req, "attestation_agent.AttestationAgentService", "CheckInitData", cres); + } } struct GetEvidenceMethod { @@ -81,6 +86,17 @@ impl ::ttrpc::r#async::MethodHandler for ExtendRuntimeMeasurementMethod { } } +struct CheckInitDataMethod { + service: Arc>, +} + +#[async_trait] +impl ::ttrpc::r#async::MethodHandler for CheckInitDataMethod { + async fn handler(&self, ctx: ::ttrpc::r#async::TtrpcContext, req: ::ttrpc::Request) -> ::ttrpc::Result<::ttrpc::Response> { + ::ttrpc::async_request_handler!(self, ctx, req, attestation_agent, CheckInitDataRequest, check_init_data); + } +} + #[async_trait] pub trait AttestationAgentService: Sync { async fn get_evidence(&self, _ctx: &::ttrpc::r#async::TtrpcContext, _: super::attestation_agent::GetEvidenceRequest) -> ::ttrpc::Result { @@ -92,6 +108,9 @@ pub trait AttestationAgentService: Sync { async fn extend_runtime_measurement(&self, _ctx: &::ttrpc::r#async::TtrpcContext, _: super::attestation_agent::ExtendRuntimeMeasurementRequest) -> ::ttrpc::Result { Err(::ttrpc::Error::RpcStatus(::ttrpc::get_status(::ttrpc::Code::NOT_FOUND, "/attestation_agent.AttestationAgentService/ExtendRuntimeMeasurement is not supported".to_string()))) } + async fn check_init_data(&self, _ctx: &::ttrpc::r#async::TtrpcContext, _: super::attestation_agent::CheckInitDataRequest) -> ::ttrpc::Result { + Err(::ttrpc::Error::RpcStatus(::ttrpc::get_status(::ttrpc::Code::NOT_FOUND, "/attestation_agent.AttestationAgentService/CheckInitData is not supported".to_string()))) + } } pub fn create_attestation_agent_service(service: Arc>) -> HashMap { @@ -108,6 +127,9 @@ pub fn create_attestation_agent_service(service: Arc); + methods.insert("CheckInitData".to_string(), + Box::new(CheckInitDataMethod{service: service.clone()}) as Box); + ret.insert("attestation_agent.AttestationAgentService".to_string(), ::ttrpc::r#async::Service{ methods, streams }); ret } diff --git a/attestation-agent/lib/src/lib.rs b/attestation-agent/lib/src/lib.rs index 3c002af19..9e56b63aa 100644 --- a/attestation-agent/lib/src/lib.rs +++ b/attestation-agent/lib/src/lib.rs @@ -88,6 +88,13 @@ pub trait AttestationAPIs { events: Vec>, register_index: Option, ) -> Result<()>; + + /// Check the initdata binding + async fn check_init_data( + &mut self, + init_data: Vec, + _init_data_plaintext: Option>, + ) -> Result<()>; } /// Attestation agent to provide attestation service. @@ -221,4 +228,17 @@ impl AttestationAPIs for AttestationAgent { .await?; Ok(()) } + + /// Check the initdata binding. If current platform does not support initdata + /// injection, return a success and raise a warning log. + async fn check_init_data( + &mut self, + init_data: Vec, + _init_data_plaintext: Option>, + ) -> Result<()> { + let tee_type = detect_tee_type(); + let mut attester = TryInto::::try_into(tee_type)?; + attester.check_init_data(init_data).await?; + Ok(()) + } } diff --git a/attestation-agent/protos/attestation-agent.proto b/attestation-agent/protos/attestation-agent.proto index a6a455464..aad8762c0 100644 --- a/attestation-agent/protos/attestation-agent.proto +++ b/attestation-agent/protos/attestation-agent.proto @@ -25,8 +25,16 @@ message ExtendRuntimeMeasurementRequest { message ExtendRuntimeMeasurementResponse {} +message CheckInitDataRequest { + bytes InitDataDigest = 1; + optional bytes InitDataPlaintext = 2; +} + +message CheckInitDataResponse {} + service AttestationAgentService { rpc GetEvidence(GetEvidenceRequest) returns (GetEvidenceResponse) {}; rpc GetToken(GetTokenRequest) returns (GetTokenResponse) {}; rpc ExtendRuntimeMeasurement(ExtendRuntimeMeasurementRequest) returns (ExtendRuntimeMeasurementResponse) {}; + rpc CheckInitData(CheckInitDataRequest) returns (CheckInitDataResponse) {}; }