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