From 7c53bb314475fa41f666b52a079a62244181051b Mon Sep 17 00:00:00 2001 From: Shikha Panwar Date: Wed, 3 Jan 2024 17:58:11 +0000 Subject: [PATCH] Secretkeeper: VTS to use dice_chain as identity VTS (being the client of Sk) will use dice_chain as the identity. Consequently we can use the sealing policy constructed out of this identity & no more need to use HYPOTHETICAL_DICE_POLICY hack. For sample identity, we create dice_sample module which constructs an example dice chain (in Explicit key chain format), along with secrets. Test: Secretkeeper VTS Bug: 291224769 Change-Id: Ia1d1a92391d3ee455bf9fe254770b4a9bd08cb12 --- security/secretkeeper/aidl/vts/Android.bp | 3 + security/secretkeeper/aidl/vts/dice_sample.rs | 185 ++++++++++++++++++ .../aidl/vts/secretkeeper_test_client.rs | 97 ++++++--- 3 files changed, 256 insertions(+), 29 deletions(-) create mode 100644 security/secretkeeper/aidl/vts/dice_sample.rs diff --git a/security/secretkeeper/aidl/vts/Android.bp b/security/secretkeeper/aidl/vts/Android.bp index 1e01149086..720b8a2479 100644 --- a/security/secretkeeper/aidl/vts/Android.bp +++ b/security/secretkeeper/aidl/vts/Android.bp @@ -30,6 +30,8 @@ rust_test { ], test_config: "AndroidTest.xml", rustlibs: [ + "libdiced_open_dice", + "libdice_policy", "libsecretkeeper_client", "libsecretkeeper_comm_nostd", "libsecretkeeper_core_nostd", @@ -39,6 +41,7 @@ rust_test { "libcoset", "libauthgraph_vts_test", "libbinder_rs", + "libciborium", "libcoset", "liblog_rust", ], diff --git a/security/secretkeeper/aidl/vts/dice_sample.rs b/security/secretkeeper/aidl/vts/dice_sample.rs new file mode 100644 index 0000000000..db532b1d3c --- /dev/null +++ b/security/secretkeeper/aidl/vts/dice_sample.rs @@ -0,0 +1,185 @@ +/* + * 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. + */ + +//! This module provides a set of sample DICE chains for testing purpose only. Note that this +//! module duplicates a large chunk of code in libdiced_sample_inputs. We avoid modifying the +//! latter for testing purposes because it is installed on device. + +use ciborium::{de, ser, value::Value}; +use core::ffi::CStr; +use coset::{iana, Algorithm, AsCborValue, CoseKey, KeyOperation, KeyType, Label}; +use diced_open_dice::{ + derive_cdi_private_key_seed, keypair_from_seed, retry_bcc_format_config_descriptor, + retry_bcc_main_flow, retry_dice_main_flow, Config, DiceArtifacts, DiceConfigValues, DiceError, + DiceMode, InputValues, OwnedDiceArtifacts, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE, +}; +use log::error; +use secretkeeper_client::dice::OwnedDiceArtifactsWithExplicitKey; + +/// Sample UDS used to perform the root DICE flow by `make_sample_bcc_and_cdis`. +const UDS: &[u8; CDI_SIZE] = &[ + 0x65, 0x4f, 0xab, 0xa9, 0xa5, 0xad, 0x0f, 0x5e, 0x15, 0xc3, 0x12, 0xf7, 0x77, 0x45, 0xfa, 0x55, + 0x18, 0x6a, 0xa6, 0x34, 0xb6, 0x7c, 0x82, 0x7b, 0x89, 0x4c, 0xc5, 0x52, 0xd3, 0x27, 0x35, 0x8e, +]; + +const CODE_HASH_ABL: [u8; HASH_SIZE] = [ + 0x16, 0x48, 0xf2, 0x55, 0x53, 0x23, 0xdd, 0x15, 0x2e, 0x83, 0x38, 0xc3, 0x64, 0x38, 0x63, 0x26, + 0x0f, 0xcf, 0x5b, 0xd1, 0x3a, 0xd3, 0x40, 0x3e, 0x23, 0xf8, 0x34, 0x4c, 0x6d, 0xa2, 0xbe, 0x25, + 0x1c, 0xb0, 0x29, 0xe8, 0xc3, 0xfb, 0xb8, 0x80, 0xdc, 0xb1, 0xd2, 0xb3, 0x91, 0x4d, 0xd3, 0xfb, + 0x01, 0x0f, 0xe4, 0xe9, 0x46, 0xa2, 0xc0, 0x26, 0x57, 0x5a, 0xba, 0x30, 0xf7, 0x15, 0x98, 0x14, +]; +const AUTHORITY_HASH_ABL: [u8; HASH_SIZE] = [ + 0xf9, 0x00, 0x9d, 0xc2, 0x59, 0x09, 0xe0, 0xb6, 0x98, 0xbd, 0xe3, 0x97, 0x4a, 0xcb, 0x3c, 0xe7, + 0x6b, 0x24, 0xc3, 0xe4, 0x98, 0xdd, 0xa9, 0x6a, 0x41, 0x59, 0x15, 0xb1, 0x23, 0xe6, 0xc8, 0xdf, + 0xfb, 0x52, 0xb4, 0x52, 0xc1, 0xb9, 0x61, 0xdd, 0xbc, 0x5b, 0x37, 0x0e, 0x12, 0x12, 0xb2, 0xfd, + 0xc1, 0x09, 0xb0, 0xcf, 0x33, 0x81, 0x4c, 0xc6, 0x29, 0x1b, 0x99, 0xea, 0xae, 0xfd, 0xaa, 0x0d, +]; +const HIDDEN_ABL: [u8; HIDDEN_SIZE] = [ + 0xa2, 0x01, 0xd0, 0xc0, 0xaa, 0x75, 0x3c, 0x06, 0x43, 0x98, 0x6c, 0xc3, 0x5a, 0xb5, 0x5f, 0x1f, + 0x0f, 0x92, 0x44, 0x3b, 0x0e, 0xd4, 0x29, 0x75, 0xe3, 0xdb, 0x36, 0xda, 0xc8, 0x07, 0x97, 0x4d, + 0xff, 0xbc, 0x6a, 0xa4, 0x8a, 0xef, 0xc4, 0x7f, 0xf8, 0x61, 0x7d, 0x51, 0x4d, 0x2f, 0xdf, 0x7e, + 0x8c, 0x3d, 0xa3, 0xfc, 0x63, 0xd4, 0xd4, 0x74, 0x8a, 0xc4, 0x14, 0x45, 0x83, 0x6b, 0x12, 0x7e, +]; +const CODE_HASH_AVB: [u8; HASH_SIZE] = [ + 0xa4, 0x0c, 0xcb, 0xc1, 0xbf, 0xfa, 0xcc, 0xfd, 0xeb, 0xf4, 0xfc, 0x43, 0x83, 0x7f, 0x46, 0x8d, + 0xd8, 0xd8, 0x14, 0xc1, 0x96, 0x14, 0x1f, 0x6e, 0xb3, 0xa0, 0xd9, 0x56, 0xb3, 0xbf, 0x2f, 0xfa, + 0x88, 0x70, 0x11, 0x07, 0x39, 0xa4, 0xd2, 0xa9, 0x6b, 0x18, 0x28, 0xe8, 0x29, 0x20, 0x49, 0x0f, + 0xbb, 0x8d, 0x08, 0x8c, 0xc6, 0x54, 0xe9, 0x71, 0xd2, 0x7e, 0xa4, 0xfe, 0x58, 0x7f, 0xd3, 0xc7, +]; +const AUTHORITY_HASH_AVB: [u8; HASH_SIZE] = [ + 0xb2, 0x69, 0x05, 0x48, 0x56, 0xb5, 0xfa, 0x55, 0x6f, 0xac, 0x56, 0xd9, 0x02, 0x35, 0x2b, 0xaa, + 0x4c, 0xba, 0x28, 0xdd, 0x82, 0x3a, 0x86, 0xf5, 0xd4, 0xc2, 0xf1, 0xf9, 0x35, 0x7d, 0xe4, 0x43, + 0x13, 0xbf, 0xfe, 0xd3, 0x36, 0xd8, 0x1c, 0x12, 0x78, 0x5c, 0x9c, 0x3e, 0xf6, 0x66, 0xef, 0xab, + 0x3d, 0x0f, 0x89, 0xa4, 0x6f, 0xc9, 0x72, 0xee, 0x73, 0x43, 0x02, 0x8a, 0xef, 0xbc, 0x05, 0x98, +]; +const HIDDEN_AVB: [u8; HIDDEN_SIZE] = [ + 0x5b, 0x3f, 0xc9, 0x6b, 0xe3, 0x95, 0x59, 0x40, 0x5e, 0x64, 0xe5, 0x64, 0x3f, 0xfd, 0x21, 0x09, + 0x9d, 0xf3, 0xcd, 0xc7, 0xa4, 0x2a, 0xe2, 0x97, 0xdd, 0xe2, 0x4f, 0xb0, 0x7d, 0x7e, 0xf5, 0x8e, + 0xd6, 0x4d, 0x84, 0x25, 0x54, 0x41, 0x3f, 0x8f, 0x78, 0x64, 0x1a, 0x51, 0x27, 0x9d, 0x55, 0x8a, + 0xe9, 0x90, 0x35, 0xab, 0x39, 0x80, 0x4b, 0x94, 0x40, 0x84, 0xa2, 0xfd, 0x73, 0xeb, 0x35, 0x7a, +]; +const AUTHORITY_HASH_ANDROID: [u8; HASH_SIZE] = [ + 0x04, 0x25, 0x5d, 0x60, 0x5f, 0x5c, 0x45, 0x0d, 0xf2, 0x9a, 0x6e, 0x99, 0x30, 0x03, 0xb8, 0xd6, + 0xe1, 0x99, 0x71, 0x1b, 0xf8, 0x44, 0xfa, 0xb5, 0x31, 0x79, 0x1c, 0x37, 0x68, 0x4e, 0x1d, 0xc0, + 0x24, 0x74, 0x68, 0xf8, 0x80, 0x20, 0x3e, 0x44, 0xb1, 0x43, 0xd2, 0x9c, 0xfc, 0x12, 0x9e, 0x77, + 0x0a, 0xde, 0x29, 0x24, 0xff, 0x2e, 0xfa, 0xc7, 0x10, 0xd5, 0x73, 0xd4, 0xc6, 0xdf, 0x62, 0x9f, +]; + +/// Encode the public key to CBOR Value. The input (raw 32 bytes) is wrapped into CoseKey. +fn ed25519_public_key_to_cbor_value(public_key: &[u8]) -> Value { + let key = CoseKey { + kty: KeyType::Assigned(iana::KeyType::OKP), + alg: Some(Algorithm::Assigned(iana::Algorithm::EdDSA)), + key_ops: vec![KeyOperation::Assigned(iana::KeyOperation::Verify)].into_iter().collect(), + params: vec![ + ( + Label::Int(iana::Ec2KeyParameter::Crv as i64), + Value::from(iana::EllipticCurve::Ed25519 as u64), + ), + (Label::Int(iana::Ec2KeyParameter::X as i64), Value::Bytes(public_key.to_vec())), + ], + ..Default::default() + }; + key.to_cbor_value().unwrap() +} + +/// Makes a DICE chain (BCC) from the sample input. +/// +/// The DICE chain is of the following format: +/// public key derived from UDS -> ABL certificate -> AVB certificate -> Android certificate +/// The `security_version` is included in the Android certificate. +pub fn make_explicit_owned_dice(security_version: u64) -> OwnedDiceArtifactsWithExplicitKey { + let dice = make_sample_bcc_and_cdis(security_version); + OwnedDiceArtifactsWithExplicitKey::from_owned_artifacts(dice).unwrap() +} + +fn make_sample_bcc_and_cdis(security_version: u64) -> OwnedDiceArtifacts { + let private_key_seed = derive_cdi_private_key_seed(UDS).unwrap(); + + // Gets the root public key in DICE chain (BCC). + let (public_key, _) = keypair_from_seed(private_key_seed.as_array()).unwrap(); + let ed25519_public_key_value = ed25519_public_key_to_cbor_value(&public_key); + + // Gets the ABL certificate to as the root certificate of DICE chain. + let config_values = DiceConfigValues { + component_name: Some(CStr::from_bytes_with_nul(b"ABL\0").unwrap()), + component_version: Some(1), + resettable: true, + ..Default::default() + }; + let config_descriptor = retry_bcc_format_config_descriptor(&config_values).unwrap(); + let input_values = InputValues::new( + CODE_HASH_ABL, + Config::Descriptor(config_descriptor.as_slice()), + AUTHORITY_HASH_ABL, + DiceMode::kDiceModeNormal, + HIDDEN_ABL, + ); + let (cdi_values, cert) = retry_dice_main_flow(UDS, UDS, &input_values).unwrap(); + let bcc_value = + Value::Array(vec![ed25519_public_key_value, de::from_reader(&cert[..]).unwrap()]); + let mut bcc: Vec = vec![]; + ser::into_writer(&bcc_value, &mut bcc).unwrap(); + + // Appends AVB certificate to DICE chain. + let config_values = DiceConfigValues { + component_name: Some(CStr::from_bytes_with_nul(b"AVB\0").unwrap()), + component_version: Some(1), + resettable: true, + ..Default::default() + }; + let config_descriptor = retry_bcc_format_config_descriptor(&config_values).unwrap(); + let input_values = InputValues::new( + CODE_HASH_AVB, + Config::Descriptor(config_descriptor.as_slice()), + AUTHORITY_HASH_AVB, + DiceMode::kDiceModeNormal, + HIDDEN_AVB, + ); + let dice_artifacts = + retry_bcc_main_flow(&cdi_values.cdi_attest, &cdi_values.cdi_seal, &bcc, &input_values) + .unwrap(); + + // Appends Android certificate to DICE chain. + let config_values = DiceConfigValues { + component_name: Some(CStr::from_bytes_with_nul(b"Android\0").unwrap()), + component_version: Some(12), + security_version: Some(security_version), + resettable: true, + ..Default::default() + }; + let config_descriptor = retry_bcc_format_config_descriptor(&config_values).unwrap(); + let input_values = InputValues::new( + [0u8; HASH_SIZE], // code_hash + Config::Descriptor(config_descriptor.as_slice()), + AUTHORITY_HASH_ANDROID, + DiceMode::kDiceModeNormal, + [0u8; HIDDEN_SIZE], // hidden + ); + retry_bcc_main_flow( + dice_artifacts.cdi_attest(), + dice_artifacts.cdi_seal(), + dice_artifacts + .bcc() + .ok_or_else(|| { + error!("bcc is none"); + DiceError::InvalidInput + }) + .unwrap(), + &input_values, + ) + .unwrap() +} diff --git a/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs b/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs index 37b280422d..a62546d779 100644 --- a/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs +++ b/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs @@ -15,6 +15,9 @@ */ #![cfg(test)] +mod dice_sample; + +use crate::dice_sample::make_explicit_owned_dice; use rdroidtest_macro::{ignore_if, rdroidtest}; use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::ISecretkeeper::ISecretkeeper; @@ -23,6 +26,8 @@ use authgraph_vts_test as ag_vts; use authgraph_boringssl as boring; use authgraph_core::key; use coset::{CborSerializable, CoseEncrypt0}; +use dice_policy::{ConstraintSpec, ConstraintType, DicePolicy}; +use secretkeeper_client::dice::OwnedDiceArtifactsWithExplicitKey; use secretkeeper_client::SkSession; use secretkeeper_core::cipher; use secretkeeper_comm::data_types::error::SecretkeeperError; @@ -37,16 +42,6 @@ use secretkeeper_comm::data_types::packet::{ResponsePacket, ResponseType}; const SECRETKEEPER_SERVICE: &str = "android.hardware.security.secretkeeper.ISecretkeeper"; const CURRENT_VERSION: u64 = 1; -// TODO(b/291238565): This will change once libdice_policy switches to Explicit-key DiceCertChain -// This is generated by patching libdice_policy such that it dumps an example dice chain & -// a policy, such that the former matches the latter. -const HYPOTHETICAL_DICE_POLICY: [u8; 49] = [ - 0x84, 0x01, 0x81, 0x83, 0x01, 0x80, 0x01, 0x81, 0x83, 0x01, 0x80, 0x43, 0xa1, 0x01, 0x00, 0x82, - 0x83, 0x01, 0x81, 0x01, 0x73, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69, 0x63, - 0x65, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x83, 0x02, 0x82, 0x03, 0x18, 0x64, 0x19, 0xe9, - 0x75, -]; - // Random bytes (of ID_SIZE/SECRET_SIZE) generated for tests. const ID_EXAMPLE: Id = Id([ 0xF1, 0xB2, 0xED, 0x3B, 0xD1, 0xBD, 0xF0, 0x7D, 0xE1, 0xF0, 0x01, 0xFC, 0x61, 0x71, 0xD3, 0x42, @@ -89,6 +84,7 @@ fn get_connection(instance: &str) -> binder::Strong { struct SkClient { sk: binder::Strong, session: SkSession, + dice_artifacts: OwnedDiceArtifactsWithExplicitKey, } impl Drop for SkClient { @@ -99,9 +95,20 @@ impl Drop for SkClient { } impl SkClient { + /// Create an `SkClient` using the default `OwnedDiceArtifactsWithExplicitKey` for identity. fn new(instance: &str) -> Self { + let default_dice = make_explicit_owned_dice(/*Security version in a node */ 5); + Self::with_identity(instance, default_dice) + } + + /// Create an `SkClient` using the given `OwnedDiceArtifactsWithExplicitKey` for identity. + fn with_identity(instance: &str, dice_artifacts: OwnedDiceArtifactsWithExplicitKey) -> Self { let sk = get_connection(instance); - Self { sk: sk.clone(), session: SkSession::new(sk).unwrap() } + Self { + sk: sk.clone(), + session: SkSession::new(sk, &dice_artifacts).unwrap(), + dice_artifacts, + } } /// This method is wrapper that use `SkSession::secret_management_request` which handles @@ -144,13 +151,12 @@ impl SkClient { .unwrap() } - /// Helper method to store a secret. + /// Helper method to store a secret. This uses the default compatible sealing_policy on + /// dice_chain. fn store(&mut self, id: &Id, secret: &Secret) { - let store_request = StoreSecretRequest { - id: id.clone(), - secret: secret.clone(), - sealing_policy: HYPOTHETICAL_DICE_POLICY.to_vec(), - }; + let sealing_policy = sealing_policy(self.dice_artifacts.explicit_key_dice_chain().unwrap()); + let store_request = + StoreSecretRequest { id: id.clone(), secret: secret.clone(), sealing_policy }; let store_request = store_request.serialize_to_packet().to_vec().unwrap(); let store_response = self.secret_management_request(&store_request); @@ -192,6 +198,34 @@ impl SkClient { } } +/// Construct a sealing policy on the dice chain. This method uses the following set of +/// constraints which are compatible with sample DICE chains used in VTS. +/// 1. ExactMatch on AUTHORITY_HASH (non-optional). +/// 2. ExactMatch on KEY_MODE (non-optional). +/// 3. GreaterOrEqual on SECURITY_VERSION (optional). +fn sealing_policy(dice: &[u8]) -> Vec { + let authority_hash: i64 = -4670549; + let key_mode: i64 = -4670551; + let config_desc: i64 = -4670548; + let security_version: i64 = -70005; + + let constraint_spec = [ + ConstraintSpec::new( + ConstraintType::ExactMatch, + vec![authority_hash], + /* Optional */ false, + ), + ConstraintSpec::new(ConstraintType::ExactMatch, vec![key_mode], false), + ConstraintSpec::new( + ConstraintType::GreaterOrEqual, + vec![config_desc, security_version], + true, + ), + ]; + + DicePolicy::from_dice_chain(dice, &constraint_spec).unwrap().to_vec().unwrap() +} + /// Perform AuthGraph key exchange, returning the session keys and session ID. fn authgraph_key_exchange(sk: binder::Strong) -> ([key::AesKey; 2], Vec) { let sink = sk.getAuthGraphKe().expect("failed to get AuthGraph"); @@ -370,9 +404,11 @@ fn secretkeeper_store_delete_all(instance: String) { // first few messages. #[rdroidtest(get_instances())] fn secret_management_replay_protection_seq_num(instance: String) { - let sk_client = SkClient::new(&instance); + let dice_chain = make_explicit_owned_dice(/*Security version in a node */ 5); + let sealing_policy = sealing_policy(dice_chain.explicit_key_dice_chain().unwrap()); + let sk_client = SkClient::with_identity(&instance, dice_chain); // Construct encoded request packets for the test - let (req_1, req_2, req_3) = construct_secret_management_requests(); + let (req_1, req_2, req_3) = construct_secret_management_requests(sealing_policy); // Lets now construct the seq_numbers(in request & expected in response) let mut seq_a = SeqNum::new(); @@ -404,10 +440,12 @@ fn secret_management_replay_protection_seq_num(instance: String) { // for new sessions. #[rdroidtest(get_instances())] fn secret_management_replay_protection_seq_num_per_session(instance: String) { - let sk_client = SkClient::new(&instance); + let dice_chain = make_explicit_owned_dice(/*Security version in a node */ 5); + let sealing_policy = sealing_policy(dice_chain.explicit_key_dice_chain().unwrap()); + let sk_client = SkClient::with_identity(&instance, dice_chain); // Construct encoded request packets for the test - let (req_1, _, _) = construct_secret_management_requests(); + let (req_1, _, _) = construct_secret_management_requests(sealing_policy); // Lets now construct the seq_number (in request & expected in response) let mut seq_a = SeqNum::new(); @@ -434,10 +472,12 @@ fn secret_management_replay_protection_seq_num_per_session(instance: String) { #[rdroidtest(get_instances())] #[ignore] fn secret_management_replay_protection_out_of_seq_req_not_accepted(instance: String) { - let sk_client = SkClient::new(&instance); + let dice_chain = make_explicit_owned_dice(/*Security version in a node */ 5); + let sealing_policy = sealing_policy(dice_chain.explicit_key_dice_chain().unwrap()); + let sk_client = SkClient::with_identity(&instance, dice_chain); // Construct encoded request packets for the test - let (req_1, req_2, _) = construct_secret_management_requests(); + let (req_1, req_2, _) = construct_secret_management_requests(sealing_policy); // Lets now construct the seq_numbers(in request & expected in response) let mut seq_a = SeqNum::new(); @@ -451,14 +491,13 @@ fn secret_management_replay_protection_out_of_seq_req_not_accepted(instance: Str sk_client.secret_management_request_custom_aad(&req_2, /*Skipping seq_1*/ &seq_2, &seq_1); } -fn construct_secret_management_requests() -> (Vec, Vec, Vec) { +// Helper method that constructs 3 SecretManagement requests. Callers would usually not care about +// what each of the request concretely is. +fn construct_secret_management_requests(sealing_policy: Vec) -> (Vec, Vec, Vec) { let version_request = GetVersionRequest {}; let version_request = version_request.serialize_to_packet().to_vec().unwrap(); - let store_request = StoreSecretRequest { - id: ID_EXAMPLE, - secret: SECRET_EXAMPLE, - sealing_policy: HYPOTHETICAL_DICE_POLICY.to_vec(), - }; + let store_request = + StoreSecretRequest { id: ID_EXAMPLE, secret: SECRET_EXAMPLE, sealing_policy }; let store_request = store_request.serialize_to_packet().to_vec().unwrap(); let get_request = GetSecretRequest { id: ID_EXAMPLE, updated_sealing_policy: None }; let get_request = get_request.serialize_to_packet().to_vec().unwrap();