From 6c09af215df9f033389fcb5cff33fa9a333062f4 Mon Sep 17 00:00:00 2001 From: David Drysdale Date: Mon, 6 Nov 2023 09:57:10 +0000 Subject: [PATCH] AuthGraph: move code into library Use the core library's new service implementation, which wraps a channel to the TA. In this nonsecure case, the TA is local in-process, so use the core library's AuthGraphTa, and implement the SerializedChannel as just a direct invocation of the TA. Move this code into a _nonsecure library, so the main.rs just has the code needed to start the executable and register the service. Test: VtsAidlAuthGraphSessionTest Bug: 284470121 Change-Id: I738d3876872a8cd248f0ebec708676d1173b6e37 --- security/authgraph/default/Android.bp | 24 +++- security/authgraph/default/src/lib.rs | 81 +++++++++++++ security/authgraph/default/src/main.rs | 162 +------------------------ 3 files changed, 107 insertions(+), 160 deletions(-) create mode 100644 security/authgraph/default/src/lib.rs diff --git a/security/authgraph/default/Android.bp b/security/authgraph/default/Android.bp index c0f2106c3e..ac67136ada 100644 --- a/security/authgraph/default/Android.bp +++ b/security/authgraph/default/Android.bp @@ -22,6 +22,26 @@ package { default_applicable_licenses: ["hardware_interfaces_license"], } +rust_library { + name: "libauthgraph_nonsecure", + crate_name: "authgraph_nonsecure", + defaults: [ + "authgraph_use_latest_hal_aidl_rust", + ], + vendor_available: true, + rustlibs: [ + "libandroid_logger", + "libauthgraph_boringssl", + "libauthgraph_core", + "libauthgraph_hal", + "libbinder_rs", + "liblibc", + "liblog_rust", + ], + srcs: ["src/lib.rs"], + +} + rust_binary { name: "android.hardware.security.authgraph-service.nonsecure", relative_install_path: "hw", @@ -33,10 +53,8 @@ rust_binary { ], rustlibs: [ "libandroid_logger", - "libauthgraph_core", - "libauthgraph_boringssl", "libauthgraph_hal", - "libauthgraph_wire", + "libauthgraph_nonsecure", "libbinder_rs", "liblibc", "liblog_rust", diff --git a/security/authgraph/default/src/lib.rs b/security/authgraph/default/src/lib.rs new file mode 100644 index 0000000000..4cd0cb74ae --- /dev/null +++ b/security/authgraph/default/src/lib.rs @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! Common functionality for non-secure/testing instance of AuthGraph. + +use authgraph_boringssl as boring; +use authgraph_core::{ + key::MillisecondsSinceEpoch, + ta::{AuthGraphTa, Role}, + traits, +}; +use authgraph_hal::channel::SerializedChannel; +use std::sync::{Arc, Mutex}; +use std::time::Instant; + +/// Monotonic clock with an epoch that starts at the point of construction. +/// (This makes it unsuitable for use outside of testing, because the epoch +/// will not match that of any other component.) +pub struct StdClock(Instant); + +impl Default for StdClock { + fn default() -> Self { + Self(Instant::now()) + } +} + +impl traits::MonotonicClock for StdClock { + fn now(&self) -> MillisecondsSinceEpoch { + let millis: i64 = self + .0 + .elapsed() + .as_millis() + .try_into() + .expect("failed to fit timestamp in i64"); + MillisecondsSinceEpoch(millis) + } +} + +/// Implementation of the AuthGraph TA that runs locally in-process (and which is therefore +/// insecure). +pub struct LocalTa { + ta: Arc>, +} + +impl LocalTa { + /// Create a new instance. + pub fn new() -> Self { + Self { + ta: Arc::new(Mutex::new(AuthGraphTa::new( + boring::trait_impls( + Box::::default(), + Some(Box::new(StdClock::default())), + ), + Role::Both, + ))), + } + } +} + +/// Pretend to be a serialized channel to the TA, but actually just directly invoke the TA with +/// incoming requests. +impl SerializedChannel for LocalTa { + const MAX_SIZE: usize = usize::MAX; + + fn execute(&mut self, req_data: &[u8]) -> binder::Result> { + Ok(self.ta.lock().unwrap().process(req_data)) + } +} diff --git a/security/authgraph/default/src/main.rs b/security/authgraph/default/src/main.rs index dc4a8e41fd..873eb4eb77 100644 --- a/security/authgraph/default/src/main.rs +++ b/security/authgraph/default/src/main.rs @@ -22,18 +22,10 @@ //! expose an entrypoint that allowed retrieval of the specific IAuthGraphKeyExchange instance that //! is correlated with the component). -use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph::{ - Arc::Arc, IAuthGraphKeyExchange::BnAuthGraphKeyExchange, - IAuthGraphKeyExchange::IAuthGraphKeyExchange, Identity::Identity, KeInitResult::KeInitResult, - Key::Key, PubKey::PubKey, SessionIdSignature::SessionIdSignature, SessionInfo::SessionInfo, - SessionInitiationInfo::SessionInitiationInfo, -}; -use authgraph_boringssl as boring; -use authgraph_core::{key::MillisecondsSinceEpoch, keyexchange as ke, traits}; -use authgraph_hal::{errcode_to_binder, Innto, TryInnto}; +use authgraph_hal::service; +use authgraph_nonsecure::LocalTa; use log::{error, info}; -use std::ffi::CString; -use std::sync::Mutex; +use std::sync::{Arc, Mutex}; static SERVICE_NAME: &str = "android.hardware.security.authgraph.IAuthGraphKeyExchange"; static SERVICE_INSTANCE: &str = "nonsecure"; @@ -73,7 +65,8 @@ fn inner_main() -> Result<(), HalServiceError> { binder::ProcessState::start_thread_pool(); // Register the service - let service = AuthGraphService::new_as_binder(); + let local_ta = LocalTa::new(); + let service = service::AuthGraphService::new_as_binder(Arc::new(Mutex::new(local_ta))); let service_name = format!("{}/{}", SERVICE_NAME, SERVICE_INSTANCE); binder::add_service(&service_name, service.as_binder()).map_err(|e| { format!( @@ -87,148 +80,3 @@ fn inner_main() -> Result<(), HalServiceError> { info!("AuthGraph HAL service is terminating."); // should not reach here Ok(()) } - -/// Non-secure implementation of the AuthGraph key exchange service. -struct AuthGraphService { - imp: Mutex, -} - -impl AuthGraphService { - /// Create a new instance. - fn new() -> Self { - Self { - imp: Mutex::new(traits::TraitImpl { - aes_gcm: Box::new(boring::BoringAes), - ecdh: Box::new(boring::BoringEcDh), - ecdsa: Box::new(boring::BoringEcDsa), - hmac: Box::new(boring::BoringHmac), - hkdf: Box::new(boring::BoringHkdf), - sha256: Box::new(boring::BoringSha256), - rng: Box::new(boring::BoringRng), - device: Box::::default(), - clock: Some(Box::new(StdClock)), - }), - } - } - - /// Create a new instance wrapped in a proxy object. - pub fn new_as_binder() -> binder::Strong { - BnAuthGraphKeyExchange::new_binder(Self::new(), binder::BinderFeatures::default()) - } -} - -impl binder::Interface for AuthGraphService {} - -/// Extract (and require) an unsigned public key as bytes from a [`PubKey`]. -fn unsigned_pub_key(pub_key: &PubKey) -> binder::Result<&[u8]> { - match pub_key { - PubKey::PlainKey(key) => Ok(&key.plainPubKey), - PubKey::SignedKey(_) => Err(binder::Status::new_exception( - binder::ExceptionCode::ILLEGAL_ARGUMENT, - Some(&CString::new("expected unsigned public key").unwrap()), - )), - } -} - -fn err_to_binder(err: authgraph_core::error::Error) -> binder::Status { - if err.0 != authgraph_wire::ErrorCode::Ok && !err.1.is_empty() { - error!("failure {:?} message: '{}'", err.0, err.1); - } - errcode_to_binder(err.0) -} - -/// This nonsecure implementation of the AuthGraph HAL interface directly calls the AuthGraph -/// reference implementation library code; a real implementation requires the AuthGraph -/// code to run in a secure environment, not within Android. -impl IAuthGraphKeyExchange for AuthGraphService { - fn create(&self) -> binder::Result { - info!("create()"); - let mut imp = self.imp.lock().unwrap(); - let info = ke::create(&mut *imp).map_err(err_to_binder)?; - Ok(info.innto()) - } - fn init( - &self, - peer_pub_key: &PubKey, - peer_id: &Identity, - peer_nonce: &[u8], - peer_version: i32, - ) -> binder::Result { - info!("init(v={peer_version})"); - let mut imp = self.imp.lock().unwrap(); - let peer_pub_key = unsigned_pub_key(peer_pub_key)?; - let result = ke::init( - &mut *imp, - peer_pub_key, - &peer_id.identity, - &peer_nonce, - peer_version, - ) - .map_err(err_to_binder)?; - Ok(result.innto()) - } - - fn finish( - &self, - peer_pub_key: &PubKey, - peer_id: &Identity, - peer_signature: &SessionIdSignature, - peer_nonce: &[u8], - peer_version: i32, - own_key: &Key, - ) -> binder::Result { - info!("finish(v={peer_version})"); - let mut imp = self.imp.lock().unwrap(); - let peer_pub_key = unsigned_pub_key(peer_pub_key)?; - let own_key: Key = own_key.clone(); - let own_key: authgraph_core::key::Key = own_key.try_innto()?; - let session_info = ke::finish( - &mut *imp, - peer_pub_key, - &peer_id.identity, - &peer_signature.signature, - &peer_nonce, - peer_version, - own_key, - ) - .map_err(err_to_binder)?; - Ok(session_info.innto()) - } - - fn authenticationComplete( - &self, - peer_signature: &SessionIdSignature, - shared_keys: &[Arc; 2], - ) -> binder::Result<[Arc; 2]> { - info!("authComplete()"); - let mut imp = self.imp.lock().unwrap(); - let shared_keys = [shared_keys[0].arc.clone(), shared_keys[1].arc.clone()]; - let arcs = ke::authentication_complete(&mut *imp, &peer_signature.signature, shared_keys) - .map_err(err_to_binder)?; - Ok(arcs.map(|arc| Arc { arc })) - } -} - -/// Monotonic clock. -#[derive(Default)] -pub struct StdClock; - -impl traits::MonotonicClock for StdClock { - fn now(&self) -> authgraph_core::key::MillisecondsSinceEpoch { - let mut time = libc::timespec { - tv_sec: 0, // libc::time_t - tv_nsec: 0, // libc::c_long - }; - let rc = - // Safety: `time` is a valid structure. - unsafe { libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut time as *mut libc::timespec) }; - if rc < 0 { - log::warn!("failed to get time!"); - return MillisecondsSinceEpoch(0); - } - // The types in `libc::timespec` may be different on different architectures, - // so allow conversion to `i64`. - #[allow(clippy::unnecessary_cast)] - MillisecondsSinceEpoch((time.tv_sec as i64 * 1000) + (time.tv_nsec as i64 / 1000 / 1000)) - } -}