Merge "Secretkeeper: VTS to use dice_chain as identity" into main

This commit is contained in:
Shikha Panwar 2024-01-12 16:49:09 +00:00 committed by Gerrit Code Review
commit 6d5bb1eeaf
3 changed files with 256 additions and 29 deletions

View file

@ -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",
],

View file

@ -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<u8> = 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()
}

View file

@ -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<dyn ISecretkeeper> {
struct SkClient {
sk: binder::Strong<dyn ISecretkeeper>,
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<u8> {
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<dyn ISecretkeeper>) -> ([key::AesKey; 2], Vec<u8>) {
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<u8>, Vec<u8>, Vec<u8>) {
// 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<u8>) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
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();