xref: /aosp_15_r20/system/authgraph/hal/src/service.rs (revision 4185b0660fbe514985fdcf75410317caad8afad1)
1*4185b066SAndroid Build Coastguard Worker // Copyright 2023 Google LLC
2*4185b066SAndroid Build Coastguard Worker //
3*4185b066SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*4185b066SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*4185b066SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*4185b066SAndroid Build Coastguard Worker //
7*4185b066SAndroid Build Coastguard Worker //      http://www.apache.org/licenses/LICENSE-2.0
8*4185b066SAndroid Build Coastguard Worker //
9*4185b066SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*4185b066SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*4185b066SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*4185b066SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*4185b066SAndroid Build Coastguard Worker // limitations under the License.
14*4185b066SAndroid Build Coastguard Worker //
15*4185b066SAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
16*4185b066SAndroid Build Coastguard Worker 
17*4185b066SAndroid Build Coastguard Worker //! Implementation of a HAL service for AuthGraph.
18*4185b066SAndroid Build Coastguard Worker 
19*4185b066SAndroid Build Coastguard Worker use crate::{channel::SerializedChannel, errcode_to_binder, Innto, TryInnto};
20*4185b066SAndroid Build Coastguard Worker use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph::{
21*4185b066SAndroid Build Coastguard Worker     Arc::Arc, IAuthGraphKeyExchange::BnAuthGraphKeyExchange,
22*4185b066SAndroid Build Coastguard Worker     IAuthGraphKeyExchange::IAuthGraphKeyExchange, Identity::Identity, KeInitResult::KeInitResult,
23*4185b066SAndroid Build Coastguard Worker     Key::Key, PubKey::PubKey, SessionIdSignature::SessionIdSignature, SessionInfo::SessionInfo,
24*4185b066SAndroid Build Coastguard Worker     SessionInitiationInfo::SessionInitiationInfo,
25*4185b066SAndroid Build Coastguard Worker };
26*4185b066SAndroid Build Coastguard Worker use authgraph_wire as wire;
27*4185b066SAndroid Build Coastguard Worker use log::{error, warn};
28*4185b066SAndroid Build Coastguard Worker use std::ffi::CString;
29*4185b066SAndroid Build Coastguard Worker use wire::{
30*4185b066SAndroid Build Coastguard Worker     cbor::{AsCborValue, CborError},
31*4185b066SAndroid Build Coastguard Worker     Code, ErrorCode, PerformOpReq, PerformOpResponse, PerformOpRsp,
32*4185b066SAndroid Build Coastguard Worker };
33*4185b066SAndroid Build Coastguard Worker 
34*4185b066SAndroid Build Coastguard Worker /// Emit a failure for a failed CBOR conversion.
35*4185b066SAndroid Build Coastguard Worker #[inline]
failed_cbor(err: CborError) -> binder::Status36*4185b066SAndroid Build Coastguard Worker pub fn failed_cbor(err: CborError) -> binder::Status {
37*4185b066SAndroid Build Coastguard Worker     binder::Status::new_service_specific_error(
38*4185b066SAndroid Build Coastguard Worker         ErrorCode::InternalError as i32,
39*4185b066SAndroid Build Coastguard Worker         Some(&CString::new(format!("CBOR conversion failed: {:?}", err)).unwrap()),
40*4185b066SAndroid Build Coastguard Worker     )
41*4185b066SAndroid Build Coastguard Worker }
42*4185b066SAndroid Build Coastguard Worker 
43*4185b066SAndroid Build Coastguard Worker /// Implementation of the AuthGraph key exchange HAL service, communicating with a TA
44*4185b066SAndroid Build Coastguard Worker /// in a secure environment via a communication channel.
45*4185b066SAndroid Build Coastguard Worker pub struct AuthGraphService<T: SerializedChannel + 'static> {
46*4185b066SAndroid Build Coastguard Worker     channel: T,
47*4185b066SAndroid Build Coastguard Worker }
48*4185b066SAndroid Build Coastguard Worker 
49*4185b066SAndroid Build Coastguard Worker impl<T: SerializedChannel + 'static> AuthGraphService<T> {
50*4185b066SAndroid Build Coastguard Worker     /// Construct a new instance that uses the provided channel.
new(channel: T) -> Self51*4185b066SAndroid Build Coastguard Worker     pub fn new(channel: T) -> Self {
52*4185b066SAndroid Build Coastguard Worker         Self { channel }
53*4185b066SAndroid Build Coastguard Worker     }
54*4185b066SAndroid Build Coastguard Worker 
55*4185b066SAndroid Build Coastguard Worker     /// Create a new instance wrapped in a proxy object.
new_as_binder(channel: T) -> binder::Strong<dyn IAuthGraphKeyExchange>56*4185b066SAndroid Build Coastguard Worker     pub fn new_as_binder(channel: T) -> binder::Strong<dyn IAuthGraphKeyExchange> {
57*4185b066SAndroid Build Coastguard Worker         BnAuthGraphKeyExchange::new_binder(Self::new(channel), binder::BinderFeatures::default())
58*4185b066SAndroid Build Coastguard Worker     }
59*4185b066SAndroid Build Coastguard Worker 
60*4185b066SAndroid Build Coastguard Worker     /// Execute the given request, by serializing it and sending it down the internal channel.  Then
61*4185b066SAndroid Build Coastguard Worker     /// read and deserialize the response.
execute_req(&self, req: PerformOpReq) -> binder::Result<PerformOpRsp>62*4185b066SAndroid Build Coastguard Worker     fn execute_req(&self, req: PerformOpReq) -> binder::Result<PerformOpRsp> {
63*4185b066SAndroid Build Coastguard Worker         let code = req.code();
64*4185b066SAndroid Build Coastguard Worker         let req_data = req.into_vec().map_err(failed_cbor)?;
65*4185b066SAndroid Build Coastguard Worker         if req_data.len() > T::MAX_SIZE {
66*4185b066SAndroid Build Coastguard Worker             error!(
67*4185b066SAndroid Build Coastguard Worker                 "HAL operation {:?} encodes bigger {} than max size {}",
68*4185b066SAndroid Build Coastguard Worker                 code,
69*4185b066SAndroid Build Coastguard Worker                 req_data.len(),
70*4185b066SAndroid Build Coastguard Worker                 T::MAX_SIZE
71*4185b066SAndroid Build Coastguard Worker             );
72*4185b066SAndroid Build Coastguard Worker             return Err(errcode_to_binder(ErrorCode::InternalError));
73*4185b066SAndroid Build Coastguard Worker         }
74*4185b066SAndroid Build Coastguard Worker 
75*4185b066SAndroid Build Coastguard Worker         // Pass the request to the channel, and read the response.
76*4185b066SAndroid Build Coastguard Worker         let rsp_data = self.channel.execute(&req_data)?;
77*4185b066SAndroid Build Coastguard Worker 
78*4185b066SAndroid Build Coastguard Worker         let outer_rsp = PerformOpResponse::from_slice(&rsp_data).map_err(failed_cbor)?;
79*4185b066SAndroid Build Coastguard Worker         if outer_rsp.error_code != ErrorCode::Ok {
80*4185b066SAndroid Build Coastguard Worker             warn!("HAL: command {:?} failed: {:?}", code, outer_rsp.error_code);
81*4185b066SAndroid Build Coastguard Worker             return Err(errcode_to_binder(outer_rsp.error_code));
82*4185b066SAndroid Build Coastguard Worker         }
83*4185b066SAndroid Build Coastguard Worker         // On an OK outer response, the inner response should be populated with a message that
84*4185b066SAndroid Build Coastguard Worker         // matches the request.
85*4185b066SAndroid Build Coastguard Worker         outer_rsp.rsp.ok_or_else(|| {
86*4185b066SAndroid Build Coastguard Worker             binder::Status::new_service_specific_error(
87*4185b066SAndroid Build Coastguard Worker                 ErrorCode::InternalError as i32,
88*4185b066SAndroid Build Coastguard Worker                 Some(&CString::new("missing inner rsp in OK outer rsp".to_string()).unwrap()),
89*4185b066SAndroid Build Coastguard Worker             )
90*4185b066SAndroid Build Coastguard Worker         })
91*4185b066SAndroid Build Coastguard Worker     }
92*4185b066SAndroid Build Coastguard Worker }
93*4185b066SAndroid Build Coastguard Worker 
94*4185b066SAndroid Build Coastguard Worker /// Macro to extract a specific variant from a [`PerformOpRsp`].
95*4185b066SAndroid Build Coastguard Worker macro_rules! require_op_rsp {
96*4185b066SAndroid Build Coastguard Worker     { $rsp:expr => $variant:ident } => {
97*4185b066SAndroid Build Coastguard Worker         match $rsp {
98*4185b066SAndroid Build Coastguard Worker             PerformOpRsp::$variant(rsp) => Ok(rsp),
99*4185b066SAndroid Build Coastguard Worker             _ => Err(failed_cbor(CborError::UnexpectedItem("wrong rsp code", "rsp code"))),
100*4185b066SAndroid Build Coastguard Worker         }
101*4185b066SAndroid Build Coastguard Worker     }
102*4185b066SAndroid Build Coastguard Worker }
103*4185b066SAndroid Build Coastguard Worker 
104*4185b066SAndroid Build Coastguard Worker impl<T: SerializedChannel> binder::Interface for AuthGraphService<T> {}
105*4185b066SAndroid Build Coastguard Worker 
106*4185b066SAndroid Build Coastguard Worker /// Implement the `IAuthGraphKeyExchange` interface by translating incoming method invocations
107*4185b066SAndroid Build Coastguard Worker /// into request messages, which are passed to the TA.  The corresponding response from the TA
108*4185b066SAndroid Build Coastguard Worker /// is then parsed and the return values extracted.
109*4185b066SAndroid Build Coastguard Worker impl<T: SerializedChannel> IAuthGraphKeyExchange for AuthGraphService<T> {
create(&self) -> binder::Result<SessionInitiationInfo>110*4185b066SAndroid Build Coastguard Worker     fn create(&self) -> binder::Result<SessionInitiationInfo> {
111*4185b066SAndroid Build Coastguard Worker         let req = PerformOpReq::Create(wire::CreateRequest {});
112*4185b066SAndroid Build Coastguard Worker         let wire::CreateResponse { ret } = require_op_rsp!(self.execute_req(req)? => Create)?;
113*4185b066SAndroid Build Coastguard Worker         Ok(ret.innto())
114*4185b066SAndroid Build Coastguard Worker     }
init( &self, peer_pub_key: &PubKey, peer_id: &Identity, peer_nonce: &[u8], peer_version: i32, ) -> binder::Result<KeInitResult>115*4185b066SAndroid Build Coastguard Worker     fn init(
116*4185b066SAndroid Build Coastguard Worker         &self,
117*4185b066SAndroid Build Coastguard Worker         peer_pub_key: &PubKey,
118*4185b066SAndroid Build Coastguard Worker         peer_id: &Identity,
119*4185b066SAndroid Build Coastguard Worker         peer_nonce: &[u8],
120*4185b066SAndroid Build Coastguard Worker         peer_version: i32,
121*4185b066SAndroid Build Coastguard Worker     ) -> binder::Result<KeInitResult> {
122*4185b066SAndroid Build Coastguard Worker         let req = PerformOpReq::Init(wire::InitRequest {
123*4185b066SAndroid Build Coastguard Worker             peer_pub_key: unsigned_pub_key(peer_pub_key)?,
124*4185b066SAndroid Build Coastguard Worker             peer_id: peer_id.identity.to_vec(),
125*4185b066SAndroid Build Coastguard Worker             peer_nonce: peer_nonce.to_vec(),
126*4185b066SAndroid Build Coastguard Worker             peer_version,
127*4185b066SAndroid Build Coastguard Worker         });
128*4185b066SAndroid Build Coastguard Worker         let wire::InitResponse { ret } = require_op_rsp!(self.execute_req(req)? => Init)?;
129*4185b066SAndroid Build Coastguard Worker         Ok(ret.innto())
130*4185b066SAndroid Build Coastguard Worker     }
131*4185b066SAndroid Build Coastguard Worker 
finish( &self, peer_pub_key: &PubKey, peer_id: &Identity, peer_signature: &SessionIdSignature, peer_nonce: &[u8], peer_version: i32, own_key: &Key, ) -> binder::Result<SessionInfo>132*4185b066SAndroid Build Coastguard Worker     fn finish(
133*4185b066SAndroid Build Coastguard Worker         &self,
134*4185b066SAndroid Build Coastguard Worker         peer_pub_key: &PubKey,
135*4185b066SAndroid Build Coastguard Worker         peer_id: &Identity,
136*4185b066SAndroid Build Coastguard Worker         peer_signature: &SessionIdSignature,
137*4185b066SAndroid Build Coastguard Worker         peer_nonce: &[u8],
138*4185b066SAndroid Build Coastguard Worker         peer_version: i32,
139*4185b066SAndroid Build Coastguard Worker         own_key: &Key,
140*4185b066SAndroid Build Coastguard Worker     ) -> binder::Result<SessionInfo> {
141*4185b066SAndroid Build Coastguard Worker         let req = PerformOpReq::Finish(wire::FinishRequest {
142*4185b066SAndroid Build Coastguard Worker             peer_pub_key: unsigned_pub_key(peer_pub_key)?,
143*4185b066SAndroid Build Coastguard Worker             peer_id: peer_id.identity.to_vec(),
144*4185b066SAndroid Build Coastguard Worker             peer_signature: peer_signature.signature.to_vec(),
145*4185b066SAndroid Build Coastguard Worker             peer_nonce: peer_nonce.to_vec(),
146*4185b066SAndroid Build Coastguard Worker             peer_version,
147*4185b066SAndroid Build Coastguard Worker             own_key: own_key.clone().try_innto()?,
148*4185b066SAndroid Build Coastguard Worker         });
149*4185b066SAndroid Build Coastguard Worker         let wire::FinishResponse { ret } = require_op_rsp!(self.execute_req(req)? => Finish)?;
150*4185b066SAndroid Build Coastguard Worker         Ok(ret.innto())
151*4185b066SAndroid Build Coastguard Worker     }
152*4185b066SAndroid Build Coastguard Worker 
authenticationComplete( &self, peer_signature: &SessionIdSignature, shared_keys: &[Arc; 2], ) -> binder::Result<[Arc; 2]>153*4185b066SAndroid Build Coastguard Worker     fn authenticationComplete(
154*4185b066SAndroid Build Coastguard Worker         &self,
155*4185b066SAndroid Build Coastguard Worker         peer_signature: &SessionIdSignature,
156*4185b066SAndroid Build Coastguard Worker         shared_keys: &[Arc; 2],
157*4185b066SAndroid Build Coastguard Worker     ) -> binder::Result<[Arc; 2]> {
158*4185b066SAndroid Build Coastguard Worker         let req = PerformOpReq::AuthenticationComplete(wire::AuthenticationCompleteRequest {
159*4185b066SAndroid Build Coastguard Worker             peer_signature: peer_signature.signature.to_vec(),
160*4185b066SAndroid Build Coastguard Worker             shared_keys: [shared_keys[0].arc.to_vec(), shared_keys[1].arc.to_vec()],
161*4185b066SAndroid Build Coastguard Worker         });
162*4185b066SAndroid Build Coastguard Worker         let wire::AuthenticationCompleteResponse { ret } =
163*4185b066SAndroid Build Coastguard Worker             require_op_rsp!(self.execute_req(req)? => AuthenticationComplete)?;
164*4185b066SAndroid Build Coastguard Worker         let [arc0, arc1] = ret;
165*4185b066SAndroid Build Coastguard Worker         Ok([Arc { arc: arc0 }, Arc { arc: arc1 }])
166*4185b066SAndroid Build Coastguard Worker     }
167*4185b066SAndroid Build Coastguard Worker }
168*4185b066SAndroid Build Coastguard Worker 
169*4185b066SAndroid Build Coastguard Worker /// Extract (and require) an unsigned public key as bytes from a [`PubKey`].
unsigned_pub_key(pub_key: &PubKey) -> binder::Result<Vec<u8>>170*4185b066SAndroid Build Coastguard Worker fn unsigned_pub_key(pub_key: &PubKey) -> binder::Result<Vec<u8>> {
171*4185b066SAndroid Build Coastguard Worker     match pub_key {
172*4185b066SAndroid Build Coastguard Worker         PubKey::PlainKey(key) => Ok(key.plainPubKey.to_vec()),
173*4185b066SAndroid Build Coastguard Worker         PubKey::SignedKey(_) => Err(binder::Status::new_exception(
174*4185b066SAndroid Build Coastguard Worker             binder::ExceptionCode::ILLEGAL_ARGUMENT,
175*4185b066SAndroid Build Coastguard Worker             Some(&CString::new("expected unsigned public key").unwrap()),
176*4185b066SAndroid Build Coastguard Worker         )),
177*4185b066SAndroid Build Coastguard Worker     }
178*4185b066SAndroid Build Coastguard Worker }
179