diff --git a/security/authgraph/aidl/vts/functional/lib.rs b/security/authgraph/aidl/vts/functional/lib.rs index 7b9b2b98be..da3fa1cec6 100644 --- a/security/authgraph/aidl/vts/functional/lib.rs +++ b/security/authgraph/aidl/vts/functional/lib.rs @@ -24,22 +24,19 @@ use android_hardware_security_authgraph::aidl::android::hardware::security::auth PlainPubKey::PlainPubKey, PubKey::PubKey, SessionIdSignature::SessionIdSignature, }; use authgraph_boringssl as boring; -use authgraph_core::keyexchange as ke; -use authgraph_core::{arc, key, traits}; -use authgraph_nonsecure::StdClock; +use authgraph_core::{error::Error as AgError, keyexchange as ke}; use coset::CborSerializable; pub mod sink; pub mod source; -/// Return a collection of AuthGraph trait implementations suitable for testing. -pub fn test_impls() -> traits::TraitImpl { - // Note that the local implementation is using a clock with a potentially different epoch than - // the implementation under test. - boring::trait_impls( +/// Return an AuthGraphParticipant suitable for testing. +pub fn test_ag_participant() -> Result { + Ok(ke::AuthGraphParticipant::new( + boring::crypto_trait_impls(), Box::::default(), - Some(Box::new(StdClock::default())), - ) + ke::MAX_OPENED_SESSIONS, + )?) } fn build_plain_pub_key(pub_key: &Option>) -> PubKey { @@ -56,14 +53,6 @@ fn extract_plain_pub_key(pub_key: &Option) -> &PlainPubKey { } } -fn verification_key_from_identity(impls: &traits::TraitImpl, identity: &[u8]) -> key::EcVerifyKey { - let identity = key::Identity::from_slice(identity).expect("invalid identity CBOR"); - impls - .device - .process_peer_cert_chain(&identity.cert_chain, &*impls.ecdsa) - .expect("failed to extract signing key") -} - fn vec_to_identity(data: &[u8]) -> Identity { Identity { identity: data.to_vec(), @@ -75,26 +64,3 @@ fn vec_to_signature(data: &[u8]) -> SessionIdSignature { signature: data.to_vec(), } } - -/// Decrypt a pair of AES-256 keys encrypted with the AuthGraph PBK. -pub fn decipher_aes_keys(imp: &traits::TraitImpl, arc: &[Vec; 2]) -> [key::AesKey; 2] { - [ - decipher_aes_key(imp, &arc[0]), - decipher_aes_key(imp, &arc[1]), - ] -} - -/// Decrypt an AES-256 key encrypted with the AuthGraph PBK. -pub fn decipher_aes_key(imp: &traits::TraitImpl, arc: &[u8]) -> key::AesKey { - let pbk = imp.device.get_per_boot_key().expect("no PBK available"); - let arc::ArcContent { - payload, - protected_headers: _, - unprotected_headers: _, - } = arc::decipher_arc(&pbk, arc, &*imp.aes_gcm).expect("failed to decrypt arc"); - assert_eq!(payload.0.len(), 32); - let mut key = key::AesKey([0; 32]); - key.0.copy_from_slice(&payload.0); - assert_ne!(key.0, [0; 32], "agreed AES-256 key should be non-zero"); - key -} diff --git a/security/authgraph/aidl/vts/functional/role_test.rs b/security/authgraph/aidl/vts/functional/role_test.rs index e95361ae07..71a2fae941 100644 --- a/security/authgraph/aidl/vts/functional/role_test.rs +++ b/security/authgraph/aidl/vts/functional/role_test.rs @@ -48,31 +48,31 @@ macro_rules! require_nonsecure { #[test] fn test_nonsecure_source_mainline() { - let mut impls = vts::test_impls(); - vts::source::test_mainline(&mut impls, require_nonsecure!()); + let mut sink = vts::test_ag_participant().expect("failed to create a local sink"); + vts::source::test_mainline(&mut sink, require_nonsecure!()); } #[test] fn test_nonsecure_source_corrupt_sig() { - let mut impls = vts::test_impls(); - vts::source::test_corrupt_sig(&mut impls, require_nonsecure!()); + let mut sink = vts::test_ag_participant().expect("failed to create a local sink"); + vts::source::test_corrupt_sig(&mut sink, require_nonsecure!()); } #[test] fn test_nonsecure_source_corrupt_keys() { - let mut impls = vts::test_impls(); - vts::source::test_corrupt_key(&mut impls, require_nonsecure!()); + let mut sink = vts::test_ag_participant().expect("failed to create a local sink"); + vts::source::test_corrupt_key(&mut sink, require_nonsecure!()); } #[test] fn test_nonsecure_sink_mainline() { - let mut impls = vts::test_impls(); - vts::sink::test_mainline(&mut impls, require_nonsecure!()); + let mut source = vts::test_ag_participant().expect("failed to create a local source"); + vts::sink::test_mainline(&mut source, require_nonsecure!()); } #[test] fn test_nonsecure_sink_corrupt_sig() { - let mut impls = vts::test_impls(); - vts::sink::test_corrupt_sig(&mut impls, require_nonsecure!()); + let mut source = vts::test_ag_participant().expect("failed to create a local source"); + vts::sink::test_corrupt_sig(&mut source, require_nonsecure!()); } #[test] fn test_nonsecure_sink_corrupt_keys() { - let mut impls = vts::test_impls(); - vts::sink::test_corrupt_keys(&mut impls, require_nonsecure!()); + let mut source = vts::test_ag_participant().expect("failed to create a local source"); + vts::sink::test_corrupt_keys(&mut source, require_nonsecure!()); } diff --git a/security/authgraph/aidl/vts/functional/sink.rs b/security/authgraph/aidl/vts/functional/sink.rs index 5c81593d69..bb357b8815 100644 --- a/security/authgraph/aidl/vts/functional/sink.rs +++ b/security/authgraph/aidl/vts/functional/sink.rs @@ -16,23 +16,28 @@ //! VTS tests for sinks use super::*; -use authgraph_core::traits; +use authgraph_core::{key, keyexchange as ke}; /// Run AuthGraph tests against the provided sink, using a local test source implementation. -pub fn test(impls: &mut traits::TraitImpl, sink: binder::Strong) { - test_mainline(impls, sink.clone()); - test_corrupt_sig(impls, sink.clone()); - test_corrupt_keys(impls, sink); +pub fn test( + local_source: &mut ke::AuthGraphParticipant, + sink: binder::Strong, +) { + test_mainline(local_source, sink.clone()); + test_corrupt_sig(local_source, sink.clone()); + test_corrupt_keys(local_source, sink); } /// Perform mainline AuthGraph key exchange with the provided sink and local implementation. /// Return the agreed AES keys in plaintext. pub fn test_mainline( - impls: &mut traits::TraitImpl, + local_source: &mut ke::AuthGraphParticipant, sink: binder::Strong, ) -> [key::AesKey; 2] { // Step 1: create an ephemeral ECDH key at the (local) source. - let source_init_info = ke::create(impls).expect("failed to create() with local impl"); + let source_init_info = local_source + .create() + .expect("failed to create() with local impl"); // Step 2: pass the source's ECDH public key and other session info to the (remote) sink. let init_result = sink @@ -50,40 +55,43 @@ pub fn test_mainline( assert!(!sink_info.sessionId.is_empty()); // The AuthGraph core library will verify the session ID signature, but do it here too. - let sink_verification_key = - verification_key_from_identity(&impls, &sink_init_info.identity.identity); - ke::verify_signature_on_session_id( - &sink_verification_key, - &sink_info.sessionId, - &sink_info.signature.signature, - &*impls.ecdsa, - ) - .expect("failed verification of signed session ID"); + let sink_verification_key = local_source + .peer_verification_key_from_identity(&sink_init_info.identity.identity) + .expect("failed to get peer verification from identity"); + local_source + .verify_signature_on_session_id( + &sink_verification_key, + &sink_info.sessionId, + &sink_info.signature.signature, + ) + .expect("failed verification of signed session ID"); // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it // can calculate the same pair of symmetric keys. - let source_info = ke::finish( - impls, - &sink_pub_key.plainPubKey, - &sink_init_info.identity.identity, - &sink_info.signature.signature, - &sink_init_info.nonce, - sink_init_info.version, - source_init_info.ke_key, - ) - .expect("failed to finish() with local impl"); + let source_info = local_source + .finish( + &sink_pub_key.plainPubKey, + &sink_init_info.identity.identity, + &sink_info.signature.signature, + &sink_init_info.nonce, + sink_init_info.version, + source_init_info.ke_key, + ) + .expect("failed to finish() with local impl"); assert!(!source_info.session_id.is_empty()); // The AuthGraph core library will verify the session ID signature, but do it here too. - let source_verification_key = - verification_key_from_identity(&impls, &source_init_info.identity); - ke::verify_signature_on_session_id( - &source_verification_key, - &source_info.session_id, - &source_info.session_id_signature, - &*impls.ecdsa, - ) - .expect("failed verification of signed session ID"); + let source_verification_key = key::Identity::from_slice(&source_init_info.identity) + .expect("invalid identity CBOR") + .cert_chain + .root_key; + local_source + .verify_signature_on_session_id( + &source_verification_key, + &source_info.session_id, + &source_info.session_id_signature, + ) + .expect("failed verification of signed session ID"); // Both ends should agree on the session ID. assert_eq!(source_info.session_id, sink_info.sessionId); @@ -96,19 +104,28 @@ pub fn test_mainline( &sink_info.sharedKeys, ) .expect("failed to authenticationComplete() with remote sink"); - // Decrypt and return the session keys. - decipher_aes_keys(&impls, &source_info.shared_keys) + let decrypted_shared_keys = local_source + .decipher_shared_keys_from_arcs(&source_info.shared_keys) + .expect("failed to decrypt shared key arcs") + .try_into(); + let decrypted_shared_keys_array = match decrypted_shared_keys { + Ok(array) => array, + Err(_) => panic!("wrong number of decrypted shared key arcs"), + }; + decrypted_shared_keys_array } /// Perform mainline AuthGraph key exchange with the provided sink, but provide an invalid /// session ID signature. pub fn test_corrupt_sig( - impls: &mut traits::TraitImpl, + local_source: &mut ke::AuthGraphParticipant, sink: binder::Strong, ) { // Step 1: create an ephemeral ECDH key at the (local) source. - let source_init_info = ke::create(impls).expect("failed to create() with local impl"); + let source_init_info = local_source + .create() + .expect("failed to create() with local impl"); // Step 2: pass the source's ECDH public key and other session info to the (remote) sink. let init_result = sink @@ -127,16 +144,16 @@ pub fn test_corrupt_sig( // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it // can calculate the same pair of symmetric keys. - let source_info = ke::finish( - impls, - &sink_pub_key.plainPubKey, - &sink_init_info.identity.identity, - &sink_info.signature.signature, - &sink_init_info.nonce, - sink_init_info.version, - source_init_info.ke_key, - ) - .expect("failed to finish() with local impl"); + let source_info = local_source + .finish( + &sink_pub_key.plainPubKey, + &sink_init_info.identity.identity, + &sink_info.signature.signature, + &sink_init_info.nonce, + sink_init_info.version, + source_init_info.ke_key, + ) + .expect("failed to finish() with local impl"); assert!(!source_info.session_id.is_empty()); // Build a corrupted version of the (local) source's session ID signature. @@ -158,11 +175,13 @@ pub fn test_corrupt_sig( /// Perform mainline AuthGraph key exchange with the provided sink, but provide an invalid /// Arc for the sink's key. pub fn test_corrupt_keys( - impls: &mut traits::TraitImpl, + local_source: &mut ke::AuthGraphParticipant, sink: binder::Strong, ) { // Step 1: create an ephemeral ECDH key at the (local) source. - let source_init_info = ke::create(impls).expect("failed to create() with local impl"); + let source_init_info = local_source + .create() + .expect("failed to create() with local impl"); // Step 2: pass the source's ECDH public key and other session info to the (remote) sink. let init_result = sink @@ -181,16 +200,16 @@ pub fn test_corrupt_keys( // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it // can calculate the same pair of symmetric keys. - let source_info = ke::finish( - impls, - &sink_pub_key.plainPubKey, - &sink_init_info.identity.identity, - &sink_info.signature.signature, - &sink_init_info.nonce, - sink_init_info.version, - source_init_info.ke_key, - ) - .expect("failed to finish() with local impl"); + let source_info = local_source + .finish( + &sink_pub_key.plainPubKey, + &sink_init_info.identity.identity, + &sink_info.signature.signature, + &sink_init_info.nonce, + sink_init_info.version, + source_init_info.ke_key, + ) + .expect("failed to finish() with local impl"); assert!(!source_info.session_id.is_empty()); // Deliberately corrupt the sink's shared key Arcs before returning them diff --git a/security/authgraph/aidl/vts/functional/source.rs b/security/authgraph/aidl/vts/functional/source.rs index 9aaaaee0d5..4178a99733 100644 --- a/security/authgraph/aidl/vts/functional/source.rs +++ b/security/authgraph/aidl/vts/functional/source.rs @@ -16,19 +16,22 @@ //! VTS tests for sources use super::*; -use authgraph_core::traits; +use authgraph_core::{key, keyexchange as ke}; /// Run AuthGraph tests against the provided source, using a local test sink implementation. -pub fn test(impls: &mut traits::TraitImpl, source: binder::Strong) { - test_mainline(impls, source.clone()); - test_corrupt_sig(impls, source.clone()); - test_corrupt_key(impls, source); +pub fn test( + local_sink: &mut ke::AuthGraphParticipant, + source: binder::Strong, +) { + test_mainline(local_sink, source.clone()); + test_corrupt_sig(local_sink, source.clone()); + test_corrupt_key(local_sink, source); } /// Perform mainline AuthGraph key exchange with the provided source. /// Return the agreed AES keys in plaintext. pub fn test_mainline( - impls: &mut traits::TraitImpl, + local_sink: &mut ke::AuthGraphParticipant, source: binder::Strong, ) -> [key::AesKey; 2] { // Step 1: create an ephemeral ECDH key at the (remote) source. @@ -40,14 +43,14 @@ pub fn test_mainline( let source_pub_key = extract_plain_pub_key(&source_init_info.key.pubKey); // Step 2: pass the source's ECDH public key and other session info to the (local) sink. - let init_result = ke::init( - impls, - &source_pub_key.plainPubKey, - &source_init_info.identity.identity, - &source_init_info.nonce, - source_init_info.version, - ) - .expect("failed to init() with local impl"); + let init_result = local_sink + .init( + &source_pub_key.plainPubKey, + &source_init_info.identity.identity, + &source_init_info.nonce, + source_init_info.version, + ) + .expect("failed to init() with local impl"); let sink_init_info = init_result.session_init_info; let sink_pub_key = sink_init_info .ke_key @@ -58,14 +61,17 @@ pub fn test_mainline( assert!(!sink_info.session_id.is_empty()); // The AuthGraph core library will verify the session ID signature, but do it here too. - let sink_verification_key = verification_key_from_identity(&impls, &sink_init_info.identity); - ke::verify_signature_on_session_id( - &sink_verification_key, - &sink_info.session_id, - &sink_info.session_id_signature, - &*impls.ecdsa, - ) - .expect("failed verification of signed session ID"); + let sink_verification_key = key::Identity::from_slice(&sink_init_info.identity) + .expect("invalid identity CBOR") + .cert_chain + .root_key; + local_sink + .verify_signature_on_session_id( + &sink_verification_key, + &sink_info.session_id, + &sink_info.session_id_signature, + ) + .expect("failed verification of signed session ID"); // Step 3: pass the sink's ECDH public key and other session info to the (remote) source, so it // can calculate the same pair of symmetric keys. @@ -86,36 +92,41 @@ pub fn test_mainline( assert!(!source_info.sessionId.is_empty()); // The AuthGraph core library will verify the session ID signature, but do it here too. - let source_verification_key = - verification_key_from_identity(&impls, &source_init_info.identity.identity); - ke::verify_signature_on_session_id( - &source_verification_key, - &source_info.sessionId, - &source_info.signature.signature, - &*impls.ecdsa, - ) - .expect("failed verification of signed session ID"); + let source_verification_key = local_sink + .peer_verification_key_from_identity(&source_init_info.identity.identity) + .expect("failed to get peer verification from identity"); + local_sink + .verify_signature_on_session_id( + &source_verification_key, + &source_info.sessionId, + &source_info.signature.signature, + ) + .expect("failed verification of signed session ID"); // Both ends should agree on the session ID. assert_eq!(source_info.sessionId, sink_info.session_id); // Step 4: pass the (remote) source's session ID signature back to the sink, so it can check it // and update the symmetric keys so they're marked as authentication complete. - let sink_arcs = ke::authentication_complete( - impls, - &source_info.signature.signature, - sink_info.shared_keys, - ) - .expect("failed to authenticationComplete() with local sink"); - + let sink_arcs = local_sink + .authentication_complete(&source_info.signature.signature, sink_info.shared_keys) + .expect("failed to authenticationComplete() with local sink"); // Decrypt and return the session keys. - decipher_aes_keys(&impls, &sink_arcs) + let decrypted_shared_keys = local_sink + .decipher_shared_keys_from_arcs(&sink_arcs) + .expect("failed to decrypt shared key arcs") + .try_into(); + let decrypted_shared_keys_array = match decrypted_shared_keys { + Ok(array) => array, + Err(_) => panic!("wrong number of decrypted shared key arcs"), + }; + decrypted_shared_keys_array } /// Perform mainline AuthGraph key exchange with the provided source, but provide an invalid session /// ID signature. pub fn test_corrupt_sig( - impls: &mut traits::TraitImpl, + local_sink: &mut ke::AuthGraphParticipant, source: binder::Strong, ) { // Step 1: create an ephemeral ECDH key at the (remote) source. @@ -127,14 +138,14 @@ pub fn test_corrupt_sig( let source_pub_key = extract_plain_pub_key(&source_init_info.key.pubKey); // Step 2: pass the source's ECDH public key and other session info to the (local) sink. - let init_result = ke::init( - impls, - &source_pub_key.plainPubKey, - &source_init_info.identity.identity, - &source_init_info.nonce, - source_init_info.version, - ) - .expect("failed to init() with local impl"); + let init_result = local_sink + .init( + &source_pub_key.plainPubKey, + &source_init_info.identity.identity, + &source_init_info.nonce, + source_init_info.version, + ) + .expect("failed to init() with local impl"); let sink_init_info = init_result.session_init_info; let sink_pub_key = sink_init_info .ke_key @@ -172,7 +183,7 @@ pub fn test_corrupt_sig( /// Perform mainline AuthGraph key exchange with the provided source, but give it back /// a corrupted key. pub fn test_corrupt_key( - impls: &mut traits::TraitImpl, + local_sink: &mut ke::AuthGraphParticipant, source: binder::Strong, ) { // Step 1: create an ephemeral ECDH key at the (remote) source. @@ -184,14 +195,14 @@ pub fn test_corrupt_key( let source_pub_key = extract_plain_pub_key(&source_init_info.key.pubKey); // Step 2: pass the source's ECDH public key and other session info to the (local) sink. - let init_result = ke::init( - impls, - &source_pub_key.plainPubKey, - &source_init_info.identity.identity, - &source_init_info.nonce, - source_init_info.version, - ) - .expect("failed to init() with local impl"); + let init_result = local_sink + .init( + &source_pub_key.plainPubKey, + &source_init_info.identity.identity, + &source_init_info.nonce, + source_init_info.version, + ) + .expect("failed to init() with local impl"); let sink_init_info = init_result.session_init_info; let sink_pub_key = sink_init_info .ke_key @@ -202,14 +213,17 @@ pub fn test_corrupt_key( assert!(!sink_info.session_id.is_empty()); // The AuthGraph core library will verify the session ID signature, but do it here too. - let sink_verification_key = verification_key_from_identity(&impls, &sink_init_info.identity); - ke::verify_signature_on_session_id( - &sink_verification_key, - &sink_info.session_id, - &sink_info.session_id_signature, - &*impls.ecdsa, - ) - .expect("failed verification of signed session ID"); + let sink_verification_key = key::Identity::from_slice(&sink_init_info.identity) + .expect("invalid identity CBOR") + .cert_chain + .root_key; + local_sink + .verify_signature_on_session_id( + &sink_verification_key, + &sink_info.session_id, + &sink_info.session_id_signature, + ) + .expect("failed verification of signed session ID"); // Deliberately corrupt the source's encrypted key. let mut corrupt_key = source_init_info.key.clone(); diff --git a/security/authgraph/default/src/fuzzer.rs b/security/authgraph/default/src/fuzzer.rs index 6a9cfdd0b6..d4017776f8 100644 --- a/security/authgraph/default/src/fuzzer.rs +++ b/security/authgraph/default/src/fuzzer.rs @@ -25,7 +25,7 @@ use libfuzzer_sys::fuzz_target; use std::sync::{Arc, Mutex}; fuzz_target!(|data: &[u8]| { - let local_ta = LocalTa::new(); + let local_ta = LocalTa::new().expect("Failed to create an AuthGraph local TA."); let service = AuthGraphService::new_as_binder(Arc::new(Mutex::new(local_ta))); fuzz_service(&mut service.as_binder(), data); }); diff --git a/security/authgraph/default/src/lib.rs b/security/authgraph/default/src/lib.rs index 4cd0cb74ae..43d037c5ff 100644 --- a/security/authgraph/default/src/lib.rs +++ b/security/authgraph/default/src/lib.rs @@ -18,7 +18,9 @@ use authgraph_boringssl as boring; use authgraph_core::{ + error, key::MillisecondsSinceEpoch, + keyexchange, ta::{AuthGraphTa, Role}, traits, }; @@ -57,16 +59,17 @@ pub struct LocalTa { impl LocalTa { /// Create a new instance. - pub fn new() -> Self { - Self { + pub fn new() -> Result { + Ok(Self { ta: Arc::new(Mutex::new(AuthGraphTa::new( - boring::trait_impls( + keyexchange::AuthGraphParticipant::new( + boring::crypto_trait_impls(), Box::::default(), - Some(Box::new(StdClock::default())), - ), + keyexchange::MAX_OPENED_SESSIONS, + )?, Role::Both, ))), - } + }) } } diff --git a/security/authgraph/default/src/main.rs b/security/authgraph/default/src/main.rs index 873eb4eb77..81f2dd6115 100644 --- a/security/authgraph/default/src/main.rs +++ b/security/authgraph/default/src/main.rs @@ -65,7 +65,8 @@ fn inner_main() -> Result<(), HalServiceError> { binder::ProcessState::start_thread_pool(); // Register the service - let local_ta = LocalTa::new(); + let local_ta = + LocalTa::new().map_err(|e| format!("Failed to create the TA because: {e:?}"))?; 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| {