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