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