// Copyright 2023 Google LLC // // 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. // //////////////////////////////////////////////////////////////////////////////// //! Implementation of a HAL service for AuthGraph. use crate::{channel::SerializedChannel, errcode_to_binder, Innto, TryInnto}; use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph::{ Arc::Arc, IAuthGraphKeyExchange::BnAuthGraphKeyExchange, IAuthGraphKeyExchange::IAuthGraphKeyExchange, Identity::Identity, KeInitResult::KeInitResult, Key::Key, PubKey::PubKey, SessionIdSignature::SessionIdSignature, SessionInfo::SessionInfo, SessionInitiationInfo::SessionInitiationInfo, }; use authgraph_wire as wire; use log::{error, warn}; use std::ffi::CString; use wire::{ cbor::{AsCborValue, CborError}, Code, ErrorCode, PerformOpReq, PerformOpResponse, PerformOpRsp, }; /// Emit a failure for a failed CBOR conversion. #[inline] pub fn failed_cbor(err: CborError) -> binder::Status { binder::Status::new_service_specific_error( ErrorCode::InternalError as i32, Some(&CString::new(format!("CBOR conversion failed: {:?}", err)).unwrap()), ) } /// Implementation of the AuthGraph key exchange HAL service, communicating with a TA /// in a secure environment via a communication channel. pub struct AuthGraphService { channel: T, } impl AuthGraphService { /// Construct a new instance that uses the provided channel. pub fn new(channel: T) -> Self { Self { channel } } /// Create a new instance wrapped in a proxy object. pub fn new_as_binder(channel: T) -> binder::Strong { BnAuthGraphKeyExchange::new_binder(Self::new(channel), binder::BinderFeatures::default()) } /// Execute the given request, by serializing it and sending it down the internal channel. Then /// read and deserialize the response. fn execute_req(&self, req: PerformOpReq) -> binder::Result { let code = req.code(); let req_data = req.into_vec().map_err(failed_cbor)?; if req_data.len() > T::MAX_SIZE { error!( "HAL operation {:?} encodes bigger {} than max size {}", code, req_data.len(), T::MAX_SIZE ); return Err(errcode_to_binder(ErrorCode::InternalError)); } // Pass the request to the channel, and read the response. let rsp_data = self.channel.execute(&req_data)?; let outer_rsp = PerformOpResponse::from_slice(&rsp_data).map_err(failed_cbor)?; if outer_rsp.error_code != ErrorCode::Ok { warn!("HAL: command {:?} failed: {:?}", code, outer_rsp.error_code); return Err(errcode_to_binder(outer_rsp.error_code)); } // On an OK outer response, the inner response should be populated with a message that // matches the request. outer_rsp.rsp.ok_or_else(|| { binder::Status::new_service_specific_error( ErrorCode::InternalError as i32, Some(&CString::new("missing inner rsp in OK outer rsp".to_string()).unwrap()), ) }) } } /// Macro to extract a specific variant from a [`PerformOpRsp`]. macro_rules! require_op_rsp { { $rsp:expr => $variant:ident } => { match $rsp { PerformOpRsp::$variant(rsp) => Ok(rsp), _ => Err(failed_cbor(CborError::UnexpectedItem("wrong rsp code", "rsp code"))), } } } impl binder::Interface for AuthGraphService {} /// Implement the `IAuthGraphKeyExchange` interface by translating incoming method invocations /// into request messages, which are passed to the TA. The corresponding response from the TA /// is then parsed and the return values extracted. impl IAuthGraphKeyExchange for AuthGraphService { fn create(&self) -> binder::Result { let req = PerformOpReq::Create(wire::CreateRequest {}); let wire::CreateResponse { ret } = require_op_rsp!(self.execute_req(req)? => Create)?; Ok(ret.innto()) } fn init( &self, peer_pub_key: &PubKey, peer_id: &Identity, peer_nonce: &[u8], peer_version: i32, ) -> binder::Result { let req = PerformOpReq::Init(wire::InitRequest { peer_pub_key: unsigned_pub_key(peer_pub_key)?, peer_id: peer_id.identity.to_vec(), peer_nonce: peer_nonce.to_vec(), peer_version, }); let wire::InitResponse { ret } = require_op_rsp!(self.execute_req(req)? => Init)?; Ok(ret.innto()) } fn finish( &self, peer_pub_key: &PubKey, peer_id: &Identity, peer_signature: &SessionIdSignature, peer_nonce: &[u8], peer_version: i32, own_key: &Key, ) -> binder::Result { let req = PerformOpReq::Finish(wire::FinishRequest { peer_pub_key: unsigned_pub_key(peer_pub_key)?, peer_id: peer_id.identity.to_vec(), peer_signature: peer_signature.signature.to_vec(), peer_nonce: peer_nonce.to_vec(), peer_version, own_key: own_key.clone().try_innto()?, }); let wire::FinishResponse { ret } = require_op_rsp!(self.execute_req(req)? => Finish)?; Ok(ret.innto()) } fn authenticationComplete( &self, peer_signature: &SessionIdSignature, shared_keys: &[Arc; 2], ) -> binder::Result<[Arc; 2]> { let req = PerformOpReq::AuthenticationComplete(wire::AuthenticationCompleteRequest { peer_signature: peer_signature.signature.to_vec(), shared_keys: [shared_keys[0].arc.to_vec(), shared_keys[1].arc.to_vec()], }); let wire::AuthenticationCompleteResponse { ret } = require_op_rsp!(self.execute_req(req)? => AuthenticationComplete)?; let [arc0, arc1] = ret; Ok([Arc { arc: arc0 }, Arc { arc: arc1 }]) } } /// Extract (and require) an unsigned public key as bytes from a [`PubKey`]. fn unsigned_pub_key(pub_key: &PubKey) -> binder::Result> { match pub_key { PubKey::PlainKey(key) => Ok(key.plainPubKey.to_vec()), PubKey::SignedKey(_) => Err(binder::Status::new_exception( binder::ExceptionCode::ILLEGAL_ARGUMENT, Some(&CString::new("expected unsigned public key").unwrap()), )), } }