blob: dc4a8e41fdd1af250c6fe0732e2fe3d5b80a3333 [file] [log] [blame]
David Drysdale7fd838c2023-10-05 13:07:28 +01001/*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! Default implementation of the AuthGraph key exchange HAL.
18//!
19//! This implementation of the HAL is only intended to allow testing and policy compliance. A real
20//! implementation of the AuthGraph HAL would be implemented in a secure environment, and would not
21//! be independently registered with service manager (a secure component that uses AuthGraph would
22//! expose an entrypoint that allowed retrieval of the specific IAuthGraphKeyExchange instance that
23//! is correlated with the component).
24
25use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph::{
26 Arc::Arc, IAuthGraphKeyExchange::BnAuthGraphKeyExchange,
27 IAuthGraphKeyExchange::IAuthGraphKeyExchange, Identity::Identity, KeInitResult::KeInitResult,
28 Key::Key, PubKey::PubKey, SessionIdSignature::SessionIdSignature, SessionInfo::SessionInfo,
29 SessionInitiationInfo::SessionInitiationInfo,
30};
31use authgraph_boringssl as boring;
32use authgraph_core::{key::MillisecondsSinceEpoch, keyexchange as ke, traits};
David Drysdalef2117ff2023-11-03 12:18:24 +000033use authgraph_hal::{errcode_to_binder, Innto, TryInnto};
David Drysdale7fd838c2023-10-05 13:07:28 +010034use log::{error, info};
35use std::ffi::CString;
36use std::sync::Mutex;
37
38static SERVICE_NAME: &str = "android.hardware.security.authgraph.IAuthGraphKeyExchange";
39static SERVICE_INSTANCE: &str = "nonsecure";
40
41/// Local error type for failures in the HAL service.
42#[derive(Debug, Clone)]
43struct HalServiceError(String);
44
45impl From<String> for HalServiceError {
46 fn from(s: String) -> Self {
47 Self(s)
48 }
49}
50
51fn main() {
52 if let Err(e) = inner_main() {
53 panic!("HAL service failed: {:?}", e);
54 }
55}
56
57fn inner_main() -> Result<(), HalServiceError> {
58 // Initialize Android logging.
59 android_logger::init_once(
60 android_logger::Config::default()
61 .with_tag("authgraph-hal-nonsecure")
62 .with_min_level(log::Level::Info)
63 .with_log_id(android_logger::LogId::System),
64 );
65 // Redirect panic messages to logcat.
66 std::panic::set_hook(Box::new(|panic_info| {
67 error!("{}", panic_info);
68 }));
69
70 info!("Insecure AuthGraph key exchange HAL service is starting.");
71
72 info!("Starting thread pool now.");
73 binder::ProcessState::start_thread_pool();
74
75 // Register the service
76 let service = AuthGraphService::new_as_binder();
77 let service_name = format!("{}/{}", SERVICE_NAME, SERVICE_INSTANCE);
78 binder::add_service(&service_name, service.as_binder()).map_err(|e| {
79 format!(
80 "Failed to register service {} because of {:?}.",
81 service_name, e
82 )
83 })?;
84
85 info!("Successfully registered AuthGraph HAL services.");
86 binder::ProcessState::join_thread_pool();
87 info!("AuthGraph HAL service is terminating."); // should not reach here
88 Ok(())
89}
90
91/// Non-secure implementation of the AuthGraph key exchange service.
92struct AuthGraphService {
93 imp: Mutex<traits::TraitImpl>,
94}
95
96impl AuthGraphService {
97 /// Create a new instance.
98 fn new() -> Self {
99 Self {
100 imp: Mutex::new(traits::TraitImpl {
101 aes_gcm: Box::new(boring::BoringAes),
102 ecdh: Box::new(boring::BoringEcDh),
103 ecdsa: Box::new(boring::BoringEcDsa),
104 hmac: Box::new(boring::BoringHmac),
105 hkdf: Box::new(boring::BoringHkdf),
106 sha256: Box::new(boring::BoringSha256),
107 rng: Box::new(boring::BoringRng),
108 device: Box::<boring::test_device::AgDevice>::default(),
109 clock: Some(Box::new(StdClock)),
110 }),
111 }
112 }
113
114 /// Create a new instance wrapped in a proxy object.
115 pub fn new_as_binder() -> binder::Strong<dyn IAuthGraphKeyExchange> {
116 BnAuthGraphKeyExchange::new_binder(Self::new(), binder::BinderFeatures::default())
117 }
118}
119
120impl binder::Interface for AuthGraphService {}
121
122/// Extract (and require) an unsigned public key as bytes from a [`PubKey`].
123fn unsigned_pub_key(pub_key: &PubKey) -> binder::Result<&[u8]> {
124 match pub_key {
125 PubKey::PlainKey(key) => Ok(&key.plainPubKey),
126 PubKey::SignedKey(_) => Err(binder::Status::new_exception(
127 binder::ExceptionCode::ILLEGAL_ARGUMENT,
128 Some(&CString::new("expected unsigned public key").unwrap()),
129 )),
130 }
131}
132
David Drysdalef2117ff2023-11-03 12:18:24 +0000133fn err_to_binder(err: authgraph_core::error::Error) -> binder::Status {
134 if err.0 != authgraph_wire::ErrorCode::Ok && !err.1.is_empty() {
135 error!("failure {:?} message: '{}'", err.0, err.1);
136 }
137 errcode_to_binder(err.0)
138}
139
David Drysdale7fd838c2023-10-05 13:07:28 +0100140/// This nonsecure implementation of the AuthGraph HAL interface directly calls the AuthGraph
141/// reference implementation library code; a real implementation requires the AuthGraph
142/// code to run in a secure environment, not within Android.
143impl IAuthGraphKeyExchange for AuthGraphService {
144 fn create(&self) -> binder::Result<SessionInitiationInfo> {
145 info!("create()");
146 let mut imp = self.imp.lock().unwrap();
147 let info = ke::create(&mut *imp).map_err(err_to_binder)?;
148 Ok(info.innto())
149 }
150 fn init(
151 &self,
152 peer_pub_key: &PubKey,
153 peer_id: &Identity,
154 peer_nonce: &[u8],
155 peer_version: i32,
156 ) -> binder::Result<KeInitResult> {
157 info!("init(v={peer_version})");
158 let mut imp = self.imp.lock().unwrap();
159 let peer_pub_key = unsigned_pub_key(peer_pub_key)?;
160 let result = ke::init(
161 &mut *imp,
162 peer_pub_key,
163 &peer_id.identity,
164 &peer_nonce,
165 peer_version,
166 )
167 .map_err(err_to_binder)?;
168 Ok(result.innto())
169 }
170
171 fn finish(
172 &self,
173 peer_pub_key: &PubKey,
174 peer_id: &Identity,
175 peer_signature: &SessionIdSignature,
176 peer_nonce: &[u8],
177 peer_version: i32,
178 own_key: &Key,
179 ) -> binder::Result<SessionInfo> {
180 info!("finish(v={peer_version})");
181 let mut imp = self.imp.lock().unwrap();
182 let peer_pub_key = unsigned_pub_key(peer_pub_key)?;
183 let own_key: Key = own_key.clone();
184 let own_key: authgraph_core::key::Key = own_key.try_innto()?;
185 let session_info = ke::finish(
186 &mut *imp,
187 peer_pub_key,
188 &peer_id.identity,
189 &peer_signature.signature,
190 &peer_nonce,
191 peer_version,
192 own_key,
193 )
194 .map_err(err_to_binder)?;
195 Ok(session_info.innto())
196 }
197
198 fn authenticationComplete(
199 &self,
200 peer_signature: &SessionIdSignature,
201 shared_keys: &[Arc; 2],
202 ) -> binder::Result<[Arc; 2]> {
203 info!("authComplete()");
204 let mut imp = self.imp.lock().unwrap();
205 let shared_keys = [shared_keys[0].arc.clone(), shared_keys[1].arc.clone()];
206 let arcs = ke::authentication_complete(&mut *imp, &peer_signature.signature, shared_keys)
207 .map_err(err_to_binder)?;
208 Ok(arcs.map(|arc| Arc { arc }))
209 }
210}
211
212/// Monotonic clock.
213#[derive(Default)]
214pub struct StdClock;
215
216impl traits::MonotonicClock for StdClock {
217 fn now(&self) -> authgraph_core::key::MillisecondsSinceEpoch {
218 let mut time = libc::timespec {
219 tv_sec: 0, // libc::time_t
220 tv_nsec: 0, // libc::c_long
221 };
222 let rc =
223 // Safety: `time` is a valid structure.
224 unsafe { libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut time as *mut libc::timespec) };
225 if rc < 0 {
226 log::warn!("failed to get time!");
227 return MillisecondsSinceEpoch(0);
228 }
229 // The types in `libc::timespec` may be different on different architectures,
230 // so allow conversion to `i64`.
231 #[allow(clippy::unnecessary_cast)]
232 MillisecondsSinceEpoch((time.tv_sec as i64 * 1000) + (time.tv_nsec as i64 / 1000 / 1000))
233 }
234}