Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5df6ed5e authored by Hasini Gunasinghe's avatar Hasini Gunasinghe
Browse files

Update the VTS module according to the refactoring in aosp/2826571

Test: atest VtsAidlAuthGraphRoleTest, atest VtsAidlAuthGraphSessionTest
Change-Id: I6cc3bd17952f602b58668d35e09c6a5385c7de61
parent 715bb87f
Loading
Loading
Loading
Loading
+7 −41
Original line number Diff line number Diff line
@@ -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<ke::AuthGraphParticipant, AgError> {
    Ok(ke::AuthGraphParticipant::new(
        boring::crypto_trait_impls(),
        Box::<boring::test_device::AgDevice>::default(),
        Some(Box::new(StdClock::default())),
    )
        ke::MAX_OPENED_SESSIONS,
    )?)
}

fn build_plain_pub_key(pub_key: &Option<Vec<u8>>) -> PubKey {
@@ -56,14 +53,6 @@ fn extract_plain_pub_key(pub_key: &Option<PubKey>) -> &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<u8>; 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
}
+12 −12
Original line number Diff line number Diff line
@@ -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!());
}
+80 −61
Original line number Diff line number Diff line
@@ -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<dyn IAuthGraphKeyExchange>) {
    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<dyn IAuthGraphKeyExchange>,
) {
    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<dyn IAuthGraphKeyExchange>,
) -> [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,20 +55,21 @@ 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(
    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,
        &*impls.ecdsa,
        )
        .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,
    let source_info = local_source
        .finish(
            &sink_pub_key.plainPubKey,
            &sink_init_info.identity.identity,
            &sink_info.signature.signature,
@@ -75,13 +81,15 @@ pub fn test_mainline(
    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(
    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,
        &*impls.ecdsa,
        )
        .expect("failed verification of signed session ID");

@@ -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<dyn IAuthGraphKeyExchange>,
) {
    // 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,8 +144,8 @@ 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,
    let source_info = local_source
        .finish(
            &sink_pub_key.plainPubKey,
            &sink_init_info.identity.identity,
            &sink_info.signature.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<dyn IAuthGraphKeyExchange>,
) {
    // 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,8 +200,8 @@ 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,
    let source_info = local_source
        .finish(
            &sink_pub_key.plainPubKey,
            &sink_init_info.identity.identity,
            &sink_info.signature.signature,
+79 −65
Original line number Diff line number Diff line
@@ -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<dyn IAuthGraphKeyExchange>) {
    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<dyn IAuthGraphKeyExchange>,
) {
    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<dyn IAuthGraphKeyExchange>,
) -> [key::AesKey; 2] {
    // Step 1: create an ephemeral ECDH key at the (remote) source.
@@ -40,8 +43,8 @@ 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,
    let init_result = local_sink
        .init(
            &source_pub_key.plainPubKey,
            &source_init_info.identity.identity,
            &source_init_info.nonce,
@@ -58,12 +61,15 @@ 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(
    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,
        &*impls.ecdsa,
        )
        .expect("failed verification of signed session ID");

@@ -86,13 +92,14 @@ 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(
    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,
        &*impls.ecdsa,
        )
        .expect("failed verification of signed session ID");

@@ -101,21 +108,25 @@ pub fn test_mainline(

    // 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,
    )
    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<dyn IAuthGraphKeyExchange>,
) {
    // Step 1: create an ephemeral ECDH key at the (remote) source.
@@ -127,8 +138,8 @@ 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,
    let init_result = local_sink
        .init(
            &source_pub_key.plainPubKey,
            &source_init_info.identity.identity,
            &source_init_info.nonce,
@@ -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<dyn IAuthGraphKeyExchange>,
) {
    // Step 1: create an ephemeral ECDH key at the (remote) source.
@@ -184,8 +195,8 @@ 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,
    let init_result = local_sink
        .init(
            &source_pub_key.plainPubKey,
            &source_init_info.identity.identity,
            &source_init_info.nonce,
@@ -202,12 +213,15 @@ 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(
    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,
        &*impls.ecdsa,
        )
        .expect("failed verification of signed session ID");

+1 −1
Original line number Diff line number Diff line
@@ -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);
});
Loading