Merge "Make RKPD default"
diff --git a/diced/Android.bp b/diced/Android.bp
index e13d863..d4d29d5 100644
--- a/diced/Android.bp
+++ b/diced/Android.bp
@@ -60,7 +60,6 @@
"libanyhow",
"libdiced_open_dice_cbor",
"libdiced_utils",
- "libkeystore2_crypto_rust",
],
}
@@ -75,27 +74,6 @@
"libanyhow",
"libdiced_open_dice_cbor",
"libdiced_utils",
- "libkeystore2_crypto_rust",
- ],
-}
-
-rust_library {
- name: "libdiced",
- crate_name: "diced",
- srcs: ["src/lib.rs"],
-
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "android.security.dice-rust",
- "libdiced_open_dice_cbor",
- "libanyhow",
- "libbinder_rs",
- "libdiced_utils",
- "libkeystore2_crypto_rust",
- "libkeystore2_selinux",
- "liblibc",
- "liblog_rust",
- "libthiserror",
],
}
@@ -121,66 +99,6 @@
],
}
-rust_binary {
- name: "diced",
- srcs: ["src/diced_main.rs"],
- prefer_rlib: true,
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "libandroid_logger",
- "libbinder_rs",
- "libdiced",
- "libdiced_open_dice_cbor",
- "libdiced_sample_inputs",
- "libdiced_utils",
- "liblog_rust",
- ],
- init_rc: ["diced.rc"],
-}
-
-rust_binary {
- name: "diced.microdroid",
- srcs: ["src/diced_main.rs"],
- prefer_rlib: true,
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "libandroid_logger",
- "libbinder_rs",
- "libdiced",
- "libdiced_open_dice_cbor",
- "libdiced_sample_inputs",
- "libdiced_utils",
- "liblog_rust",
- ],
- init_rc: ["diced.microdroid.rc"],
- bootstrap: true,
-}
-
-rust_test {
- name: "diced_test",
- crate_name: "diced_test",
- srcs: ["src/lib.rs"],
- test_suites: ["general-tests"],
- auto_gen_config: true,
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "android.security.dice-rust",
- "libanyhow",
- "libbinder_rs",
- "libdiced_open_dice_cbor",
- "libdiced_utils",
- "libkeystore2_crypto_rust",
- "libkeystore2_selinux",
- "libkeystore2_vintf_rust",
- "liblibc",
- "liblog_rust",
- "libnix",
- "libserde",
- "libserde_cbor",
- "libthiserror",
- ],
-}
-
rust_test {
name: "diced_vendor_test",
crate_name: "diced_vendor_test",
diff --git a/diced/TEST_MAPPING b/diced/TEST_MAPPING
index d81efdd..be1c5e5 100644
--- a/diced/TEST_MAPPING
+++ b/diced/TEST_MAPPING
@@ -1,15 +1,18 @@
{
"presubmit": [
{
+ "name": "libdiced_open_dice.integration_test"
+ },
+ {
+ "name": "libdiced_open_dice_nostd.integration_test"
+ },
+ {
"name": "diced_utils_test"
},
{
"name": "diced_sample_inputs_test"
},
{
- "name": "diced_test"
- },
- {
"name": "diced_vendor_test"
}
]
diff --git a/diced/diced.microdroid.rc b/diced/diced.microdroid.rc
deleted file mode 100644
index 2226f47..0000000
--- a/diced/diced.microdroid.rc
+++ /dev/null
@@ -1,13 +0,0 @@
-# Start the Diced service.
-#
-# See system/core/init/README.md for information on the init.rc language.
-
-service diced /system/bin/diced.microdroid
- class main
- user diced
- group diced
- # The diced service must not be allowed to restart.
- # If it crashes for any reason security critical state is lost.
- # The only remedy is to restart the device.
- oneshot
- writepid /dev/cpuset/foreground/tasks
diff --git a/diced/diced.rc b/diced/diced.rc
deleted file mode 100644
index 8c43fa5..0000000
--- a/diced/diced.rc
+++ /dev/null
@@ -1,13 +0,0 @@
-# Start the Diced service.
-#
-# See system/core/init/README.md for information on the init.rc language.
-
-service diced /system/bin/diced
- class main
- user diced
- group diced
- # The diced service must not be allowed to restart.
- # If it crashes for any reason security critical state is lost.
- # The only remedy is to restart the device.
- oneshot
- writepid /dev/cpuset/foreground/tasks
diff --git a/diced/open_dice/Android.bp b/diced/open_dice/Android.bp
new file mode 100644
index 0000000..ea3ee3b
--- /dev/null
+++ b/diced/open_dice/Android.bp
@@ -0,0 +1,72 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+ name: "libdiced_open_dice_defaults",
+ crate_name: "diced_open_dice",
+ srcs: ["src/lib.rs"],
+ static_libs: [
+ "libopen_dice_cbor",
+ ],
+ vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
+}
+
+rust_library_rlib {
+ name: "libdiced_open_dice_nostd",
+ defaults: ["libdiced_open_dice_defaults"],
+ rustlibs: [
+ "libopen_dice_bcc_bindgen_nostd",
+ "libopen_dice_cbor_bindgen_nostd",
+ "libzeroize_nostd",
+ ],
+ whole_static_libs: [
+ "libopen_dice_cbor",
+ "libcrypto_baremetal",
+ ],
+}
+
+rust_library {
+ name: "libdiced_open_dice",
+ defaults: ["libdiced_open_dice_defaults"],
+ rustlibs: [
+ "libopen_dice_bcc_bindgen",
+ "libopen_dice_cbor_bindgen",
+ // For ZVec
+ "libkeystore2_crypto_rust",
+ "libzeroize",
+ ],
+ features: [
+ "std",
+ ],
+ whole_static_libs: [
+ "libopen_dice_bcc",
+ ],
+}
+
+rust_defaults {
+ name: "libdiced_open_dice_test_defaults",
+ crate_name: "diced_open_dice_test",
+ srcs: ["tests/*.rs"],
+ test_suites: ["general-tests"],
+}
+
+rust_test {
+ name: "libdiced_open_dice.integration_test",
+ defaults: ["libdiced_open_dice_test_defaults"],
+ rustlibs: [
+ "libdiced_open_dice",
+ ],
+}
+
+rust_test {
+ name: "libdiced_open_dice_nostd.integration_test",
+ defaults: ["libdiced_open_dice_test_defaults"],
+ rustlibs: [
+ "libdiced_open_dice_nostd",
+ ],
+}
diff --git a/diced/open_dice/src/bcc.rs b/diced/open_dice/src/bcc.rs
new file mode 100644
index 0000000..e3a96fe
--- /dev/null
+++ b/diced/open_dice/src/bcc.rs
@@ -0,0 +1,92 @@
+// Copyright 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 mirrors the content in open-dice/include/dice/android/bcc.h
+
+use crate::dice::{Cdi, CdiValues, InputValues};
+use crate::error::{check_result, Result};
+use open_dice_bcc_bindgen::{
+ BccConfigValues, BccFormatConfigDescriptor, BccMainFlow, BCC_INPUT_COMPONENT_NAME,
+ BCC_INPUT_COMPONENT_VERSION, BCC_INPUT_RESETTABLE,
+};
+use std::{ffi::CStr, ptr};
+
+/// Formats a configuration descriptor following the BCC's specification.
+/// See https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
+pub fn bcc_format_config_descriptor(
+ name: Option<&CStr>,
+ version: Option<u64>,
+ resettable: bool,
+ buffer: &mut [u8],
+) -> Result<usize> {
+ let mut inputs = 0;
+ if name.is_some() {
+ inputs |= BCC_INPUT_COMPONENT_NAME;
+ }
+ if version.is_some() {
+ inputs |= BCC_INPUT_COMPONENT_VERSION;
+ }
+ if resettable {
+ inputs |= BCC_INPUT_RESETTABLE;
+ }
+
+ let values = BccConfigValues {
+ inputs,
+ component_name: name.map_or(ptr::null(), |p| p.as_ptr()),
+ component_version: version.unwrap_or(0),
+ };
+
+ let mut buffer_size = 0;
+ // SAFETY: The function writes to the buffer, within the given bounds, and only reads the
+ // input values. It writes its result to buffer_size.
+ check_result(unsafe {
+ BccFormatConfigDescriptor(&values, buffer.len(), buffer.as_mut_ptr(), &mut buffer_size)
+ })?;
+ Ok(buffer_size)
+}
+
+/// Executes the main BCC flow.
+///
+/// Given a full set of input values along with the current BCC and CDI values,
+/// computes the next CDI values and matching updated BCC.
+pub fn bcc_main_flow(
+ current_cdi_attest: &Cdi,
+ current_cdi_seal: &Cdi,
+ current_bcc: &[u8],
+ input_values: &InputValues,
+ next_cdi_values: &mut CdiValues,
+ next_bcc: &mut [u8],
+) -> Result<usize> {
+ let mut next_bcc_size = 0;
+ // SAFETY: `BccMainFlow` only reads the current `bcc` and CDI values and writes
+ // to `next_bcc` and next CDI values within its bounds. It also reads
+ // `input_values` as a constant input and doesn't store any pointer.
+ // The first argument can be null and is not used in the current implementation.
+ check_result(unsafe {
+ BccMainFlow(
+ ptr::null_mut(), // context
+ current_cdi_attest.as_ptr(),
+ current_cdi_seal.as_ptr(),
+ current_bcc.as_ptr(),
+ current_bcc.len(),
+ input_values.as_ptr(),
+ next_bcc.len(),
+ next_bcc.as_mut_ptr(),
+ &mut next_bcc_size,
+ next_cdi_values.cdi_attest.as_mut_ptr(),
+ next_cdi_values.cdi_seal.as_mut_ptr(),
+ )
+ })?;
+ Ok(next_bcc_size)
+}
diff --git a/diced/open_dice/src/dice.rs b/diced/open_dice/src/dice.rs
new file mode 100644
index 0000000..ed7d843
--- /dev/null
+++ b/diced/open_dice/src/dice.rs
@@ -0,0 +1,198 @@
+// Copyright 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.
+
+//! Structs and functions about the types used in DICE.
+//! This module mirrors the content in open-dice/include/dice/dice.h
+
+use crate::error::{check_result, Result};
+pub use open_dice_cbor_bindgen::DiceMode;
+use open_dice_cbor_bindgen::{
+ DiceConfigType, DiceDeriveCdiCertificateId, DiceDeriveCdiPrivateKeySeed, DiceInputValues,
+ DiceMainFlow, DICE_CDI_SIZE, DICE_HASH_SIZE, DICE_HIDDEN_SIZE, DICE_ID_SIZE,
+ DICE_INLINE_CONFIG_SIZE, DICE_PRIVATE_KEY_SEED_SIZE,
+};
+use std::ptr;
+use zeroize::{Zeroize, ZeroizeOnDrop};
+
+/// The size of a DICE hash.
+pub const HASH_SIZE: usize = DICE_HASH_SIZE as usize;
+/// The size of the DICE hidden value.
+pub const HIDDEN_SIZE: usize = DICE_HIDDEN_SIZE as usize;
+/// The size of a DICE inline config.
+const INLINE_CONFIG_SIZE: usize = DICE_INLINE_CONFIG_SIZE as usize;
+/// The size of a CDI.
+pub const CDI_SIZE: usize = DICE_CDI_SIZE as usize;
+/// The size of a private key seed.
+pub const PRIVATE_KEY_SEED_SIZE: usize = DICE_PRIVATE_KEY_SEED_SIZE as usize;
+/// The size of an ID.
+pub const ID_SIZE: usize = DICE_ID_SIZE as usize;
+
+/// Array type of hashes used by DICE.
+pub type Hash = [u8; HASH_SIZE];
+/// Array type of additional input.
+pub type Hidden = [u8; HIDDEN_SIZE];
+/// Array type of inline configuration values.
+pub type InlineConfig = [u8; INLINE_CONFIG_SIZE];
+/// Array type of CDIs.
+pub type Cdi = [u8; CDI_SIZE];
+/// Array type of private key seeds.
+pub type PrivateKeySeed = [u8; PRIVATE_KEY_SEED_SIZE];
+/// Array type of DICE ID.
+pub type DiceId = [u8; ID_SIZE];
+
+/// CDI Values.
+#[derive(Zeroize, ZeroizeOnDrop, Default)]
+pub struct CdiValues {
+ /// Attestation CDI.
+ pub cdi_attest: Cdi,
+ /// Sealing CDI.
+ pub cdi_seal: Cdi,
+}
+
+/// Configuration descriptor for DICE input values.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Config<'a> {
+ /// Reference to an inline descriptor.
+ Inline(&'a InlineConfig),
+ /// Reference to a free form descriptor that will be hashed by the implementation.
+ Descriptor(&'a [u8]),
+}
+
+impl Config<'_> {
+ fn dice_config_type(&self) -> DiceConfigType {
+ match self {
+ Self::Inline(_) => DiceConfigType::kDiceConfigTypeInline,
+ Self::Descriptor(_) => DiceConfigType::kDiceConfigTypeDescriptor,
+ }
+ }
+
+ fn inline_config(&self) -> InlineConfig {
+ match self {
+ Self::Inline(inline) => **inline,
+ Self::Descriptor(_) => [0u8; INLINE_CONFIG_SIZE],
+ }
+ }
+
+ fn descriptor_ptr(&self) -> *const u8 {
+ match self {
+ Self::Descriptor(descriptor) => descriptor.as_ptr(),
+ _ => ptr::null(),
+ }
+ }
+
+ fn descriptor_size(&self) -> usize {
+ match self {
+ Self::Descriptor(descriptor) => descriptor.len(),
+ _ => 0,
+ }
+ }
+}
+
+/// Wrap of `DiceInputValues`.
+#[derive(Clone, Debug)]
+pub struct InputValues(DiceInputValues);
+
+impl InputValues {
+ /// Creates a new `InputValues`.
+ pub fn new(
+ code_hash: Hash,
+ config: Config,
+ authority_hash: Hash,
+ mode: DiceMode,
+ hidden: Hidden,
+ ) -> Self {
+ Self(DiceInputValues {
+ code_hash,
+ code_descriptor: ptr::null(),
+ code_descriptor_size: 0,
+ config_type: config.dice_config_type(),
+ config_value: config.inline_config(),
+ config_descriptor: config.descriptor_ptr(),
+ config_descriptor_size: config.descriptor_size(),
+ authority_hash,
+ authority_descriptor: ptr::null(),
+ authority_descriptor_size: 0,
+ mode,
+ hidden,
+ })
+ }
+
+ /// Returns a raw pointer to the wrapped `DiceInputValues`.
+ pub fn as_ptr(&self) -> *const DiceInputValues {
+ &self.0 as *const DiceInputValues
+ }
+}
+
+/// Derives a CDI private key seed from a `cdi_attest` value.
+pub fn derive_cdi_private_key_seed(cdi_attest: &Cdi) -> Result<PrivateKeySeed> {
+ let mut seed = [0u8; PRIVATE_KEY_SEED_SIZE];
+ // SAFETY: The function writes to the buffer within the given bounds, and only reads the
+ // input values. The first argument context is not used in this function.
+ check_result(unsafe {
+ DiceDeriveCdiPrivateKeySeed(
+ ptr::null_mut(), // context
+ cdi_attest.as_ptr(),
+ seed.as_mut_ptr(),
+ )
+ })?;
+ Ok(seed)
+}
+
+/// Derives an ID from the given `cdi_public_key` value.
+pub fn derive_cdi_certificate_id(cdi_public_key: &[u8]) -> Result<DiceId> {
+ let mut id = [0u8; ID_SIZE];
+ // SAFETY: The function writes to the buffer within the given bounds, and only reads the
+ // input values. The first argument context is not used in this function.
+ check_result(unsafe {
+ DiceDeriveCdiCertificateId(
+ ptr::null_mut(), // context
+ cdi_public_key.as_ptr(),
+ cdi_public_key.len(),
+ id.as_mut_ptr(),
+ )
+ })?;
+ Ok(id)
+}
+
+/// Executes the main DICE flow.
+///
+/// Given a full set of input values and the current CDI values, computes the
+/// next CDI values and a matching certificate.
+/// Returns the actual size of the next CDI certificate.
+pub fn dice_main_flow(
+ current_cdi_attest: &Cdi,
+ current_cdi_seal: &Cdi,
+ input_values: &InputValues,
+ next_cdi_certificate: &mut [u8],
+ next_cdi_values: &mut CdiValues,
+) -> Result<usize> {
+ let mut next_cdi_certificate_actual_size = 0;
+ // SAFETY: The function only reads the current CDI values and inputs and writes
+ // to `next_cdi_certificate` and next CDI values within its bounds.
+ // The first argument can be null and is not used in the current implementation.
+ check_result(unsafe {
+ DiceMainFlow(
+ ptr::null_mut(), // context
+ current_cdi_attest.as_ptr(),
+ current_cdi_seal.as_ptr(),
+ input_values.as_ptr(),
+ next_cdi_certificate.len(),
+ next_cdi_certificate.as_mut_ptr(),
+ &mut next_cdi_certificate_actual_size,
+ next_cdi_values.cdi_attest.as_mut_ptr(),
+ next_cdi_values.cdi_seal.as_mut_ptr(),
+ )
+ })?;
+ Ok(next_cdi_certificate_actual_size)
+}
diff --git a/diced/open_dice/src/error.rs b/diced/open_dice/src/error.rs
new file mode 100644
index 0000000..7405c51
--- /dev/null
+++ b/diced/open_dice/src/error.rs
@@ -0,0 +1,71 @@
+// Copyright 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.
+
+//! Errors and relating functions thrown in this library.
+
+use open_dice_cbor_bindgen::DiceResult;
+use std::{fmt, result};
+
+#[cfg(feature = "std")]
+use std::error::Error;
+
+/// Error type used by DICE.
+#[derive(Debug)]
+pub enum DiceError {
+ /// Provided input was invalid.
+ InvalidInput,
+ /// Provided buffer was too small.
+ BufferTooSmall,
+ /// Platform error.
+ PlatformError,
+ /// The allocation of a ZVec failed.
+ #[cfg(feature = "std")]
+ ZVecError(keystore2_crypto::zvec::Error),
+}
+
+#[cfg(feature = "std")]
+impl From<keystore2_crypto::zvec::Error> for DiceError {
+ fn from(e: keystore2_crypto::zvec::Error) -> Self {
+ Self::ZVecError(e)
+ }
+}
+
+/// This makes `DiceError` accepted by anyhow.
+#[cfg(feature = "std")]
+impl Error for DiceError {}
+
+impl fmt::Display for DiceError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::InvalidInput => write!(f, "invalid input"),
+ Self::BufferTooSmall => write!(f, "buffer too small"),
+ Self::PlatformError => write!(f, "platform error"),
+ #[cfg(feature = "std")]
+ Self::ZVecError(e) => write!(f, "ZVec allocation failed {e}"),
+ }
+ }
+}
+
+/// DICE result type.
+pub type Result<T> = result::Result<T, DiceError>;
+
+/// Checks the given `DiceResult`. Returns an error if it's not OK.
+pub fn check_result(result: DiceResult) -> Result<()> {
+ match result {
+ DiceResult::kDiceResultOk => Ok(()),
+ DiceResult::kDiceResultInvalidInput => Err(DiceError::InvalidInput),
+ DiceResult::kDiceResultBufferTooSmall => Err(DiceError::BufferTooSmall),
+ DiceResult::kDiceResultPlatformError => Err(DiceError::PlatformError),
+ }
+}
diff --git a/diced/open_dice/src/lib.rs b/diced/open_dice/src/lib.rs
new file mode 100644
index 0000000..7e8c59a
--- /dev/null
+++ b/diced/open_dice/src/lib.rs
@@ -0,0 +1,42 @@
+// Copyright 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.
+
+//! Implements safe wrappers around the public API of libopen-dice for
+//! both std and nostd usages.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#[cfg(not(feature = "std"))]
+extern crate core as std;
+
+mod bcc;
+mod dice;
+mod error;
+mod ops;
+#[cfg(feature = "std")]
+mod retry;
+
+pub use bcc::{bcc_format_config_descriptor, bcc_main_flow};
+pub use dice::{
+ derive_cdi_certificate_id, derive_cdi_private_key_seed, dice_main_flow, Cdi, CdiValues, Config,
+ DiceMode, Hash, Hidden, InlineConfig, InputValues, PrivateKeySeed, CDI_SIZE, HASH_SIZE,
+ HIDDEN_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
+};
+pub use error::{check_result, DiceError, Result};
+pub use ops::{generate_certificate, hash, kdf};
+#[cfg(feature = "std")]
+pub use retry::{
+ retry_bcc_format_config_descriptor, retry_bcc_main_flow, retry_dice_main_flow,
+ retry_generate_certificate, OwnedDiceArtifacts,
+};
diff --git a/diced/open_dice/src/ops.rs b/diced/open_dice/src/ops.rs
new file mode 100644
index 0000000..3c0ef23
--- /dev/null
+++ b/diced/open_dice/src/ops.rs
@@ -0,0 +1,86 @@
+// Copyright 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 mirrors the content in open-dice/include/dice/ops.h
+//! It contains the set of functions that implement various operations that the
+//! main DICE functions depend on.
+
+use crate::dice::{Hash, InputValues, HASH_SIZE, PRIVATE_KEY_SEED_SIZE};
+use crate::error::{check_result, Result};
+use open_dice_cbor_bindgen::{DiceGenerateCertificate, DiceHash, DiceKdf};
+use std::ptr;
+
+/// Hashes the provided input using DICE's hash function `DiceHash`.
+pub fn hash(input: &[u8]) -> Result<Hash> {
+ let mut output: Hash = [0; HASH_SIZE];
+ // SAFETY: DiceHash takes a sized input buffer and writes to a constant-sized output buffer.
+ // The first argument context is not used in this function.
+ check_result(unsafe {
+ DiceHash(
+ ptr::null_mut(), // context
+ input.as_ptr(),
+ input.len(),
+ output.as_mut_ptr(),
+ )
+ })?;
+ Ok(output)
+}
+
+/// An implementation of HKDF-SHA512. Derives a key of `derived_key.len()` bytes from `ikm`, `salt`,
+/// and `info`. The derived key is written to the `derived_key`.
+pub fn kdf(ikm: &[u8], salt: &[u8], info: &[u8], derived_key: &mut [u8]) -> Result<()> {
+ // SAFETY: The function writes to the `derived_key`, within the given bounds, and only reads the
+ // input values. The first argument context is not used in this function.
+ check_result(unsafe {
+ DiceKdf(
+ ptr::null_mut(), // context
+ derived_key.len(),
+ ikm.as_ptr(),
+ ikm.len(),
+ salt.as_ptr(),
+ salt.len(),
+ info.as_ptr(),
+ info.len(),
+ derived_key.as_mut_ptr(),
+ )
+ })
+}
+
+/// Generates an X.509 certificate from the given `subject_private_key_seed` and
+/// `input_values`, and signed by `authority_private_key_seed`.
+/// The subject private key seed is supplied here so the implementation can choose
+/// between asymmetric mechanisms, for example ECDSA vs Ed25519.
+/// Returns the actual size of the generated certificate.
+pub fn generate_certificate(
+ subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+ authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+ input_values: &InputValues,
+ certificate: &mut [u8],
+) -> Result<usize> {
+ let mut certificate_actual_size = 0;
+ // SAFETY: The function writes to the `certificate` within the given bounds, and only reads the
+ // input values and the key seeds. The first argument context is not used in this function.
+ check_result(unsafe {
+ DiceGenerateCertificate(
+ ptr::null_mut(), // context
+ subject_private_key_seed.as_ptr(),
+ authority_private_key_seed.as_ptr(),
+ input_values.as_ptr(),
+ certificate.len(),
+ certificate.as_mut_ptr(),
+ &mut certificate_actual_size,
+ )
+ })?;
+ Ok(certificate_actual_size)
+}
diff --git a/diced/open_dice/src/retry.rs b/diced/open_dice/src/retry.rs
new file mode 100644
index 0000000..c28b691
--- /dev/null
+++ b/diced/open_dice/src/retry.rs
@@ -0,0 +1,143 @@
+// Copyright 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 implements a retry version for multiple DICE functions that
+//! require preallocated output buffer. As the retry functions require
+//! memory allocation on heap, currently we only expose these functions in
+//! std environment.
+
+use crate::bcc::{bcc_format_config_descriptor, bcc_main_flow};
+use crate::dice::{dice_main_flow, Cdi, CdiValues, InputValues, PRIVATE_KEY_SEED_SIZE};
+use crate::error::{DiceError, Result};
+use crate::ops::generate_certificate;
+use std::ffi::CStr;
+
+/// Artifacts stores a set of dice artifacts comprising CDI_ATTEST, CDI_SEAL,
+/// and the BCC formatted attestation certificate chain.
+/// As we align with the DICE standards today, this is the certificate chain
+/// is also called DICE certificate chain.
+pub struct OwnedDiceArtifacts {
+ /// CDI Values.
+ pub cdi_values: CdiValues,
+ /// Boot Certificate Chain.
+ pub bcc: Vec<u8>,
+}
+
+/// Retries the given function with bigger output buffer size.
+fn retry_with_bigger_buffer<F>(mut f: F) -> Result<Vec<u8>>
+where
+ F: FnMut(&mut Vec<u8>) -> Result<usize>,
+{
+ const INITIAL_BUFFER_SIZE: usize = 256;
+ const MAX_BUFFER_SIZE: usize = 64 * 1024 * 1024;
+
+ let mut buffer = vec![0u8; INITIAL_BUFFER_SIZE];
+ while buffer.len() <= MAX_BUFFER_SIZE {
+ match f(&mut buffer) {
+ Err(DiceError::BufferTooSmall) => {
+ let new_size = buffer.len() * 2;
+ buffer.resize(new_size, 0);
+ }
+ Err(e) => return Err(e),
+ Ok(actual_size) => {
+ if actual_size > buffer.len() {
+ panic!(
+ "actual_size larger than buffer size: open-dice function
+ may have written past the end of the buffer."
+ );
+ }
+ buffer.truncate(actual_size);
+ return Ok(buffer);
+ }
+ }
+ }
+ Err(DiceError::PlatformError)
+}
+
+/// Formats a configuration descriptor following the BCC's specification.
+pub fn retry_bcc_format_config_descriptor(
+ name: Option<&CStr>,
+ version: Option<u64>,
+ resettable: bool,
+) -> Result<Vec<u8>> {
+ retry_with_bigger_buffer(|buffer| {
+ bcc_format_config_descriptor(name, version, resettable, buffer)
+ })
+}
+
+/// Executes the main BCC flow.
+///
+/// Given a full set of input values along with the current BCC and CDI values,
+/// computes the next CDI values and matching updated BCC.
+pub fn retry_bcc_main_flow(
+ current_cdi_attest: &Cdi,
+ current_cdi_seal: &Cdi,
+ bcc: &[u8],
+ input_values: &InputValues,
+) -> Result<OwnedDiceArtifacts> {
+ let mut next_cdi_values = CdiValues::default();
+ let next_bcc = retry_with_bigger_buffer(|next_bcc| {
+ bcc_main_flow(
+ current_cdi_attest,
+ current_cdi_seal,
+ bcc,
+ input_values,
+ &mut next_cdi_values,
+ next_bcc,
+ )
+ })?;
+ Ok(OwnedDiceArtifacts { cdi_values: next_cdi_values, bcc: next_bcc })
+}
+
+/// Executes the main DICE flow.
+///
+/// Given a full set of input values and the current CDI values, computes the
+/// next CDI values and a matching certificate.
+pub fn retry_dice_main_flow(
+ current_cdi_attest: &Cdi,
+ current_cdi_seal: &Cdi,
+ input_values: &InputValues,
+) -> Result<(CdiValues, Vec<u8>)> {
+ let mut next_cdi_values = CdiValues::default();
+ let next_cdi_certificate = retry_with_bigger_buffer(|next_cdi_certificate| {
+ dice_main_flow(
+ current_cdi_attest,
+ current_cdi_seal,
+ input_values,
+ next_cdi_certificate,
+ &mut next_cdi_values,
+ )
+ })?;
+ Ok((next_cdi_values, next_cdi_certificate))
+}
+
+/// Generates an X.509 certificate from the given `subject_private_key_seed` and
+/// `input_values`, and signed by `authority_private_key_seed`.
+/// The subject private key seed is supplied here so the implementation can choose
+/// between asymmetric mechanisms, for example ECDSA vs Ed25519.
+/// Returns the generated certificate.
+pub fn retry_generate_certificate(
+ subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+ authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+ input_values: &InputValues,
+) -> Result<Vec<u8>> {
+ retry_with_bigger_buffer(|certificate| {
+ generate_certificate(
+ subject_private_key_seed,
+ authority_private_key_seed,
+ input_values,
+ certificate,
+ )
+ })
+}
diff --git a/diced/open_dice/tests/api_test.rs b/diced/open_dice/tests/api_test.rs
new file mode 100644
index 0000000..3823bb7
--- /dev/null
+++ b/diced/open_dice/tests/api_test.rs
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+use diced_open_dice::{
+ derive_cdi_certificate_id, hash, kdf, HASH_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
+};
+
+#[test]
+fn hash_succeeds() {
+ const EXPECTED_HASH: [u8; HASH_SIZE] = [
+ 0x30, 0x9e, 0xcc, 0x48, 0x9c, 0x12, 0xd6, 0xeb, 0x4c, 0xc4, 0x0f, 0x50, 0xc9, 0x02, 0xf2,
+ 0xb4, 0xd0, 0xed, 0x77, 0xee, 0x51, 0x1a, 0x7c, 0x7a, 0x9b, 0xcd, 0x3c, 0xa8, 0x6d, 0x4c,
+ 0xd8, 0x6f, 0x98, 0x9d, 0xd3, 0x5b, 0xc5, 0xff, 0x49, 0x96, 0x70, 0xda, 0x34, 0x25, 0x5b,
+ 0x45, 0xb0, 0xcf, 0xd8, 0x30, 0xe8, 0x1f, 0x60, 0x5d, 0xcf, 0x7d, 0xc5, 0x54, 0x2e, 0x93,
+ 0xae, 0x9c, 0xd7, 0x6f,
+ ];
+ assert_eq!(EXPECTED_HASH, hash(b"hello world").expect("hash failed"));
+}
+
+#[test]
+fn kdf_succeeds() {
+ let mut derived_key = [0u8; PRIVATE_KEY_SEED_SIZE];
+ kdf(b"myInitialKeyMaterial", b"mySalt", b"myInfo", &mut derived_key).unwrap();
+ const EXPECTED_DERIVED_KEY: [u8; PRIVATE_KEY_SEED_SIZE] = [
+ 0x91, 0x9b, 0x8d, 0x29, 0xc4, 0x1b, 0x93, 0xd7, 0xeb, 0x09, 0xfa, 0xd7, 0xc9, 0x87, 0xb0,
+ 0xd1, 0xcc, 0x26, 0xef, 0x07, 0x83, 0x42, 0xcf, 0xa3, 0x45, 0x0a, 0x57, 0xe9, 0x19, 0x86,
+ 0xef, 0x48,
+ ];
+ assert_eq!(EXPECTED_DERIVED_KEY, derived_key);
+}
+
+#[test]
+fn derive_cdi_certificate_id_succeeds() {
+ const EXPECTED_ID: [u8; ID_SIZE] = [
+ 0x7a, 0x36, 0x45, 0x2c, 0x02, 0xf6, 0x2b, 0xec, 0xf9, 0x80, 0x06, 0x75, 0x87, 0xa5, 0xc1,
+ 0x44, 0x0c, 0xd3, 0xc0, 0x6d,
+ ];
+ assert_eq!(EXPECTED_ID, derive_cdi_certificate_id(b"MyPubKey").unwrap());
+}
diff --git a/diced/open_dice_cbor/Android.bp b/diced/open_dice_cbor/Android.bp
index a84190a..e52af7d 100644
--- a/diced/open_dice_cbor/Android.bp
+++ b/diced/open_dice_cbor/Android.bp
@@ -22,14 +22,12 @@
srcs: ["lib.rs"],
rustlibs: [
+ "libdiced_open_dice",
// For ZVec
"libkeystore2_crypto_rust",
- "libopen_dice_bcc_bindgen",
"libopen_dice_cbor_bindgen",
- "libthiserror",
],
static_libs: [
- "libopen_dice_bcc",
"libopen_dice_cbor",
],
vendor_available: true,
@@ -46,14 +44,12 @@
test_suites: ["general-tests"],
auto_gen_config: true,
rustlibs: [
+ "libdiced_open_dice",
"libdiced_sample_inputs",
"libkeystore2_crypto_rust",
- "libopen_dice_bcc_bindgen",
"libopen_dice_cbor_bindgen",
- "libthiserror",
],
static_libs: [
- "libopen_dice_bcc",
"libopen_dice_cbor",
],
}
diff --git a/diced/open_dice_cbor/lib.rs b/diced/open_dice_cbor/lib.rs
index ffb8a48..8af903f 100644
--- a/diced/open_dice_cbor/lib.rs
+++ b/diced/open_dice_cbor/lib.rs
@@ -20,52 +20,30 @@
//! let context = dice::dice::OpenDiceCborContext::new()
//! let parent_cdi_attest = [1u8, dice::CDI_SIZE];
//! let parent_cdi_seal = [2u8, dice::CDI_SIZE];
-//! let input_values = dice::InputValuesOwned {
-//! code_hash: [3u8, dice::HASH_SIZE],
-//! config: dice::ConfigOwned::Descriptor("My descriptor".as_bytes().to_vec()),
-//! authority_hash: [0u8, dice::HASH_SIZE],
-//! mode: dice::Mode::Normal,
-//! hidden: [0u8, dice::HIDDEN_SIZE],
+//! let input_values = dice::InputValues::new(
+//! [3u8, dice::HASH_SIZE], // code_hash
+//! dice::Config::Descriptor(&"My descriptor".as_bytes().to_vec()),
+//! [0u8, dice::HASH_SIZE], // authority_hash
+//! dice::DiceMode::kDiceModeNormal,
+//! [0u8, dice::HIDDEN_SIZE], // hidden
//! };
//! let (cdi_attest, cdi_seal, cert_chain) = context
//! .main_flow(&parent_cdi_attest, &parent_cdi_seal, &input_values)?;
//! ```
-use keystore2_crypto::{zvec, ZVec};
-use open_dice_bcc_bindgen::BccMainFlow;
-use open_dice_cbor_bindgen::{
- DiceConfigType, DiceDeriveCdiCertificateId, DiceDeriveCdiPrivateKeySeed,
- DiceGenerateCertificate, DiceHash, DiceInputValues, DiceKdf, DiceKeypairFromSeed, DiceMainFlow,
- DiceMode, DiceResult, DiceSign, DiceVerify, DICE_CDI_SIZE, DICE_HASH_SIZE, DICE_HIDDEN_SIZE,
- DICE_ID_SIZE, DICE_INLINE_CONFIG_SIZE, DICE_PRIVATE_KEY_SEED_SIZE, DICE_PRIVATE_KEY_SIZE,
- DICE_PUBLIC_KEY_SIZE, DICE_SIGNATURE_SIZE,
+pub use diced_open_dice::{
+ check_result, derive_cdi_private_key_seed, hash, retry_bcc_format_config_descriptor,
+ retry_bcc_main_flow, retry_dice_main_flow, Config, DiceError, Hash, Hidden, InputValues,
+ OwnedDiceArtifacts, Result, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE, PRIVATE_KEY_SEED_SIZE,
};
+use keystore2_crypto::ZVec;
+pub use open_dice_cbor_bindgen::DiceMode;
use open_dice_cbor_bindgen::{
- DiceConfigType_kDiceConfigTypeDescriptor as DICE_CONFIG_TYPE_DESCRIPTOR,
- DiceConfigType_kDiceConfigTypeInline as DICE_CONFIG_TYPE_INLINE,
- DiceMode_kDiceModeDebug as DICE_MODE_DEBUG,
- DiceMode_kDiceModeMaintenance as DICE_MODE_RECOVERY,
- DiceMode_kDiceModeNormal as DICE_MODE_NORMAL,
- DiceMode_kDiceModeNotInitialized as DICE_MODE_NOT_CONFIGURED,
- DiceResult_kDiceResultBufferTooSmall as DICE_RESULT_BUFFER_TOO_SMALL,
- DiceResult_kDiceResultInvalidInput as DICE_RESULT_INVALID_INPUT,
- DiceResult_kDiceResultOk as DICE_RESULT_OK,
- DiceResult_kDiceResultPlatformError as DICE_RESULT_PLATFORM_ERROR,
+ DiceKeypairFromSeed, DiceSign, DiceVerify, DICE_PRIVATE_KEY_SIZE, DICE_PUBLIC_KEY_SIZE,
+ DICE_SIGNATURE_SIZE,
};
-use std::ffi::{c_void, NulError};
+use std::ffi::c_void;
-/// The size of a DICE hash.
-pub const HASH_SIZE: usize = DICE_HASH_SIZE as usize;
-/// The size of the DICE hidden value.
-pub const HIDDEN_SIZE: usize = DICE_HIDDEN_SIZE as usize;
-/// The size of a DICE inline config.
-pub const INLINE_CONFIG_SIZE: usize = DICE_INLINE_CONFIG_SIZE as usize;
-/// The size of a private key seed.
-pub const PRIVATE_KEY_SEED_SIZE: usize = DICE_PRIVATE_KEY_SEED_SIZE as usize;
-/// The size of a CDI.
-pub const CDI_SIZE: usize = DICE_CDI_SIZE as usize;
-/// The size of an ID.
-pub const ID_SIZE: usize = DICE_ID_SIZE as usize;
/// The size of a private key.
pub const PRIVATE_KEY_SIZE: usize = DICE_PRIVATE_KEY_SIZE as usize;
/// The size of a public key.
@@ -73,280 +51,6 @@
/// The size of a signature.
pub const SIGNATURE_SIZE: usize = DICE_SIGNATURE_SIZE as usize;
-/// Open dice wrapper error type.
-#[derive(Debug, thiserror::Error, PartialEq, Eq)]
-pub enum Error {
- /// The libopen-dice backend reported InvalidInput.
- #[error("Open dice backend: Invalid input")]
- InvalidInput,
- /// The libopen-dice backend reported BufferTooSmall.
- #[error("Open dice backend: Buffer too small")]
- BufferTooSmall,
- /// The libopen-dice backend reported PlatformError.
- #[error("Open dice backend: Platform error")]
- PlatformError,
- /// The libopen-dice backend reported an error that is outside of the defined range of errors.
- /// The returned error code is embedded in this value.
- #[error("Open dice backend returned an unexpected error code: {0:?}")]
- Unexpected(u32),
-
- /// The allocation of a ZVec failed. Most likely due to a failure during the call to mlock.
- #[error("ZVec allocation failed")]
- ZVec(#[from] zvec::Error),
-
- /// Functions that have to convert str to CString may fail if the string has an interior
- /// nul byte.
- #[error("Input string has an interior nul byte.")]
- CStrNulError(#[from] NulError),
-}
-
-/// Open dice result type.
-pub type Result<T> = std::result::Result<T, Error>;
-
-impl From<DiceResult> for Error {
- fn from(result: DiceResult) -> Self {
- match result {
- DICE_RESULT_INVALID_INPUT => Error::InvalidInput,
- DICE_RESULT_BUFFER_TOO_SMALL => Error::BufferTooSmall,
- DICE_RESULT_PLATFORM_ERROR => Error::PlatformError,
- r => Error::Unexpected(r),
- }
- }
-}
-
-fn check_result(result: DiceResult) -> Result<()> {
- if result == DICE_RESULT_OK {
- Ok(())
- } else {
- Err(result.into())
- }
-}
-
-/// Configuration descriptor for dice input values.
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub enum Config<'a> {
- /// A reference to an inline descriptor.
- Inline(&'a [u8; INLINE_CONFIG_SIZE]),
- /// A reference to a free form descriptor that will be hashed by the implementation.
- Descriptor(&'a [u8]),
-}
-
-enum ConfigOwned {
- Inline([u8; INLINE_CONFIG_SIZE]),
- Descriptor(Vec<u8>),
-}
-
-impl Config<'_> {
- fn get_type(&self) -> DiceConfigType {
- match self {
- Self::Inline(_) => DICE_CONFIG_TYPE_INLINE,
- Self::Descriptor(_) => DICE_CONFIG_TYPE_DESCRIPTOR,
- }
- }
-
- fn get_inline(&self) -> [u8; INLINE_CONFIG_SIZE] {
- match self {
- Self::Inline(inline) => **inline,
- _ => [0u8; INLINE_CONFIG_SIZE],
- }
- }
-
- fn get_descriptor_as_ptr(&self) -> *const u8 {
- match self {
- Self::Descriptor(descriptor) => descriptor.as_ptr(),
- _ => std::ptr::null(),
- }
- }
-
- fn get_descriptor_size(&self) -> usize {
- match self {
- Self::Descriptor(descriptor) => descriptor.len(),
- _ => 0,
- }
- }
-}
-
-impl From<Config<'_>> for ConfigOwned {
- fn from(config: Config) -> Self {
- match config {
- Config::Inline(inline) => ConfigOwned::Inline(*inline),
- Config::Descriptor(descriptor) => ConfigOwned::Descriptor(descriptor.to_owned()),
- }
- }
-}
-
-/// DICE modes as defined here:
-/// https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/specification.md#mode-value-details
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum Mode {
- /// See documentation linked above.
- NotConfigured = 0,
- /// See documentation linked above.
- Normal = 1,
- /// See documentation linked above.
- Debug = 2,
- /// See documentation linked above.
- Recovery = 3,
-}
-
-impl Mode {
- fn get_internal(&self) -> DiceMode {
- match self {
- Self::NotConfigured => DICE_MODE_NOT_CONFIGURED,
- Self::Normal => DICE_MODE_NORMAL,
- Self::Debug => DICE_MODE_DEBUG,
- Self::Recovery => DICE_MODE_RECOVERY,
- }
- }
-}
-
-/// This trait allows API users to supply DICE input values without copying.
-pub trait InputValues {
- /// Returns the code hash.
- fn code_hash(&self) -> &[u8; HASH_SIZE];
- /// Returns the config.
- fn config(&self) -> Config;
- /// Returns the authority hash.
- fn authority_hash(&self) -> &[u8; HASH_SIZE];
- /// Returns the authority descriptor.
- fn authority_descriptor(&self) -> Option<&[u8]>;
- /// Returns the mode.
- fn mode(&self) -> Mode;
- /// Returns the hidden value.
- fn hidden(&self) -> &[u8; HIDDEN_SIZE];
-}
-
-/// An owning convenience type implementing `InputValues`.
-pub struct InputValuesOwned {
- code_hash: [u8; HASH_SIZE],
- config: ConfigOwned,
- authority_hash: [u8; HASH_SIZE],
- authority_descriptor: Option<Vec<u8>>,
- mode: Mode,
- hidden: [u8; HIDDEN_SIZE],
-}
-
-impl InputValuesOwned {
- /// Construct a new instance of InputValuesOwned.
- pub fn new(
- code_hash: [u8; HASH_SIZE],
- config: Config,
- authority_hash: [u8; HASH_SIZE],
- authority_descriptor: Option<Vec<u8>>,
- mode: Mode,
- hidden: [u8; HIDDEN_SIZE],
- ) -> Self {
- Self {
- code_hash,
- config: config.into(),
- authority_hash,
- authority_descriptor,
- mode,
- hidden,
- }
- }
-}
-
-impl InputValues for InputValuesOwned {
- fn code_hash(&self) -> &[u8; HASH_SIZE] {
- &self.code_hash
- }
- fn config(&self) -> Config {
- match &self.config {
- ConfigOwned::Inline(inline) => Config::Inline(inline),
- ConfigOwned::Descriptor(descriptor) => Config::Descriptor(descriptor.as_slice()),
- }
- }
- fn authority_hash(&self) -> &[u8; HASH_SIZE] {
- &self.authority_hash
- }
- fn authority_descriptor(&self) -> Option<&[u8]> {
- self.authority_descriptor.as_deref()
- }
- fn mode(&self) -> Mode {
- self.mode
- }
- fn hidden(&self) -> &[u8; HIDDEN_SIZE] {
- &self.hidden
- }
-}
-
-fn call_with_input_values<T: InputValues + ?Sized, F, R>(input_values: &T, f: F) -> Result<R>
-where
- F: FnOnce(*const DiceInputValues) -> Result<R>,
-{
- let input_values = DiceInputValues {
- code_hash: *input_values.code_hash(),
- code_descriptor: std::ptr::null(),
- code_descriptor_size: 0,
- config_type: input_values.config().get_type(),
- config_value: input_values.config().get_inline(),
- config_descriptor: input_values.config().get_descriptor_as_ptr(),
- config_descriptor_size: input_values.config().get_descriptor_size(),
- authority_hash: *input_values.authority_hash(),
- authority_descriptor: input_values
- .authority_descriptor()
- .map_or_else(std::ptr::null, <[u8]>::as_ptr),
- authority_descriptor_size: input_values.authority_descriptor().map_or(0, <[u8]>::len),
- mode: input_values.mode().get_internal(),
- hidden: *input_values.hidden(),
- };
-
- f(&input_values as *const DiceInputValues)
-}
-
-/// Multiple of the open dice function required preallocated output buffer
-/// which may be too small, this function implements the retry logic to handle
-/// too small buffer allocations.
-/// The callback `F` must expect a mutable reference to a buffer and a size hint
-/// field. The callback is called repeatedly as long as it returns
-/// `Err(Error::BufferTooSmall)`. If the size hint remains 0, the buffer size is
-/// doubled with each iteration. If the size hint is set by the callback, the buffer
-/// will be set to accommodate at least this many bytes.
-/// If the callback returns `Ok(())`, the buffer is truncated to the size hint
-/// exactly.
-/// The function panics if the callback returns `Ok(())` and the size hint is
-/// larger than the buffer size.
-fn retry_while_adjusting_output_buffer<F>(mut f: F) -> Result<Vec<u8>>
-where
- F: FnMut(&mut Vec<u8>, &mut usize) -> Result<()>,
-{
- let mut buffer = vec![0; INITIAL_OUT_BUFFER_SIZE];
- let mut actual_size: usize = 0;
- loop {
- match f(&mut buffer, &mut actual_size) {
- // If Error::BufferTooSmall was returned, the allocated certificate
- // buffer was to small for the output. So the buffer is resized to the actual
- // size, and a second attempt is made with the new buffer.
- Err(Error::BufferTooSmall) => {
- let new_size = if actual_size == 0 {
- // Due to an off spec implementation of open dice cbor, actual size
- // does not return the required size if the buffer was too small. So
- // we have to try and approach it gradually.
- buffer.len() * 2
- } else {
- actual_size
- };
- buffer.resize(new_size, 0);
- continue;
- }
- Err(e) => return Err(e),
- Ok(()) => {
- if actual_size > buffer.len() {
- panic!(
- "actual_size larger than buffer size: open-dice function
- may have written past the end of the buffer."
- );
- }
- // Truncate the certificate buffer to the actual size because it may be
- // smaller than the original allocation.
- buffer.truncate(actual_size);
- return Ok(buffer);
- }
- }
- }
-}
-
/// Some libopen-dice variants use a context. Developers that want to customize these
/// bindings may want to implement their own Context factory that creates a context
/// useable by their preferred backend.
@@ -393,165 +97,10 @@
/// Type alias for Vec<u8> indicating that it holds a BCC certificate chain.
pub type Bcc = Vec<u8>;
-const INITIAL_OUT_BUFFER_SIZE: usize = 1024;
-
/// ContextImpl is a mixin trait that implements the safe wrappers around the open dice
/// library calls. Implementations must implement Context::get_context(). As of
/// this writing, the only implementation is OpenDiceCborContext, which returns NULL.
pub trait ContextImpl: Context + Send {
- /// Safe wrapper around open-dice DiceDeriveCdiPrivateKeySeed, see open dice
- /// documentation for details.
- fn derive_cdi_private_key_seed(&mut self, cdi_attest: &[u8; CDI_SIZE]) -> Result<ZVec> {
- let mut seed = ZVec::new(PRIVATE_KEY_SEED_SIZE)?;
- // SAFETY:
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument is expected to be a const array of size CDI_SIZE.
- // * The third argument is expected to be a non const array of size
- // PRIVATE_KEY_SEED_SIZE which is fulfilled if the call to ZVec::new above
- // succeeds.
- // * No pointers are expected to be valid beyond the scope of the function
- // call.
- check_result(unsafe {
- DiceDeriveCdiPrivateKeySeed(self.get_context(), cdi_attest.as_ptr(), seed.as_mut_ptr())
- })?;
- Ok(seed)
- }
-
- /// Safe wrapper around open-dice DiceDeriveCdiCertificateId, see open dice
- /// documentation for details.
- fn derive_cdi_certificate_id(&mut self, cdi_public_key: &[u8]) -> Result<ZVec> {
- let mut id = ZVec::new(ID_SIZE)?;
- // SAFETY:
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument is expected to be a const array with a size given by the
- // third argument.
- // * The fourth argument is expected to be a non const array of size
- // ID_SIZE which is fulfilled if the call to ZVec::new above succeeds.
- // * No pointers are expected to be valid beyond the scope of the function
- // call.
- check_result(unsafe {
- DiceDeriveCdiCertificateId(
- self.get_context(),
- cdi_public_key.as_ptr(),
- cdi_public_key.len(),
- id.as_mut_ptr(),
- )
- })?;
- Ok(id)
- }
-
- /// Safe wrapper around open-dice DiceMainFlow, see open dice
- /// documentation for details.
- /// Returns a tuple of:
- /// * The next attestation CDI,
- /// * the next seal CDI, and
- /// * the next attestation certificate.
- /// `(next_attest_cdi, next_seal_cdi, next_attestation_cert)`
- fn main_flow<T: InputValues + ?Sized>(
- &mut self,
- current_cdi_attest: &[u8; CDI_SIZE],
- current_cdi_seal: &[u8; CDI_SIZE],
- input_values: &T,
- ) -> Result<(CdiAttest, CdiSeal, Cert)> {
- let mut next_attest = CdiAttest::new(CDI_SIZE)?;
- let mut next_seal = CdiSeal::new(CDI_SIZE)?;
-
- // SAFETY (DiceMainFlow):
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument and the third argument are const arrays of size CDI_SIZE.
- // This is fulfilled as per the definition of the arguments `current_cdi_attest`
- // and `current_cdi_seal.
- // * The fourth argument is a pointer to `DiceInputValues`. It, and its indirect
- // references must be valid for the duration of the function call which
- // is guaranteed by `call_with_input_values` which puts `DiceInputValues`
- // on the stack and initializes it from the `input_values` argument which
- // implements the `InputValues` trait.
- // * The fifth and sixth argument are the length of and the pointer to the
- // allocated certificate buffer respectively. They are used to return
- // the generated certificate.
- // * The seventh argument is a pointer to a mutable usize object. It is
- // used to return the actual size of the output certificate.
- // * The eighth argument and the ninth argument are pointers to mutable buffers of size
- // CDI_SIZE. This is fulfilled if the allocation above succeeded.
- // * No pointers are expected to be valid beyond the scope of the function
- // call.
- call_with_input_values(input_values, |input_values| {
- let cert = retry_while_adjusting_output_buffer(|cert, actual_size| {
- check_result(unsafe {
- DiceMainFlow(
- self.get_context(),
- current_cdi_attest.as_ptr(),
- current_cdi_seal.as_ptr(),
- input_values,
- cert.len(),
- cert.as_mut_ptr(),
- actual_size as *mut _,
- next_attest.as_mut_ptr(),
- next_seal.as_mut_ptr(),
- )
- })
- })?;
- Ok((next_attest, next_seal, cert))
- })
- }
-
- /// Safe wrapper around open-dice DiceHash, see open dice
- /// documentation for details.
- fn hash(&mut self, input: &[u8]) -> Result<Vec<u8>> {
- let mut output: Vec<u8> = vec![0; HASH_SIZE];
-
- // SAFETY:
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument and the third argument are the pointer to and length of the given
- // input buffer respectively.
- // * The fourth argument must be a pointer to a mutable buffer of size HASH_SIZE
- // which is fulfilled by the allocation above.
- check_result(unsafe {
- DiceHash(self.get_context(), input.as_ptr(), input.len(), output.as_mut_ptr())
- })?;
- Ok(output)
- }
-
- /// Safe wrapper around open-dice DiceKdf, see open dice
- /// documentation for details.
- fn kdf(&mut self, length: usize, input_key: &[u8], salt: &[u8], info: &[u8]) -> Result<ZVec> {
- let mut output = ZVec::new(length)?;
-
- // SAFETY:
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument is primitive.
- // * The third argument and the fourth argument are the pointer to and length of the given
- // input key.
- // * The fifth argument and the sixth argument are the pointer to and length of the given
- // salt.
- // * The seventh argument and the eighth argument are the pointer to and length of the
- // given info field.
- // * The ninth argument is a pointer to the output buffer which must have the
- // length given by the `length` argument (see second argument). This is
- // fulfilled if the allocation of `output` succeeds.
- // * All pointers must be valid for the duration of the function call, but not
- // longer.
- check_result(unsafe {
- DiceKdf(
- self.get_context(),
- length,
- input_key.as_ptr(),
- input_key.len(),
- salt.as_ptr(),
- salt.len(),
- info.as_ptr(),
- info.len(),
- output.as_mut_ptr(),
- )
- })?;
- Ok(output)
- }
-
/// Safe wrapper around open-dice DiceKeyPairFromSeed, see open dice
/// documentation for details.
fn keypair_from_seed(&mut self, seed: &[u8; PRIVATE_KEY_SEED_SIZE]) -> Result<(Vec<u8>, ZVec)> {
@@ -633,162 +182,6 @@
)
})
}
-
- /// Safe wrapper around open-dice DiceGenerateCertificate, see open dice
- /// documentation for details.
- fn generate_certificate<T: InputValues>(
- &mut self,
- subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
- authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
- input_values: &T,
- ) -> Result<Vec<u8>> {
- // SAFETY (DiceMainFlow):
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument and the third argument are const arrays of size
- // `PRIVATE_KEY_SEED_SIZE`. This is fulfilled as per the definition of the arguments.
- // * The fourth argument is a pointer to `DiceInputValues` it, and its indirect
- // references must be valid for the duration of the function call which
- // is guaranteed by `call_with_input_values` which puts `DiceInputValues`
- // on the stack and initializes it from the `input_values` argument which
- // implements the `InputValues` trait.
- // * The fifth argument and the sixth argument are the length of and the pointer to the
- // allocated certificate buffer respectively. They are used to return
- // the generated certificate.
- // * The seventh argument is a pointer to a mutable usize object. It is
- // used to return the actual size of the output certificate.
- // * All pointers must be valid for the duration of the function call but not beyond.
- call_with_input_values(input_values, |input_values| {
- let cert = retry_while_adjusting_output_buffer(|cert, actual_size| {
- check_result(unsafe {
- DiceGenerateCertificate(
- self.get_context(),
- subject_private_key_seed.as_ptr(),
- authority_private_key_seed.as_ptr(),
- input_values,
- cert.len(),
- cert.as_mut_ptr(),
- actual_size as *mut _,
- )
- })
- })?;
- Ok(cert)
- })
- }
-
- /// Safe wrapper around open-dice BccDiceMainFlow, see open dice
- /// documentation for details.
- /// Returns a tuple of:
- /// * The next attestation CDI,
- /// * the next seal CDI, and
- /// * the next bcc adding the new certificate to the given bcc.
- /// `(next_attest_cdi, next_seal_cdi, next_bcc)`
- fn bcc_main_flow<T: InputValues + ?Sized>(
- &mut self,
- current_cdi_attest: &[u8; CDI_SIZE],
- current_cdi_seal: &[u8; CDI_SIZE],
- bcc: &[u8],
- input_values: &T,
- ) -> Result<(CdiAttest, CdiSeal, Bcc)> {
- let mut next_attest = CdiAttest::new(CDI_SIZE)?;
- let mut next_seal = CdiSeal::new(CDI_SIZE)?;
-
- // SAFETY (BccMainFlow):
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument and the third argument are const arrays of size CDI_SIZE.
- // This is fulfilled as per the definition of the arguments `current_cdi_attest`
- // and `current_cdi_seal`.
- // * The fourth argument and the fifth argument are the pointer to and size of the buffer
- // holding the current bcc.
- // * The sixth argument is a pointer to `DiceInputValues` it, and its indirect
- // references must be valid for the duration of the function call which
- // is guaranteed by `call_with_input_values` which puts `DiceInputValues`
- // on the stack and initializes it from the `input_values` argument which
- // implements the `InputValues` trait.
- // * The seventh argument and the eighth argument are the length of and the pointer to the
- // allocated certificate buffer respectively. They are used to return the generated
- // certificate.
- // * The ninth argument is a pointer to a mutable usize object. It is
- // used to return the actual size of the output certificate.
- // * The tenth argument and the eleventh argument are pointers to mutable buffers of
- // size CDI_SIZE. This is fulfilled if the allocation above succeeded.
- // * No pointers are expected to be valid beyond the scope of the function
- // call.
- call_with_input_values(input_values, |input_values| {
- let next_bcc = retry_while_adjusting_output_buffer(|next_bcc, actual_size| {
- check_result(unsafe {
- BccMainFlow(
- self.get_context(),
- current_cdi_attest.as_ptr(),
- current_cdi_seal.as_ptr(),
- bcc.as_ptr(),
- bcc.len(),
- input_values,
- next_bcc.len(),
- next_bcc.as_mut_ptr(),
- actual_size as *mut _,
- next_attest.as_mut_ptr(),
- next_seal.as_mut_ptr(),
- )
- })
- })?;
- Ok((next_attest, next_seal, next_bcc))
- })
- }
-}
-
-/// This submodule provides additional support for the Boot Certificate Chain (BCC)
-/// specification.
-/// See https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
-pub mod bcc {
- use super::{check_result, retry_while_adjusting_output_buffer, Result};
- use open_dice_bcc_bindgen::{
- BccConfigValues, BccFormatConfigDescriptor, BCC_INPUT_COMPONENT_NAME,
- BCC_INPUT_COMPONENT_VERSION, BCC_INPUT_RESETTABLE,
- };
- use std::ffi::CString;
-
- /// Safe wrapper around BccFormatConfigDescriptor, see open dice documentation for details.
- pub fn format_config_descriptor(
- component_name: Option<&str>,
- component_version: Option<u64>,
- resettable: bool,
- ) -> Result<Vec<u8>> {
- let component_name = match component_name {
- Some(n) => Some(CString::new(n)?),
- None => None,
- };
- let input = BccConfigValues {
- inputs: if component_name.is_some() { BCC_INPUT_COMPONENT_NAME } else { 0 }
- | if component_version.is_some() { BCC_INPUT_COMPONENT_VERSION } else { 0 }
- | if resettable { BCC_INPUT_RESETTABLE } else { 0 },
- // SAFETY: The as_ref() in the line below is vital to keep the component_name object
- // alive. Removing as_ref will move the component_name and the pointer will
- // become invalid after this statement.
- component_name: component_name.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
- component_version: component_version.unwrap_or(0),
- };
-
- // SAFETY:
- // * The first argument is a pointer to the BccConfigValues input assembled above.
- // It and its indirections must be valid for the duration of the function call.
- // * The second argument and the third argument are the length of and the pointer to the
- // allocated output buffer respectively. The buffer must be at least as long
- // as indicated by the size argument.
- // * The forth argument is a pointer to the actual size returned by the function.
- // * All pointers must be valid for the duration of the function call but not beyond.
- retry_while_adjusting_output_buffer(|config_descriptor, actual_size| {
- check_result(unsafe {
- BccFormatConfigDescriptor(
- &input as *const BccConfigValues,
- config_descriptor.len(),
- config_descriptor.as_mut_ptr(),
- actual_size as *mut _,
- )
- })
- })
- }
}
#[cfg(test)]
@@ -840,12 +233,12 @@
#[test]
fn hash_derive_sign_verify() {
let mut ctx = OpenDiceCborContext::new();
- let seed = ctx.hash("MySeedString".as_bytes()).unwrap();
+ let seed = diced_open_dice::hash("MySeedString".as_bytes()).unwrap();
assert_eq!(seed, SEED_TEST_VECTOR);
let cdi_attest = &seed[..CDI_SIZE];
assert_eq!(cdi_attest, CDI_ATTEST_TEST_VECTOR);
let cdi_private_key_seed =
- ctx.derive_cdi_private_key_seed(cdi_attest.try_into().unwrap()).unwrap();
+ derive_cdi_private_key_seed(cdi_attest.try_into().unwrap()).unwrap();
assert_eq!(&cdi_private_key_seed[..], CDI_PRIVATE_KEY_SEED_TEST_VECTOR);
let (pub_key, priv_key) =
ctx.keypair_from_seed(cdi_private_key_seed[..].try_into().unwrap()).unwrap();
@@ -997,41 +390,9 @@
// and changes in any of those functions.
#[test]
fn main_flow_and_bcc_main_flow() {
- let (cdi_attest, cdi_seal, bcc) = make_sample_bcc_and_cdis().unwrap();
- assert_eq!(&cdi_attest[..], SAMPLE_CDI_ATTEST_TEST_VECTOR);
- assert_eq!(&cdi_seal[..], SAMPLE_CDI_SEAL_TEST_VECTOR);
- assert_eq!(&bcc[..], SAMPLE_BCC_TEST_VECTOR);
- }
-
- static DERIVED_KEY_TEST_VECTOR: &[u8] = &[
- 0x0e, 0xd6, 0x07, 0x0e, 0x1c, 0x38, 0x2c, 0x76, 0x13, 0xc6, 0x76, 0x25, 0x7e, 0x07, 0x6f,
- 0xdb, 0x1d, 0xb1, 0x0f, 0x3f, 0xed, 0xc5, 0x2b, 0x95, 0xd1, 0x32, 0xf1, 0x63, 0x2f, 0x2a,
- 0x01, 0x5e,
- ];
-
- #[test]
- fn kdf() {
- let mut ctx = OpenDiceCborContext::new();
- let derived_key = ctx
- .kdf(
- PRIVATE_KEY_SEED_SIZE,
- "myKey".as_bytes(),
- "mySalt".as_bytes(),
- "myInfo".as_bytes(),
- )
- .unwrap();
- assert_eq!(&derived_key[..], DERIVED_KEY_TEST_VECTOR);
- }
-
- static CERT_ID_TEST_VECTOR: &[u8] = &[
- 0x7a, 0x36, 0x45, 0x2c, 0x02, 0xf6, 0x2b, 0xec, 0xf9, 0x80, 0x06, 0x75, 0x87, 0xa5, 0xc1,
- 0x44, 0x0c, 0xd3, 0xc0, 0x6d,
- ];
-
- #[test]
- fn derive_cdi_certificate_id() {
- let mut ctx = OpenDiceCborContext::new();
- let cert_id = ctx.derive_cdi_certificate_id("MyPubKey".as_bytes()).unwrap();
- assert_eq!(&cert_id[..], CERT_ID_TEST_VECTOR);
+ let dice_artifacts = make_sample_bcc_and_cdis().unwrap();
+ assert_eq!(&dice_artifacts.cdi_values.cdi_attest, SAMPLE_CDI_ATTEST_TEST_VECTOR);
+ assert_eq!(&dice_artifacts.cdi_values.cdi_seal, SAMPLE_CDI_SEAL_TEST_VECTOR);
+ assert_eq!(&dice_artifacts.bcc, SAMPLE_BCC_TEST_VECTOR);
}
}
diff --git a/diced/src/diced_client_test.rs b/diced/src/diced_client_test.rs
index 3915508..054b2d1 100644
--- a/diced/src/diced_client_test.rs
+++ b/diced/src/diced_client_test.rs
@@ -22,6 +22,7 @@
use diced_open_dice_cbor as dice;
use nix::libc::uid_t;
use std::convert::TryInto;
+use std::ffi::CString;
static DICE_NODE_SERVICE_NAME: &str = "android.security.dice.IDiceNode";
static DICE_MAINTENANCE_SERVICE_NAME: &str = "android.security.dice.IDiceMaintenance";
@@ -52,11 +53,7 @@
diced_utils::ResidentArtifacts::new(&former.cdiAttest, &former.cdiSeal, &former.bcc.data)
.unwrap();
- let input_values: Vec<diced_utils::InputValues> =
- input_values.iter().map(|v| v.into()).collect();
-
- let artifacts =
- artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
+ let artifacts = artifacts.execute_steps(input_values.iter()).unwrap();
let (cdi_attest, cdi_seal, bcc) = artifacts.into_tuple();
let from_former = diced_utils::make_bcc_handover(
cdi_attest[..].try_into().unwrap(),
@@ -96,11 +93,7 @@
)
.unwrap();
- let input_values: Vec<diced_utils::InputValues> =
- input_values.iter().map(|v| v.into()).collect();
-
- let artifacts =
- artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
+ let artifacts = artifacts.execute_steps(input_values.iter()).unwrap();
let (cdi_attest, cdi_seal, bcc) = artifacts.into_tuple();
let from_former = diced_utils::make_bcc_handover(
cdi_attest[..].try_into().unwrap(),
@@ -114,10 +107,11 @@
}
fn client_input_values(uid: uid_t) -> BinderInputValues {
+ let desc = CString::new(format!("{}", uid)).unwrap();
BinderInputValues {
codeHash: [0; dice::HASH_SIZE],
config: BinderConfig {
- desc: dice::bcc::format_config_descriptor(Some(&format!("{}", uid)), None, true)
+ desc: dice::retry_bcc_format_config_descriptor(Some(desc.as_c_str()), None, true)
.unwrap(),
},
authorityHash: [0; dice::HASH_SIZE],
@@ -160,11 +154,8 @@
.unwrap();
let client = [client];
- let input_values: Vec<diced_utils::InputValues> =
- input_values.iter().chain(client.iter()).map(|v| v.into()).collect();
- let artifacts =
- artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
+ let artifacts = artifacts.execute_steps(input_values.iter().chain(client.iter())).unwrap();
let (cdi_attest, cdi_seal, bcc) = artifacts.into_tuple();
let from_former = diced_utils::make_bcc_handover(
cdi_attest[..].try_into().unwrap(),
diff --git a/diced/src/diced_main.rs b/diced/src/diced_main.rs
deleted file mode 100644
index c2cf02c..0000000
--- a/diced/src/diced_main.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2021, 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.
-
-//! Main entry point for diced, the friendly neighborhood DICE service.
-
-use binder::get_interface;
-use diced::{DiceMaintenance, DiceNode, DiceNodeImpl, ProxyNodeHal, ResidentNode};
-use std::convert::TryInto;
-use std::panic;
-use std::sync::Arc;
-
-static DICE_NODE_SERVICE_NAME: &str = "android.security.dice.IDiceNode";
-static DICE_MAINTENANCE_SERVICE_NAME: &str = "android.security.dice.IDiceMaintenance";
-static DICE_HAL_SERVICE_NAME: &str = "android.hardware.security.dice.IDiceDevice/default";
-
-fn main() {
- android_logger::init_once(
- android_logger::Config::default().with_tag("diced").with_min_level(log::Level::Debug),
- );
- // Redirect panic messages to logcat.
- panic::set_hook(Box::new(|panic_info| {
- log::error!("{}", panic_info);
- }));
-
- // Saying hi.
- log::info!("Diced, your friendly neighborhood DICE service, is starting.");
-
- let node_impl: Arc<dyn DiceNodeImpl + Send + Sync> = match get_interface(DICE_HAL_SERVICE_NAME)
- {
- Ok(dice_device) => {
- Arc::new(ProxyNodeHal::new(dice_device).expect("Failed to construct a proxy node."))
- }
- Err(e) => {
- log::warn!("Failed to connect to DICE HAL: {:?}", e);
- log::warn!("Using sample dice artifacts.");
- let (cdi_attest, cdi_seal, bcc) = diced_sample_inputs::make_sample_bcc_and_cdis()
- .expect("Failed to create sample dice artifacts.");
- Arc::new(
- ResidentNode::new(
- cdi_attest[..]
- .try_into()
- .expect("Failed to convert cdi_attest into array ref."),
- cdi_seal[..].try_into().expect("Failed to convert cdi_seal into array ref."),
- bcc,
- )
- .expect("Failed to construct a resident node."),
- )
- }
- };
-
- let node = DiceNode::new_as_binder(node_impl.clone())
- .expect("Failed to create IDiceNode service instance.");
-
- let maintenance = DiceMaintenance::new_as_binder(node_impl)
- .expect("Failed to create IDiceMaintenance service instance.");
-
- binder::add_service(DICE_NODE_SERVICE_NAME, node.as_binder())
- .expect("Failed to register IDiceNode Service");
-
- binder::add_service(DICE_MAINTENANCE_SERVICE_NAME, maintenance.as_binder())
- .expect("Failed to register IDiceMaintenance Service");
-
- log::info!("Joining thread pool now.");
- binder::ProcessState::join_thread_pool();
-}
diff --git a/diced/src/error.rs b/diced/src/error.rs
deleted file mode 100644
index 3e230e4..0000000
--- a/diced/src/error.rs
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2021, 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.
-
-use android_security_dice::aidl::android::security::dice::ResponseCode::ResponseCode;
-use anyhow::Result;
-use binder::{ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode};
-use keystore2_selinux as selinux;
-use std::ffi::CString;
-
-/// This is the main Diced error type. It wraps the Diced `ResponseCode` generated
-/// from AIDL in the `Rc` variant and Binder and BinderTransaction errors in the respective
-/// variants.
-#[allow(dead_code)] // Binder error forwarding will be needed when proxy nodes are implemented.
-#[derive(Debug, thiserror::Error, Eq, PartialEq, Clone)]
-pub enum Error {
- /// Wraps a dice `ResponseCode` as defined by the android.security.dice AIDL interface
- /// specification.
- #[error("Error::Rc({0:?})")]
- Rc(ResponseCode),
- /// Wraps a Binder exception code other than a service specific exception.
- #[error("Binder exception code {0:?}, {1:?}")]
- Binder(ExceptionCode, i32),
- /// Wraps a Binder status code.
- #[error("Binder transaction error {0:?}")]
- BinderTransaction(StatusCode),
-}
-
-/// This function should be used by dice service calls to translate error conditions
-/// into service specific exceptions.
-///
-/// All error conditions get logged by this function.
-///
-/// All `Error::Rc(x)` variants get mapped onto a service specific error code of x.
-/// `selinux::Error::PermissionDenied` is mapped on `ResponseCode::PERMISSION_DENIED`.
-///
-/// All non `Error` error conditions and the Error::Binder variant get mapped onto
-/// ResponseCode::SYSTEM_ERROR`.
-///
-/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
-/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
-/// typically returns Ok(value).
-///
-/// # Examples
-///
-/// ```
-/// fn do_something() -> anyhow::Result<Vec<u8>> {
-/// Err(anyhow!(Error::Rc(ResponseCode::NOT_IMPLEMENTED)))
-/// }
-///
-/// map_or_log_err(do_something(), Ok)
-/// ```
-pub fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
-where
- F: FnOnce(U) -> BinderResult<T>,
-{
- map_err_with(
- result,
- |e| {
- log::error!("{:?}", e);
- e
- },
- handle_ok,
- )
-}
-
-/// This function behaves similar to map_or_log_error, but it does not log the errors, instead
-/// it calls map_err on the error before mapping it to a binder result allowing callers to
-/// log or transform the error before mapping it.
-fn map_err_with<T, U, F1, F2>(result: Result<U>, map_err: F1, handle_ok: F2) -> BinderResult<T>
-where
- F1: FnOnce(anyhow::Error) -> anyhow::Error,
- F2: FnOnce(U) -> BinderResult<T>,
-{
- result.map_or_else(
- |e| {
- let e = map_err(e);
- let msg = match CString::new(format!("{:?}", e)) {
- Ok(msg) => Some(msg),
- Err(_) => {
- log::warn!(
- "Cannot convert error message to CStr. It contained a nul byte.
- Omitting message from service specific error."
- );
- None
- }
- };
- let rc = get_error_code(&e);
- Err(BinderStatus::new_service_specific_error(rc, msg.as_deref()))
- },
- handle_ok,
- )
-}
-
-/// Extracts the error code from an `anyhow::Error` mapping any error that does not have a
-/// root cause of `Error::Rc` onto `ResponseCode::SYSTEM_ERROR` and to `e` with `Error::Rc(e)`
-/// otherwise.
-fn get_error_code(e: &anyhow::Error) -> i32 {
- let root_cause = e.root_cause();
- match root_cause.downcast_ref::<Error>() {
- Some(Error::Rc(rcode)) => rcode.0,
- // If an Error::Binder reaches this stage we report a system error.
- // The exception code and possible service specific error will be
- // printed in the error log above.
- Some(Error::Binder(_, _)) | Some(Error::BinderTransaction(_)) => {
- ResponseCode::SYSTEM_ERROR.0
- }
- None => match root_cause.downcast_ref::<selinux::Error>() {
- Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
- _ => ResponseCode::SYSTEM_ERROR.0,
- },
- }
-}
diff --git a/diced/src/hal_node.rs b/diced/src/hal_node.rs
index 69cf4ac..ca470e5 100644
--- a/diced/src/hal_node.rs
+++ b/diced/src/hal_node.rs
@@ -187,10 +187,8 @@
// `ResidentHal::new`
run_forked(move || {
let artifacts = artifacts.with_artifacts(|a| ResidentArtifacts::new_from(a))?;
- let input_values: Vec<utils::InputValues> =
- input_values.iter().map(|v| v.into()).collect();
let artifacts = artifacts
- .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
+ .execute_steps(input_values)
.context("In ResidentHal::get_effective_artifacts:")?;
f(artifacts)
})
@@ -205,14 +203,15 @@
.with_effective_artifacts(input_values, |artifacts| {
let (cdi_attest, _, _) = artifacts.into_tuple();
let mut dice = OpenDiceCborContext::new();
- let seed = dice
- .derive_cdi_private_key_seed(cdi_attest[..].try_into().with_context(|| {
+ let seed = dice::derive_cdi_private_key_seed(
+ cdi_attest[..].try_into().with_context(|| {
format!(
"In ResidentHal::sign: Failed to convert cdi_attest (length: {}).",
cdi_attest.len()
)
- })?)
- .context("In ResidentHal::sign: Failed to derive seed from cdi_attest.")?;
+ })?,
+ )
+ .context("In ResidentHal::sign: Failed to derive seed from cdi_attest.")?;
let (_public_key, private_key) = dice
.keypair_from_seed(seed[..].try_into().with_context(|| {
format!(
@@ -279,11 +278,8 @@
*artifacts = run_forked(|| {
let new_artifacts =
artifacts_clone.with_artifacts(|a| ResidentArtifacts::new_from(a))?;
- let input_values: Vec<utils::InputValues> =
- input_values.iter().map(|v| v.into()).collect();
-
let new_artifacts = new_artifacts
- .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
+ .execute_steps(input_values)
.context("In ResidentHal::get_effective_artifacts:")?;
artifacts_clone.update(&new_artifacts)
})?;
@@ -338,6 +334,7 @@
use diced_open_dice_cbor as dice;
use diced_sample_inputs;
use diced_utils as utils;
+ use std::ffi::CStr;
#[derive(Debug, Serialize, Deserialize, Clone)]
struct InsecureSerializableArtifacts {
@@ -346,6 +343,16 @@
bcc: Vec<u8>,
}
+ impl From<dice::OwnedDiceArtifacts> for InsecureSerializableArtifacts {
+ fn from(dice_artifacts: dice::OwnedDiceArtifacts) -> Self {
+ Self {
+ cdi_attest: dice_artifacts.cdi_values.cdi_attest,
+ cdi_seal: dice_artifacts.cdi_values.cdi_seal,
+ bcc: dice_artifacts.bcc[..].to_vec(),
+ }
+ }
+ }
+
impl DiceArtifacts for InsecureSerializableArtifacts {
fn cdi_attest(&self) -> &[u8; dice::CDI_SIZE] {
&self.cdi_attest
@@ -376,25 +383,18 @@
fn make_input_values(
code: &str,
- config_name: &str,
+ config_name: &CStr,
authority: &str,
) -> Result<BinderInputValues> {
- let mut dice_ctx = dice::OpenDiceCborContext::new();
Ok(BinderInputValues {
- codeHash: dice_ctx
- .hash(code.as_bytes())
- .context("In make_input_values: code hash failed.")?
- .as_slice()
- .try_into()?,
+ codeHash: dice::hash(code.as_bytes())
+ .context("In make_input_values: code hash failed.")?,
config: BinderConfig {
- desc: dice::bcc::format_config_descriptor(Some(config_name), None, true)
+ desc: dice::retry_bcc_format_config_descriptor(Some(config_name), None, true)
.context("In make_input_values: Failed to format config descriptor.")?,
},
- authorityHash: dice_ctx
- .hash(authority.as_bytes())
- .context("In make_input_values: authority hash failed.")?
- .as_slice()
- .try_into()?,
+ authorityHash: dice::hash(authority.as_bytes())
+ .context("In make_input_values: authority hash failed.")?,
authorityDescriptor: None,
mode: BinderMode::NORMAL,
hidden: [0; dice::HIDDEN_SIZE],
@@ -404,21 +404,27 @@
/// Test the resident artifact batched derivation in process.
#[test]
fn derive_with_resident_artifacts() -> Result<()> {
- let (cdi_attest, cdi_seal, bcc) = diced_sample_inputs::make_sample_bcc_and_cdis()?;
-
- let artifacts =
- ResidentArtifacts::new(cdi_attest[..].try_into()?, cdi_seal[..].try_into()?, &bcc)?;
+ let artifacts: ResidentArtifacts =
+ diced_sample_inputs::make_sample_bcc_and_cdis()?.try_into()?;
let input_values = &[
- make_input_values("component 1 code", "component 1", "component 1 authority")?,
- make_input_values("component 2 code", "component 2", "component 2 authority")?,
- make_input_values("component 3 code", "component 3", "component 3 authority")?,
+ make_input_values(
+ "component 1 code",
+ CStr::from_bytes_with_nul(b"component 1\0").unwrap(),
+ "component 1 authority",
+ )?,
+ make_input_values(
+ "component 2 code",
+ CStr::from_bytes_with_nul(b"component 2\0").unwrap(),
+ "component 2 authority",
+ )?,
+ make_input_values(
+ "component 3 code",
+ CStr::from_bytes_with_nul(b"component 3\0").unwrap(),
+ "component 3 authority",
+ )?,
];
-
- let input_values: Vec<utils::InputValues> = input_values.iter().map(|v| v.into()).collect();
-
- let new_artifacts =
- artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))?;
+ let new_artifacts = artifacts.execute_steps(input_values)?;
let result = utils::make_bcc_handover(
new_artifacts.cdi_attest(),
@@ -435,24 +441,31 @@
/// the same test vector as the in process test above.
#[test]
fn derive_with_insecure_artifacts() -> Result<()> {
- let (cdi_attest, cdi_seal, bcc) = diced_sample_inputs::make_sample_bcc_and_cdis()?;
+ let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()?;
// Safety: ResidentHal can only be used in single threaded environments.
// On-device Rust tests run each test in a separate process.
- let hal_impl = unsafe {
- ResidentHal::new(InsecureSerializableArtifacts {
- cdi_attest: cdi_attest[..].try_into()?,
- cdi_seal: cdi_seal[..].try_into()?,
- bcc,
- })
- }
- .expect("Failed to create ResidentHal.");
+ let hal_impl =
+ unsafe { ResidentHal::new(InsecureSerializableArtifacts::from(dice_artifacts)) }
+ .expect("Failed to create ResidentHal.");
let bcc_handover = hal_impl
.derive(&[
- make_input_values("component 1 code", "component 1", "component 1 authority")?,
- make_input_values("component 2 code", "component 2", "component 2 authority")?,
- make_input_values("component 3 code", "component 3", "component 3 authority")?,
+ make_input_values(
+ "component 1 code",
+ CStr::from_bytes_with_nul(b"component 1\0").unwrap(),
+ "component 1 authority",
+ )?,
+ make_input_values(
+ "component 2 code",
+ CStr::from_bytes_with_nul(b"component 2\0").unwrap(),
+ "component 2 authority",
+ )?,
+ make_input_values(
+ "component 3 code",
+ CStr::from_bytes_with_nul(b"component 3\0").unwrap(),
+ "component 3 authority",
+ )?,
])
.expect("Failed to derive artifacts.");
@@ -464,30 +477,33 @@
/// must yield the same outcome as three derivations with the same input values.
#[test]
fn demote() -> Result<()> {
- let (cdi_attest, cdi_seal, bcc) = diced_sample_inputs::make_sample_bcc_and_cdis()?;
+ let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()?;
// Safety: ResidentHal can only be used in single threaded environments.
// On-device Rust tests run each test in a separate process.
- let hal_impl = unsafe {
- ResidentHal::new(InsecureSerializableArtifacts {
- cdi_attest: cdi_attest[..].try_into()?,
- cdi_seal: cdi_seal[..].try_into()?,
- bcc,
- })
- }
- .expect("Failed to create ResidentHal.");
+ let hal_impl =
+ unsafe { ResidentHal::new(InsecureSerializableArtifacts::from(dice_artifacts)) }
+ .expect("Failed to create ResidentHal.");
hal_impl
.demote(&[
- make_input_values("component 1 code", "component 1", "component 1 authority")?,
- make_input_values("component 2 code", "component 2", "component 2 authority")?,
+ make_input_values(
+ "component 1 code",
+ CStr::from_bytes_with_nul(b"component 1\0").unwrap(),
+ "component 1 authority",
+ )?,
+ make_input_values(
+ "component 2 code",
+ CStr::from_bytes_with_nul(b"component 2\0").unwrap(),
+ "component 2 authority",
+ )?,
])
.expect("Failed to demote implementation.");
let bcc_handover = hal_impl
.derive(&[make_input_values(
"component 3 code",
- "component 3",
+ CStr::from_bytes_with_nul(b"component 3\0").unwrap(),
"component 3 authority",
)?])
.expect("Failed to derive artifacts.");
diff --git a/diced/src/lib.rs b/diced/src/lib.rs
deleted file mode 100644
index 50e0e96..0000000
--- a/diced/src/lib.rs
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright 2021, 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.
-
-//! Implement the android.security.dice.IDiceNode service.
-
-mod error;
-mod permission;
-mod proxy_node_hal;
-mod resident_node;
-
-pub use crate::proxy_node_hal::ProxyNodeHal;
-pub use crate::resident_node::ResidentNode;
-use android_hardware_security_dice::aidl::android::hardware::security::dice::{
- Bcc::Bcc, BccHandover::BccHandover, Config::Config as BinderConfig,
- InputValues::InputValues as BinderInputValues, Mode::Mode, Signature::Signature,
-};
-use android_security_dice::aidl::android::security::dice::{
- IDiceMaintenance::BnDiceMaintenance, IDiceMaintenance::IDiceMaintenance, IDiceNode::BnDiceNode,
- IDiceNode::IDiceNode, ResponseCode::ResponseCode,
-};
-use anyhow::{Context, Result};
-use binder::{BinderFeatures, Result as BinderResult, Strong, ThreadState};
-pub use diced_open_dice_cbor as dice;
-use error::{map_or_log_err, Error};
-use keystore2_selinux as selinux;
-use libc::uid_t;
-use permission::Permission;
-use std::sync::Arc;
-
-/// A DiceNode backend implementation.
-/// All functions except demote_self derive effective dice artifacts staring from
-/// this node and iterating through `{ [client | demotion path], input_values }`
-/// in ascending order.
-pub trait DiceNodeImpl {
- /// Signs the message using the effective dice artifacts and Ed25519Pure.
- fn sign(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- message: &[u8],
- ) -> Result<Signature>;
- /// Returns the effective attestation chain.
- fn get_attestation_chain(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- ) -> Result<Bcc>;
- /// Returns the effective dice artifacts.
- fn derive(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- ) -> Result<BccHandover>;
- /// Adds [ `client` | `input_values` ] to the demotion path of the given client.
- /// This changes the effective dice artifacts for all subsequent API calls of the
- /// given client.
- fn demote(&self, client: BinderInputValues, input_values: &[BinderInputValues]) -> Result<()>;
- /// This demotes the implementation itself. I.e. a resident node would replace its resident
- /// with the effective artifacts derived using `input_values`. A proxy node would
- /// simply call `demote` on its parent node. This is not reversible and changes
- /// the effective dice artifacts of all clients.
- fn demote_self(&self, input_values: &[BinderInputValues]) -> Result<()>;
-}
-
-/// Wraps a DiceNodeImpl and implements the actual IDiceNode AIDL API.
-pub struct DiceNode {
- node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
-}
-
-/// This function uses its namesake in the permission module and in
-/// combination with with_calling_sid from the binder crate to check
-/// if the caller has the given keystore permission.
-pub fn check_caller_permission<T: selinux::ClassPermission>(perm: T) -> Result<()> {
- ThreadState::with_calling_sid(|calling_sid| {
- let target_context =
- selinux::getcon().context("In check_caller_permission: getcon failed.")?;
-
- selinux::check_permission(
- calling_sid.ok_or(Error::Rc(ResponseCode::SYSTEM_ERROR)).context(
- "In check_keystore_permission: Cannot check permission without calling_sid.",
- )?,
- &target_context,
- perm,
- )
- })
-}
-
-fn client_input_values(uid: uid_t) -> Result<BinderInputValues> {
- Ok(BinderInputValues {
- codeHash: [0; dice::HASH_SIZE],
- config: BinderConfig {
- desc: dice::bcc::format_config_descriptor(Some(&format!("{}", uid)), None, false)
- .context("In client_input_values: failed to format config descriptor")?,
- },
- authorityHash: [0; dice::HASH_SIZE],
- authorityDescriptor: None,
- hidden: [0; dice::HIDDEN_SIZE],
- mode: Mode::NORMAL,
- })
-}
-
-impl DiceNode {
- /// Constructs an instance of DiceNode, wraps it with a BnDiceNode object and
- /// returns a strong pointer to the binder. The result can be used to register
- /// the service with service manager.
- pub fn new_as_binder(
- node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
- ) -> Result<Strong<dyn IDiceNode>> {
- let result = BnDiceNode::new_binder(
- DiceNode { node_impl },
- BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
- );
- Ok(result)
- }
-
- fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> Result<Signature> {
- check_caller_permission(Permission::UseSign).context("In DiceNode::sign:")?;
- let client =
- client_input_values(ThreadState::get_calling_uid()).context("In DiceNode::sign:")?;
- self.node_impl.sign(client, input_values, message)
- }
- fn get_attestation_chain(&self, input_values: &[BinderInputValues]) -> Result<Bcc> {
- check_caller_permission(Permission::GetAttestationChain)
- .context("In DiceNode::get_attestation_chain:")?;
- let client = client_input_values(ThreadState::get_calling_uid())
- .context("In DiceNode::get_attestation_chain:")?;
- self.node_impl.get_attestation_chain(client, input_values)
- }
- fn derive(&self, input_values: &[BinderInputValues]) -> Result<BccHandover> {
- check_caller_permission(Permission::Derive).context("In DiceNode::derive:")?;
- let client =
- client_input_values(ThreadState::get_calling_uid()).context("In DiceNode::extend:")?;
- self.node_impl.derive(client, input_values)
- }
- fn demote(&self, input_values: &[BinderInputValues]) -> Result<()> {
- check_caller_permission(Permission::Demote).context("In DiceNode::demote:")?;
- let client =
- client_input_values(ThreadState::get_calling_uid()).context("In DiceNode::demote:")?;
- self.node_impl.demote(client, input_values)
- }
-}
-
-impl binder::Interface for DiceNode {}
-
-impl IDiceNode for DiceNode {
- fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> BinderResult<Signature> {
- map_or_log_err(self.sign(input_values, message), Ok)
- }
- fn getAttestationChain(&self, input_values: &[BinderInputValues]) -> BinderResult<Bcc> {
- map_or_log_err(self.get_attestation_chain(input_values), Ok)
- }
- fn derive(&self, input_values: &[BinderInputValues]) -> BinderResult<BccHandover> {
- map_or_log_err(self.derive(input_values), Ok)
- }
- fn demote(&self, input_values: &[BinderInputValues]) -> BinderResult<()> {
- map_or_log_err(self.demote(input_values), Ok)
- }
-}
-
-/// Wraps a DiceNodeImpl and implements the IDiceMaintenance AIDL API.
-pub struct DiceMaintenance {
- node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
-}
-
-impl DiceMaintenance {
- /// Constructs an instance of DiceMaintenance, wraps it with a BnDiceMaintenance object and
- /// returns a strong pointer to the binder. The result can be used to register the service
- /// with service manager.
- pub fn new_as_binder(
- node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
- ) -> Result<Strong<dyn IDiceMaintenance>> {
- let result = BnDiceMaintenance::new_binder(
- DiceMaintenance { node_impl },
- BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
- );
- Ok(result)
- }
-
- fn demote_self(&self, input_values: &[BinderInputValues]) -> Result<()> {
- check_caller_permission(Permission::DemoteSelf)
- .context("In DiceMaintenance::demote_self:")?;
- self.node_impl.demote_self(input_values)
- }
-}
-
-impl binder::Interface for DiceMaintenance {}
-
-impl IDiceMaintenance for DiceMaintenance {
- fn demoteSelf(&self, input_values: &[BinderInputValues]) -> BinderResult<()> {
- map_or_log_err(self.demote_self(input_values), Ok)
- }
-}
diff --git a/diced/src/permission.rs b/diced/src/permission.rs
deleted file mode 100644
index 62ca653..0000000
--- a/diced/src/permission.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2021, 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 crate provides convenience wrappers for the SELinux permission
-//! defined in the diced SELinux access class.
-
-use keystore2_selinux as selinux;
-use selinux::{implement_class, ClassPermission};
-
-implement_class!(
- /// Permission provides a convenient abstraction from the SELinux class `diced`.
- #[selinux(class_name = diced)]
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- pub enum Permission {
- /// Checked when a client attempts to call seal or unseal.
- #[selinux(name = use_seal)]
- UseSeal,
- /// Checked when a client attempts to call IDiceNode::sign.
- #[selinux(name = use_sign)]
- UseSign,
- /// Checked when a client attempts to call IDiceNode::getAttestationChain.
- #[selinux(name = get_attestation_chain)]
- GetAttestationChain,
- /// Checked when a client attempts to call IDiceNode::derive.
- #[selinux(name = derive)]
- Derive,
- /// Checked when a client wants to demote itself by calling IDiceNode::demote.
- #[selinux(name = demote)]
- Demote,
- /// Checked when a client calls IDiceMaintenance::demote in an attempt to
- /// demote this dice node.
- #[selinux(name = demote_self)]
- DemoteSelf,
- }
-);
diff --git a/diced/src/proxy_node_hal.rs b/diced/src/proxy_node_hal.rs
deleted file mode 100644
index 8d883d2..0000000
--- a/diced/src/proxy_node_hal.rs
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2021, 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.
-
-//! A proxy dice node delegates all accesses to CDI_attest and CDI_seal to a parent
-//! node, here an implementation of android.hardware.security.dice.IDiceDevice.
-
-#![allow(dead_code)]
-
-use crate::DiceNodeImpl;
-use android_hardware_security_dice::aidl::android::hardware::security::dice::{
- Bcc::Bcc, BccHandover::BccHandover, IDiceDevice::IDiceDevice,
- InputValues::InputValues as BinderInputValues, Signature::Signature,
-};
-use anyhow::{Context, Result};
-use binder::Strong;
-use std::collections::HashMap;
-use std::sync::RwLock;
-
-/// The ProxyNodeHal implements a IDiceNode backend delegating crypto operations
-/// to the corresponding HAL.
-pub struct ProxyNodeHal {
- parent: Strong<dyn IDiceDevice>,
- demotion_db: RwLock<HashMap<BinderInputValues, Vec<BinderInputValues>>>,
-}
-
-impl ProxyNodeHal {
- /// Creates a new proxy node with a reference to the parent service.
- pub fn new(parent: Strong<dyn IDiceDevice>) -> Result<Self> {
- Ok(ProxyNodeHal { parent, demotion_db: Default::default() })
- }
-
- fn get_effective_input_values(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- ) -> Vec<BinderInputValues> {
- let demotion_db = self.demotion_db.read().unwrap();
-
- let client_arr = [client];
-
- demotion_db
- .get(&client_arr[0])
- .map(|v| v.iter())
- .unwrap_or_else(|| client_arr.iter())
- .chain(input_values.iter())
- .cloned()
- .collect()
- }
-}
-
-impl DiceNodeImpl for ProxyNodeHal {
- fn sign(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- message: &[u8],
- ) -> Result<Signature> {
- self.parent
- .sign(&self.get_effective_input_values(client, input_values), message)
- .context("In ProxyNodeHal::sign:")
- }
-
- fn get_attestation_chain(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- ) -> Result<Bcc> {
- self.parent
- .getAttestationChain(&self.get_effective_input_values(client, input_values))
- .context("In ProxyNodeHal::get_attestation_chain:")
- }
-
- fn derive(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- ) -> Result<BccHandover> {
- self.parent
- .derive(&self.get_effective_input_values(client, input_values))
- .context("In ProxyNodeHal::derive:")
- }
-
- fn demote(&self, client: BinderInputValues, input_values: &[BinderInputValues]) -> Result<()> {
- let mut demotion_db = self.demotion_db.write().unwrap();
-
- let client_arr = [client];
-
- // The following statement consults demotion database which yields an optional demotion
- // path. It then constructs an iterator over the following elements, then clones and
- // collects them into a new vector:
- // [ demotion path | client ], input_values
- let new_path: Vec<BinderInputValues> = demotion_db
- .get(&client_arr[0])
- .map(|v| v.iter())
- .unwrap_or_else(|| client_arr.iter())
- .chain(input_values)
- .cloned()
- .collect();
-
- let [client] = client_arr;
- demotion_db.insert(client, new_path);
- Ok(())
- }
-
- fn demote_self(&self, input_values: &[BinderInputValues]) -> Result<()> {
- self.parent.demote(input_values).context("In ProxyNodeHal::demote_self:")
- }
-}
diff --git a/diced/src/resident_node.rs b/diced/src/resident_node.rs
deleted file mode 100644
index 99a6dc9..0000000
--- a/diced/src/resident_node.rs
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright 2021, 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.
-
-//! A resident dice node keeps CDI_attest and CDI_seal memory resident and can serve
-//! its clients directly by performing all crypto operations including derivations and
-//! certificate generation itself.
-
-use crate::DiceNodeImpl;
-use android_hardware_security_dice::aidl::android::hardware::security::dice::{
- Bcc::Bcc, BccHandover::BccHandover, InputValues::InputValues as BinderInputValues,
- Signature::Signature,
-};
-use anyhow::{Context, Result};
-use dice::{ContextImpl, OpenDiceCborContext};
-use diced_open_dice_cbor as dice;
-use diced_utils::{self as utils, InputValues, ResidentArtifacts};
-use std::collections::HashMap;
-use std::convert::TryInto;
-use std::sync::RwLock;
-
-/// The ResidentNode implements a IDiceNode backend with memory resident DICE secrets.
-pub struct ResidentNode {
- artifacts: RwLock<ResidentArtifacts>,
- demotion_db: RwLock<HashMap<BinderInputValues, Vec<BinderInputValues>>>,
-}
-
-impl ResidentNode {
- /// Creates a new Resident node with the given dice secrets and certificate chain.
- pub fn new(
- cdi_attest: &[u8; dice::CDI_SIZE],
- cdi_seal: &[u8; dice::CDI_SIZE],
- bcc: Vec<u8>,
- ) -> Result<Self> {
- Ok(ResidentNode {
- artifacts: RwLock::new(
- ResidentArtifacts::new(cdi_attest, cdi_seal, &bcc)
- .context("In ResidentNode::new: Trying to initialize ResidentArtifacts")?,
- ),
- demotion_db: Default::default(),
- })
- }
-
- fn get_effective_artifacts(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- ) -> Result<ResidentArtifacts> {
- let artifacts = self.artifacts.read().unwrap().try_clone()?;
- let demotion_db = self.demotion_db.read().unwrap();
-
- let client_arr = [client];
-
- let input_values: Vec<utils::InputValues> = demotion_db
- .get(&client_arr[0])
- .map(|v| v.iter())
- .unwrap_or_else(|| client_arr.iter())
- .chain(input_values.iter())
- .map(|v| v.into())
- .collect();
-
- artifacts
- .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
- .context("In get_effective_artifacts:")
- }
-}
-
-impl DiceNodeImpl for ResidentNode {
- fn sign(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- message: &[u8],
- ) -> Result<Signature> {
- let (cdi_attest, _, _) = self
- .get_effective_artifacts(client, input_values)
- .context("In ResidentNode::sign: Failed to get effective_artifacts.")?
- .into_tuple();
- let mut dice = OpenDiceCborContext::new();
- let seed = dice
- .derive_cdi_private_key_seed(cdi_attest[..].try_into().with_context(|| {
- format!(
- "In ResidentNode::sign: Failed to convert cdi_attest (length: {}).",
- cdi_attest.len()
- )
- })?)
- .context("In ResidentNode::sign: Failed to derive seed from cdi_attest.")?;
- let (_public_key, private_key) = dice
- .keypair_from_seed(seed[..].try_into().with_context(|| {
- format!("In ResidentNode::sign: Failed to convert seed (length: {}).", seed.len())
- })?)
- .context("In ResidentNode::sign: Failed to derive keypair from seed.")?;
- Ok(Signature {
- data: dice
- .sign(
- message,
- private_key[..].try_into().with_context(|| {
- format!(
- "In ResidentNode::sign: Failed to convert private_key (length: {}).",
- private_key.len()
- )
- })?,
- )
- .context("In ResidentNode::sign: Failed to sign.")?,
- })
- }
-
- fn get_attestation_chain(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- ) -> Result<Bcc> {
- let (_, _, bcc) = self
- .get_effective_artifacts(client, input_values)
- .context("In ResidentNode::get_attestation_chain: Failed to get effective_artifacts.")?
- .into_tuple();
-
- Ok(Bcc { data: bcc })
- }
-
- fn derive(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- ) -> Result<BccHandover> {
- let (cdi_attest, cdi_seal, bcc) =
- self.get_effective_artifacts(client, input_values)?.into_tuple();
-
- utils::make_bcc_handover(
- &cdi_attest[..]
- .try_into()
- .context("In ResidentNode::derive: Trying to convert cdi_attest to sized array.")?,
- &cdi_seal[..]
- .try_into()
- .context("In ResidentNode::derive: Trying to convert cdi_attest to sized array.")?,
- &bcc,
- )
- .context("In ResidentNode::derive: Trying to format bcc handover.")
- }
-
- fn demote(&self, client: BinderInputValues, input_values: &[BinderInputValues]) -> Result<()> {
- let mut demotion_db = self.demotion_db.write().unwrap();
-
- let client_arr = [client];
-
- // The following statement consults demotion database which yields an optional demotion
- // path. It then constructs an iterator over the following elements, then clones and
- // collects them into a new vector:
- // [ demotion path | client ], input_values
- let new_path: Vec<BinderInputValues> = demotion_db
- .get(&client_arr[0])
- .map(|v| v.iter())
- .unwrap_or_else(|| client_arr.iter())
- .chain(input_values)
- .cloned()
- .collect();
-
- let [client] = client_arr;
- demotion_db.insert(client, new_path);
- Ok(())
- }
-
- fn demote_self(&self, input_values: &[BinderInputValues]) -> Result<()> {
- let mut artifacts = self.artifacts.write().unwrap();
-
- let input_values = input_values
- .iter()
- .map(|v| {
- v.try_into().with_context(|| format!("Failed to convert input values: {:#?}", v))
- })
- .collect::<Result<Vec<InputValues>>>()
- .context("In ResidentNode::demote_self:")?;
-
- *artifacts = artifacts
- .try_clone()
- .context("In ResidentNode::demote_self: Failed to clone resident artifacts")?
- .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
- .context("In ResidentNode::demote_self:")?;
- Ok(())
- }
-}
diff --git a/diced/src/sample_inputs.rs b/diced/src/sample_inputs.rs
index ff239ed..802a6d3 100644
--- a/diced/src/sample_inputs.rs
+++ b/diced/src/sample_inputs.rs
@@ -21,10 +21,9 @@
use anyhow::{Context, Result};
use dice::ContextImpl;
use diced_open_dice_cbor as dice;
-use diced_utils::cbor;
-use diced_utils::InputValues;
-use keystore2_crypto::ZVec;
-use std::convert::{TryFrom, TryInto};
+use diced_utils::{cbor, to_dice_input_values};
+use std::convert::TryInto;
+use std::ffi::CStr;
use std::io::Write;
/// Sample UDS used to perform the root dice flow by `make_sample_bcc_and_cdis`.
@@ -66,10 +65,9 @@
/// Derives a tuple of (CDI_ATTEST, CDI_SEAL, BCC) derived of the vector of input values returned
/// by `get_input_values_vector`.
-pub fn make_sample_bcc_and_cdis() -> Result<(ZVec, ZVec, Vec<u8>)> {
+pub fn make_sample_bcc_and_cdis() -> Result<dice::OwnedDiceArtifacts> {
let mut dice_ctx = dice::OpenDiceCborContext::new();
- let private_key_seed = dice_ctx
- .derive_cdi_private_key_seed(UDS)
+ let private_key_seed = dice::derive_cdi_private_key_seed(UDS)
.context("In make_sample_bcc_and_cdis: Trying to derive private key seed.")?;
let (public_key, _) =
@@ -81,14 +79,9 @@
let input_values_vector = get_input_values_vector();
- let (cdi_attest, cdi_seal, mut cert) = dice_ctx
- .main_flow(
- UDS,
- UDS,
- &InputValues::try_from(&input_values_vector[0])
- .context("In make_sample_bcc_and_cdis: Trying to convert input values. (0)")?,
- )
- .context("In make_sample_bcc_and_cdis: Trying to run first main flow.")?;
+ let (cdi_values, mut cert) =
+ dice::retry_dice_main_flow(UDS, UDS, &to_dice_input_values(&input_values_vector[0]))
+ .context("In make_sample_bcc_and_cdis: Trying to run first main flow.")?;
let mut bcc: Vec<u8> = vec![];
@@ -99,56 +92,44 @@
bcc.append(&mut cert);
- let (cdi_attest, cdi_seal, bcc) = dice_ctx
- .bcc_main_flow(
- &cdi_attest[..].try_into().context(
- "In make_sample_bcc_and_cdis: Failed to convert cdi_attest to array reference. (1)",
- )?,
- &cdi_seal[..].try_into().context(
- "In make_sample_bcc_and_cdis: Failed to convert cdi_seal to array reference. (1)",
- )?,
- &bcc,
- &InputValues::try_from(&input_values_vector[1])
- .context("In make_sample_bcc_and_cdis: Trying to convert input values. (1)")?,
- )
- .context("In make_sample_bcc_and_cdis: Trying to run first bcc main flow.")?;
- dice_ctx
- .bcc_main_flow(
- &cdi_attest[..].try_into().context(
- "In make_sample_bcc_and_cdis: Failed to convert cdi_attest to array reference. (2)",
- )?,
- &cdi_seal[..].try_into().context(
- "In make_sample_bcc_and_cdis: Failed to convert cdi_seal to array reference. (2)",
- )?,
- &bcc,
- &InputValues::try_from(&input_values_vector[2])
- .context("In make_sample_bcc_and_cdis: Trying to convert input values. (2)")?,
- )
- .context("In make_sample_bcc_and_cdis: Trying to run second bcc main flow.")
+ let dice_artifacts = dice::retry_bcc_main_flow(
+ &cdi_values.cdi_attest,
+ &cdi_values.cdi_seal,
+ &bcc,
+ &to_dice_input_values(&input_values_vector[1]),
+ )
+ .context("In make_sample_bcc_and_cdis: Trying to run first bcc main flow.")?;
+ dice::retry_bcc_main_flow(
+ &dice_artifacts.cdi_values.cdi_attest,
+ &dice_artifacts.cdi_values.cdi_seal,
+ &dice_artifacts.bcc,
+ &to_dice_input_values(&input_values_vector[2]),
+ )
+ .context("In make_sample_bcc_and_cdis: Trying to run second bcc main flow.")
}
fn make_input_values(
- code_hash: &[u8; dice::HASH_SIZE],
- authority_hash: &[u8; dice::HASH_SIZE],
- config_name: &str,
+ code_hash: dice::Hash,
+ authority_hash: dice::Hash,
+ config_name: &CStr,
config_version: u64,
config_resettable: bool,
mode: Mode,
- hidden: &[u8; dice::HIDDEN_SIZE],
+ hidden: dice::Hidden,
) -> Result<BinderInputValues> {
Ok(BinderInputValues {
- codeHash: *code_hash,
+ codeHash: code_hash,
config: BinderConfig {
- desc: dice::bcc::format_config_descriptor(
+ desc: dice::retry_bcc_format_config_descriptor(
Some(config_name),
Some(config_version),
config_resettable,
)
.context("In make_input_values: Failed to format config descriptor.")?,
},
- authorityHash: *authority_hash,
+ authorityHash: authority_hash,
authorityDescriptor: None,
- hidden: *hidden,
+ hidden,
mode,
})
}
@@ -156,90 +137,84 @@
/// Returns a set of sample input for a dice chain comprising the android boot loader ABL,
/// the verified boot information AVB, and Android S.
pub fn get_input_values_vector() -> Vec<BinderInputValues> {
+ const CODE_HASH1: [u8; dice::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_HASH1: [u8; dice::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 HIDDEN1: [u8; dice::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_HASH2: [u8; dice::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_HASH2: [u8; dice::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 HIDDEN2: [u8; dice::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_HASH3: [u8; dice::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,
+ ];
vec![
make_input_values(
- &[
- // code hash
- 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,
- ],
- &[
- // authority hash
- 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,
- ],
- "ABL", // config name
- 1, // config version
- true, // resettable
+ CODE_HASH1,
+ AUTHORITY_HASH1,
+ CStr::from_bytes_with_nul(b"ABL\0").unwrap(), // config name
+ 1, // config version
+ true, // resettable
Mode::NORMAL,
- &[
- // hidden
- 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,
- ],
+ HIDDEN1,
)
.unwrap(),
make_input_values(
- &[
- // code hash
- 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,
- ],
- &[
- // authority hash
- 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,
- ],
- "AVB", // config name
- 1, // config version
- true, // resettable
+ CODE_HASH2,
+ AUTHORITY_HASH2,
+ CStr::from_bytes_with_nul(b"AVB\0").unwrap(), // config name
+ 1, // config version
+ true, // resettable
Mode::NORMAL,
- &[
- // hidden
- 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,
- ],
+ HIDDEN2,
)
.unwrap(),
make_input_values(
- &[
- // code hash
- 0; dice::HASH_SIZE
- ],
- &[
- // authority hash
- 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,
- ],
- "Android", // config name
- 12, // config version
- true, // resettable
+ [0; dice::HASH_SIZE], // code hash
+ AUTHORITY_HASH3,
+ CStr::from_bytes_with_nul(b"Android\0").unwrap(), // config name
+ 12, // config version
+ true, // resettable
Mode::NORMAL,
- &[
- // hidden
- 0; dice::HIDDEN_SIZE
- ],
+ [0; dice::HIDDEN_SIZE], // hidden,
)
.unwrap(),
]
diff --git a/diced/src/utils.rs b/diced/src/utils.rs
index 37f78ac..c249366 100644
--- a/diced/src/utils.rs
+++ b/diced/src/utils.rs
@@ -19,63 +19,31 @@
Mode::Mode as BinderMode,
};
use anyhow::{Context, Result};
-use dice::ContextImpl;
use diced_open_dice_cbor as dice;
use keystore2_crypto::ZVec;
use std::convert::TryInto;
-/// This new type wraps a reference to BinderInputValues and implements the open dice
-/// InputValues trait.
-#[derive(Debug)]
-pub struct InputValues<'a>(&'a BinderInputValues);
-
-impl<'a> From<&'a BinderInputValues> for InputValues<'a> {
- fn from(input_values: &'a BinderInputValues) -> InputValues<'a> {
- Self(input_values)
+/// Converts the `InputValues` from the binder to the `InputValues` type in `diced_open_dice` crate.
+pub fn to_dice_input_values(input: &BinderInputValues) -> dice::InputValues {
+ if input.authorityDescriptor.is_some() {
+ unimplemented!("Authority descriptor is not yet implemented in the current library.");
}
+ dice::InputValues::new(
+ input.codeHash,
+ dice::Config::Descriptor(input.config.desc.as_slice()),
+ input.authorityHash,
+ to_dice_mode(input.mode),
+ input.hidden,
+ )
}
-impl From<&InputValues<'_>> for BinderInputValues {
- fn from(input_values: &InputValues) -> BinderInputValues {
- input_values.0.clone()
- }
-}
-impl From<InputValues<'_>> for BinderInputValues {
- fn from(input_values: InputValues) -> BinderInputValues {
- input_values.0.clone()
- }
-}
-
-impl dice::InputValues for InputValues<'_> {
- fn code_hash(&self) -> &[u8; dice::HASH_SIZE] {
- &self.0.codeHash
- }
-
- fn config(&self) -> dice::Config {
- dice::Config::Descriptor(self.0.config.desc.as_slice())
- }
-
- fn authority_hash(&self) -> &[u8; dice::HASH_SIZE] {
- &self.0.authorityHash
- }
-
- fn authority_descriptor(&self) -> Option<&[u8]> {
- self.0.authorityDescriptor.as_deref()
- }
-
- fn mode(&self) -> dice::Mode {
- match self.0.mode {
- BinderMode::NOT_INITIALIZED => dice::Mode::NotConfigured,
- BinderMode::NORMAL => dice::Mode::Normal,
- BinderMode::DEBUG => dice::Mode::Debug,
- BinderMode::RECOVERY => dice::Mode::Recovery,
- _ => dice::Mode::NotConfigured,
- }
- }
-
- fn hidden(&self) -> &[u8; dice::HIDDEN_SIZE] {
- // If `self` was created using try_from the length was checked and this cannot panic.
- &self.0.hidden
+fn to_dice_mode(binder_mode: BinderMode) -> dice::DiceMode {
+ match binder_mode {
+ BinderMode::NOT_INITIALIZED => dice::DiceMode::kDiceModeNotInitialized,
+ BinderMode::NORMAL => dice::DiceMode::kDiceModeNormal,
+ BinderMode::DEBUG => dice::DiceMode::kDiceModeDebug,
+ BinderMode::RECOVERY => dice::DiceMode::kDiceModeMaintenance,
+ _ => dice::DiceMode::kDiceModeNotInitialized,
}
}
@@ -99,6 +67,18 @@
bcc: Vec<u8>,
}
+impl TryFrom<dice::OwnedDiceArtifacts> for ResidentArtifacts {
+ type Error = anyhow::Error;
+
+ fn try_from(dice_artifacts: dice::OwnedDiceArtifacts) -> Result<Self, Self::Error> {
+ Ok(ResidentArtifacts {
+ cdi_attest: dice_artifacts.cdi_values.cdi_attest[..].try_into()?,
+ cdi_seal: dice_artifacts.cdi_values.cdi_seal[..].try_into()?,
+ bcc: dice_artifacts.bcc[..].to_vec(),
+ })
+ }
+}
+
impl ResidentArtifacts {
/// Create a ResidentArtifacts object. The parameters ensure that the stored secrets
/// can only have the appropriate size, so that subsequent casts to array references
@@ -153,33 +133,32 @@
(cdi_attest, cdi_seal, bcc)
}
- fn execute_step(self, input_values: &dyn dice::InputValues) -> Result<Self> {
+ fn execute_step(self, input_values: &BinderInputValues) -> Result<Self> {
let ResidentArtifacts { cdi_attest, cdi_seal, bcc } = self;
- let (cdi_attest, cdi_seal, bcc) = dice::OpenDiceCborContext::new()
- .bcc_main_flow(
- cdi_attest[..].try_into().with_context(|| {
- format!("Trying to convert cdi_attest. (length: {})", cdi_attest.len())
- })?,
- cdi_seal[..].try_into().with_context(|| {
- format!("Trying to convert cdi_seal. (length: {})", cdi_seal.len())
- })?,
- &bcc,
- input_values,
- )
- .context("In ResidentArtifacts::execute_step:")?;
- Ok(ResidentArtifacts { cdi_attest, cdi_seal, bcc })
+ dice::retry_bcc_main_flow(
+ cdi_attest[..].try_into().with_context(|| {
+ format!("Trying to convert cdi_attest. (length: {})", cdi_attest.len())
+ })?,
+ cdi_seal[..].try_into().with_context(|| {
+ format!("Trying to convert cdi_seal. (length: {})", cdi_seal.len())
+ })?,
+ &bcc,
+ &to_dice_input_values(input_values),
+ )
+ .context("In ResidentArtifacts::execute_step:")?
+ .try_into()
}
/// Iterate through the iterator of dice input values performing one
/// BCC main flow step on each element.
- pub fn execute_steps<'a, Iter>(self, input_values: Iter) -> Result<Self>
+ pub fn execute_steps<'a, I>(self, input_values: I) -> Result<Self>
where
- Iter: IntoIterator<Item = &'a dyn dice::InputValues>,
+ I: IntoIterator<Item = &'a BinderInputValues>,
{
input_values
.into_iter()
- .try_fold(self, |acc, input_values| acc.execute_step(input_values))
+ .try_fold(self, |acc, input| acc.execute_step(input))
.context("In ResidentArtifacts::execute_step:")
}
}
diff --git a/identity/CredentialData.cpp b/identity/CredentialData.cpp
index fb08333..1bf1527 100644
--- a/identity/CredentialData.cpp
+++ b/identity/CredentialData.cpp
@@ -581,13 +581,17 @@
vector<vector<uint8_t>> keysNeedingCert;
- int64_t nowMilliSeconds =
- std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) * 1000;
+ time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+ int64_t nowMilliseconds;
+ if (__builtin_mul_overflow(int64_t(now), int64_t(1000), &nowMilliseconds)) {
+ LOG(ERROR) << "Overflow converting " << now << " to milliseconds";
+ return {};
+ }
for (AuthKeyData& data : authKeyDatas_) {
bool keyExceedUseCount = (data.useCount >= maxUsesPerKey_);
int64_t expirationDateAdjusted = data.expirationDateMillisSinceEpoch - minValidTimeMillis_;
- bool keyBeyondAdjustedExpirationDate = (nowMilliSeconds > expirationDateAdjusted);
+ bool keyBeyondAdjustedExpirationDate = (nowMilliseconds > expirationDateAdjusted);
bool newKeyNeeded =
(data.certificate.size() == 0) || keyExceedUseCount || keyBeyondAdjustedExpirationDate;
bool certificationPending = (data.pendingCertificate.size() > 0);
diff --git a/identity/RemotelyProvisionedKey.cpp b/identity/RemotelyProvisionedKey.cpp
index 7e90d63..784a680 100644
--- a/identity/RemotelyProvisionedKey.cpp
+++ b/identity/RemotelyProvisionedKey.cpp
@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include <android/security/rkp/BnGetKeyCallback.h>
#include <android/security/rkp/BnGetRegistrationCallback.h>
+#include <android/security/rkp/IGetKeyCallback.h>
#include <android/security/rkp/IRemoteProvisioning.h>
#include <binder/IServiceManager.h>
#include <binder/Status.h>
@@ -38,6 +39,7 @@
using ::android::hardware::security::keymint::RpcHardwareInfo;
using ::android::security::rkp::BnGetKeyCallback;
using ::android::security::rkp::BnGetRegistrationCallback;
+using ::android::security::rkp::IGetKeyCallback;
using ::android::security::rkp::IRegistration;
using ::android::security::rkp::IRemoteProvisioning;
using ::android::security::rkp::RemotelyProvisionedKey;
@@ -96,11 +98,11 @@
keyPromise_.set_value(std::nullopt);
return Status::ok();
}
- Status onError(const String16& error) override {
+ Status onError(IGetKeyCallback::ErrorCode error, const String16& description) override {
if (called_.test_and_set()) {
return Status::ok();
}
- LOG(ERROR) << "GetKeyCallback failed: " << error;
+ LOG(ERROR) << "GetKeyCallback failed: " << static_cast<int>(error) << ", " << description;
keyPromise_.set_value(std::nullopt);
return Status::ok();
}
@@ -124,7 +126,8 @@
auto cb = sp<GetKeyCallback>::make(std::move(keyPromise_));
auto status = registration->getKey(keyId_, cb);
if (!status.isOk()) {
- cb->onError(String16("Failed to register GetKeyCallback"));
+ cb->onError(IGetKeyCallback::ErrorCode::ERROR_UNKNOWN,
+ String16("Failed to register GetKeyCallback"));
}
return Status::ok();
}
diff --git a/keystore-engine/keystore2_engine.cpp b/keystore-engine/keystore2_engine.cpp
index 69caf51..dc90be3 100644
--- a/keystore-engine/keystore2_engine.cpp
+++ b/keystore-engine/keystore2_engine.cpp
@@ -146,11 +146,13 @@
return nullptr;
}
- rsa->n = BN_dup(public_rsa->n);
- rsa->e = BN_dup(public_rsa->e);
- if (rsa->n == nullptr || rsa->e == nullptr) {
+ bssl::UniquePtr<BIGNUM> n(BN_dup(RSA_get0_n(public_rsa)));
+ bssl::UniquePtr<BIGNUM> e(BN_dup(RSA_get0_e(public_rsa)));
+ if (n == nullptr || e == nullptr || !RSA_set0_key(rsa.get(), n.get(), e.get(), nullptr)) {
return nullptr;
}
+ OWNERSHIP_TRANSFERRED(n);
+ OWNERSHIP_TRANSFERRED(e);
bssl::UniquePtr<EVP_PKEY> result(EVP_PKEY_new());
if (result.get() == nullptr || !EVP_PKEY_assign_RSA(result.get(), rsa.get())) {
@@ -420,19 +422,19 @@
Keystore2KeyBackend{response.metadata.key, response.iSecurityLevel});
bssl::UniquePtr<EVP_PKEY> result;
- switch (EVP_PKEY_type(pkey->type)) {
+ switch (EVP_PKEY_id(pkey.get())) {
case EVP_PKEY_RSA: {
- bssl::UniquePtr<RSA> public_rsa(EVP_PKEY_get1_RSA(pkey.get()));
- result = wrap_rsa(key_backend, public_rsa.get());
+ RSA* public_rsa = EVP_PKEY_get0_RSA(pkey.get());
+ result = wrap_rsa(key_backend, public_rsa);
break;
}
case EVP_PKEY_EC: {
- bssl::UniquePtr<EC_KEY> public_ecdsa(EVP_PKEY_get1_EC_KEY(pkey.get()));
- result = wrap_ecdsa(key_backend, public_ecdsa.get());
+ EC_KEY* public_ecdsa = EVP_PKEY_get0_EC_KEY(pkey.get());
+ result = wrap_ecdsa(key_backend, public_ecdsa);
break;
}
default:
- LOG(ERROR) << AT << "Unsupported key type " << EVP_PKEY_type(pkey->type);
+ LOG(ERROR) << AT << "Unsupported key type " << EVP_PKEY_id(pkey.get());
return nullptr;
}
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index d1d58a4..3ca3942 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -71,11 +71,6 @@
pub fn perm() -> Self {
Error::Rc(ResponseCode::PERMISSION_DENIED)
}
-
- /// Short hand for `Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)`
- pub fn out_of_keys() -> Self {
- Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
- }
}
/// Helper function to map the binder status we get from calls into KeyMint
diff --git a/keystore2/src/rkpd_client.rs b/keystore2/src/rkpd_client.rs
index ecde4e8..f1e8e11 100644
--- a/keystore2/src/rkpd_client.rs
+++ b/keystore2/src/rkpd_client.rs
@@ -13,16 +13,15 @@
// limitations under the License.
//! Helper wrapper around RKPD interface.
-// TODO(b/264891956): Return RKP specific errors.
-use crate::error::{map_binder_status_code, Error};
+use crate::error::{map_binder_status_code, Error, ResponseCode};
use crate::globals::get_remotely_provisioned_component_name;
use crate::ks_err;
use crate::utils::watchdog as wd;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
use android_security_rkp_aidl::aidl::android::security::rkp::{
- IGetKeyCallback::BnGetKeyCallback, IGetKeyCallback::IGetKeyCallback,
- IGetRegistrationCallback::BnGetRegistrationCallback,
+ IGetKeyCallback::BnGetKeyCallback, IGetKeyCallback::ErrorCode::ErrorCode as GetKeyErrorCode,
+ IGetKeyCallback::IGetKeyCallback, IGetRegistrationCallback::BnGetRegistrationCallback,
IGetRegistrationCallback::IGetRegistrationCallback, IRegistration::IRegistration,
IRemoteProvisioning::IRemoteProvisioning,
IStoreUpgradedKeyCallback::BnStoreUpgradedKeyCallback,
@@ -30,7 +29,6 @@
RemotelyProvisionedKey::RemotelyProvisionedKey,
};
use android_security_rkp_aidl::binder::{BinderFeatures, Interface, Strong};
-use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
use anyhow::{Context, Result};
use std::sync::Mutex;
use std::time::Duration;
@@ -91,17 +89,17 @@
let _wp = wd::watch_millis("IGetRegistrationCallback::onCancel", 500);
log::warn!("IGetRegistrationCallback cancelled");
self.registration_tx.send(
- Err(Error::Rc(ResponseCode::OUT_OF_KEYS))
+ Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
.context(ks_err!("GetRegistrationCallback cancelled.")),
);
Ok(())
}
- fn onError(&self, error: &str) -> binder::Result<()> {
+ fn onError(&self, description: &str) -> binder::Result<()> {
let _wp = wd::watch_millis("IGetRegistrationCallback::onError", 500);
- log::error!("IGetRegistrationCallback failed: '{error}'");
+ log::error!("IGetRegistrationCallback failed: '{description}'");
self.registration_tx.send(
- Err(Error::Rc(ResponseCode::OUT_OF_KEYS))
- .context(ks_err!("GetRegistrationCallback failed: {:?}", error)),
+ Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
+ .context(ks_err!("GetRegistrationCallback failed: {:?}", description)),
);
Ok(())
}
@@ -161,17 +159,33 @@
let _wp = wd::watch_millis("IGetKeyCallback::onCancel", 500);
log::warn!("IGetKeyCallback cancelled");
self.key_tx.send(
- Err(Error::Rc(ResponseCode::OUT_OF_KEYS)).context(ks_err!("GetKeyCallback cancelled.")),
+ Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
+ .context(ks_err!("GetKeyCallback cancelled.")),
);
Ok(())
}
- fn onError(&self, error: &str) -> binder::Result<()> {
+ fn onError(&self, error: GetKeyErrorCode, description: &str) -> binder::Result<()> {
let _wp = wd::watch_millis("IGetKeyCallback::onError", 500);
- log::error!("IGetKeyCallback failed: {error}");
- self.key_tx.send(
- Err(Error::Rc(ResponseCode::OUT_OF_KEYS))
- .context(ks_err!("GetKeyCallback failed: {:?}", error)),
- );
+ log::error!("IGetKeyCallback failed: {description}");
+ let rc = match error {
+ GetKeyErrorCode::ERROR_UNKNOWN => ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
+ GetKeyErrorCode::ERROR_PERMANENT => ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
+ GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY => {
+ ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY
+ }
+ GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH => {
+ ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE
+ }
+ _ => {
+ log::error!("Unexpected error from rkpd: {error:?}");
+ ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR
+ }
+ };
+ self.key_tx.send(Err(Error::Rc(rc)).context(ks_err!(
+ "GetKeyCallback failed: {:?} {:?}",
+ error,
+ description
+ )));
Ok(())
}
}
@@ -188,7 +202,7 @@
.context(ks_err!("Trying to get key."))?;
match timeout(RKPD_TIMEOUT, rx).await {
- Err(e) => Err(Error::Rc(ResponseCode::OUT_OF_KEYS))
+ Err(e) => Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
.context(ks_err!("Waiting for RKPD key timed out: {:?}", e)),
Ok(v) => v.unwrap(),
}
@@ -298,6 +312,7 @@
};
use android_security_rkp_aidl::aidl::android::security::rkp::IRegistration::BnRegistration;
use keystore2_crypto::parse_subject_from_certificate;
+ use std::collections::HashMap;
use std::sync::atomic::{AtomicU32, Ordering};
#[derive(Default)]
@@ -403,7 +418,7 @@
let result = tokio_rt().block_on(rx).unwrap();
assert_eq!(
result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Rc(ResponseCode::OUT_OF_KEYS)
+ Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
);
}
@@ -416,7 +431,7 @@
let result = tokio_rt().block_on(rx).unwrap();
assert_eq!(
result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Rc(ResponseCode::OUT_OF_KEYS)
+ Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
);
}
@@ -441,21 +456,38 @@
let result = tokio_rt().block_on(rx).unwrap();
assert_eq!(
result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Rc(ResponseCode::OUT_OF_KEYS)
+ Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
);
}
#[test]
fn test_get_key_cb_error() {
- let (tx, rx) = oneshot::channel();
- let cb = GetKeyCallback::new_native_binder(tx);
- assert!(cb.onError("error").is_ok());
+ let error_mapping = HashMap::from([
+ (GetKeyErrorCode::ERROR_UNKNOWN, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+ (GetKeyErrorCode::ERROR_PERMANENT, ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR),
+ (
+ GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY,
+ ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
+ ),
+ (
+ GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH,
+ ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
+ ),
+ ]);
- let result = tokio_rt().block_on(rx).unwrap();
- assert_eq!(
- result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Rc(ResponseCode::OUT_OF_KEYS)
- );
+ // Loop over the generated list of enum values to better ensure this test stays in
+ // sync with the AIDL.
+ for get_key_error in GetKeyErrorCode::enum_values() {
+ let (tx, rx) = oneshot::channel();
+ let cb = GetKeyCallback::new_native_binder(tx);
+ assert!(cb.onError(get_key_error, "error").is_ok());
+
+ let result = tokio_rt().block_on(rx).unwrap();
+ assert_eq!(
+ result.unwrap_err().downcast::<Error>().unwrap(),
+ Error::Rc(error_mapping[&get_key_error]),
+ );
+ }
}
#[test]
@@ -503,7 +535,7 @@
tokio_rt().block_on(get_rkpd_attestation_key_from_registration_async(®istration, 0));
assert_eq!(
result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Rc(ResponseCode::OUT_OF_KEYS)
+ Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
);
}
@@ -590,6 +622,9 @@
let new_key =
get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id).unwrap();
+
+ // Restore original key so that we don't leave RKPD with invalid blobs.
+ assert!(store_rkpd_attestation_key(&sec_level, &new_blob, &key.keyBlob).is_ok());
assert_eq!(new_key.keyBlob, new_blob);
}
@@ -651,4 +686,28 @@
println!("RKPD key was NOT upgraded.");
}
}
+
+ #[test]
+ fn test_stress_get_rkpd_attestation_key() {
+ binder::ProcessState::start_thread_pool();
+ let key_id = get_next_key_id();
+ let mut threads = vec![];
+ const NTHREADS: u32 = 10;
+ const NCALLS: u32 = 1000;
+
+ for _ in 0..NTHREADS {
+ threads.push(std::thread::spawn(move || {
+ for _ in 0..NCALLS {
+ let key = get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id)
+ .unwrap();
+ assert!(!key.keyBlob.is_empty());
+ assert!(!key.encodedCertChain.is_empty());
+ }
+ }));
+ }
+
+ for t in threads {
+ assert!(t.join().is_ok());
+ }
+ }
}
diff --git a/keystore2/tests/keystore2_client_delete_key_tests.rs b/keystore2/tests/keystore2_client_delete_key_tests.rs
new file mode 100644
index 0000000..2a06edb
--- /dev/null
+++ b/keystore2/tests/keystore2_client_delete_key_tests.rs
@@ -0,0 +1,150 @@
+// Copyright 2022, 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.
+
+use nix::unistd::getuid;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ ErrorCode::ErrorCode, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error};
+
+/// Generate a key and delete it using keystore2 service `deleteKey` API. Test should successfully
+/// delete the generated key.
+#[test]
+fn keystore2_delete_key_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "delete_key_success_key";
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ None,
+ )
+ .unwrap();
+
+ keystore2.deleteKey(&key_metadata.key).expect("Failed to delete a key.");
+
+ // Check wehther deleted key is removed from keystore.
+ let result = key_generations::map_ks_error(keystore2.getKeyEntry(&key_metadata.key));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Try to delete non-existing key with domain other than BLOB using keystore2 service `deleteKey`
+/// API. Test should fail with an error code `KEY_NOT_FOUND`.
+#[test]
+fn keystore2_delete_key_fail() {
+ let test_alias = "delete_key_failure_key";
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(keystore2.deleteKey(&KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+ alias: Some(test_alias.to_string()),
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Generate a key with `Domain::BLOB`. Try to delete a key with `Domain::BLOB` using keystore2
+/// service `deleteKey` API. Test should fail to delete a key with domain BLOB with an error code
+/// `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_delete_key_with_blob_domain_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "delete_key_blob_fail_key";
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::BLOB,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias.to_string()),
+ None,
+ )
+ .unwrap();
+
+ let result = key_generations::map_ks_error(keystore2.deleteKey(&key_metadata.key));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
+}
+
+/// Generate a key with `Domain::BLOB`. Delete generated key with `Domain::BLOB` using underlying
+/// security level `deleteKey` API. Test should delete the key successfully.
+#[test]
+fn keystore2_delete_key_blob_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "delete_key_blob_success_key";
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::BLOB,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias.to_string()),
+ None,
+ )
+ .unwrap();
+
+ let result = sec_level.deleteKey(&key_metadata.key);
+ assert!(result.is_ok());
+}
+
+/// Try to delete a key with `Domain::BLOB` without providing key-blob. Test should fail to delete a
+/// key with error code `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_delete_key_fails_with_missing_key_blob() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let result = key_generations::map_ks_error(sec_level.deleteKey(&KeyDescriptor {
+ domain: Domain::BLOB,
+ nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+ alias: None,
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INVALID_ARGUMENT), result.unwrap_err());
+}
+
+/// Try to delete a key with domain other than `Domain::BLOB` using underlying security-level
+/// `deleteKey` API. Test should fail to delete a key-blob from underlying security-level backend
+/// with error code `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_delete_key_blob_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_delete_keyblob_test_key_{}", getuid());
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias),
+ None,
+ )
+ .unwrap();
+
+ let result = key_generations::map_ks_error(sec_level.deleteKey(&key_metadata.key));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INVALID_ARGUMENT), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_grant_key_tests.rs b/keystore2/tests/keystore2_client_grant_key_tests.rs
index 7c75734..bde872d 100644
--- a/keystore2/tests/keystore2_client_grant_key_tests.rs
+++ b/keystore2/tests/keystore2_client_grant_key_tests.rs
@@ -19,7 +19,8 @@
Digest::Digest, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
};
use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+ Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
+ IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
ResponseCode::ResponseCode,
};
@@ -27,7 +28,9 @@
authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
};
-use crate::keystore2_client_test_utils::perform_sample_sign_operation;
+use crate::keystore2_client_test_utils::{
+ generate_ec_key_and_grant_to_users, perform_sample_sign_operation,
+};
/// Generate an EC signing key and grant it to the user with given access vector.
fn generate_ec_key_and_grant_to_user(
@@ -50,6 +53,36 @@
keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
}
+fn load_grant_key_and_perform_sign_operation(
+ keystore2: &binder::Strong<dyn IKeystoreService>,
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ grant_key_nspace: i64,
+) -> Result<(), binder::Status> {
+ let key_entry_response = keystore2.getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ })?;
+
+ // Perform sample crypto operation using granted key.
+ let op_response = sec_level.createOperation(
+ &key_entry_response.metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+ false,
+ )?;
+
+ assert!(op_response.iOperation.is_some());
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(perform_sample_sign_operation(
+ &op_response.iOperation.unwrap()
+ ))
+ );
+
+ Ok(())
+}
+
/// Try to grant a key with permission that does not map to any of the `KeyPermission` values.
/// An error is expected with values that does not map to set of permissions listed in
/// `KeyPermission`.
@@ -203,3 +236,520 @@
)
};
}
+
+/// Grant a key to the user with DELETE access. In grantee context load the key and delete it.
+/// Verify that grantee should succeed in deleting the granted key and in grantor context test
+/// should fail to find the key with error response `KEY_NOT_FOUND`.
+#[test]
+fn keystore2_grant_delete_key_success() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+ static ALIAS: &str = "ks_grant_key_delete_success";
+
+ // Generate a key and grant it to a user with DELETE permission.
+ let grant_key_nspace = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let access_vector = KeyPermission::DELETE.0;
+ let mut grant_keys = generate_ec_key_and_grant_to_users(
+ &keystore2,
+ &sec_level,
+ Some(ALIAS.to_string()),
+ vec![GRANTEE_UID.try_into().unwrap()],
+ access_vector,
+ )
+ .unwrap();
+
+ grant_keys.remove(0)
+ })
+ };
+
+ // Grantee context, delete the key.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+ keystore2
+ .deleteKey(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ })
+ .unwrap();
+ },
+ )
+ };
+
+ // Verify whether key got deleted in grantor's context.
+ unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), move || {
+ let keystore2_inst = get_keystore_service();
+ let result =
+ key_generations::map_ks_error(keystore2_inst.getKeyEntry(&KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS.to_string()),
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+ })
+ };
+}
+
+/// Grant a key to the user. In grantee context load the granted key and try to grant it to second
+/// user. Test should fail with a response code `PERMISSION_DENIED` to grant a key to second user
+/// from grantee context. Test should make sure second grantee should not have a access to granted
+/// key.
+#[test]
+fn keystore2_grant_key_fails_with_permission_denied() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+
+ const SEC_USER_ID: u32 = 98;
+ const SEC_APPLICATION_ID: u32 = 10001;
+ static SEC_GRANTEE_UID: u32 = SEC_USER_ID * AID_USER_OFFSET + SEC_APPLICATION_ID;
+ static SEC_GRANTEE_GID: u32 = SEC_GRANTEE_UID;
+
+ // Generate a key and grant it to a user with GET_INFO permission.
+ let grant_key_nspace = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let access_vector = KeyPermission::GET_INFO.0;
+ let alias = format!("ks_grant_perm_denied_key_{}", getuid());
+ let mut grant_keys = generate_ec_key_and_grant_to_users(
+ &keystore2,
+ &sec_level,
+ Some(alias),
+ vec![GRANTEE_UID.try_into().unwrap()],
+ access_vector,
+ )
+ .unwrap();
+
+ grant_keys.remove(0)
+ })
+ };
+
+ // Grantee context, load the granted key and try to grant it to `SEC_GRANTEE_UID` grantee.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+ let access_vector = KeyPermission::GET_INFO.0;
+
+ let key_entry_response = keystore2
+ .getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ })
+ .unwrap();
+
+ let result = key_generations::map_ks_error(keystore2.grant(
+ &key_entry_response.metadata.key,
+ SEC_GRANTEE_UID.try_into().unwrap(),
+ access_vector,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+ },
+ )
+ };
+
+ // Make sure second grantee shouldn't have access to the above granted key.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(SEC_GRANTEE_UID),
+ Gid::from_raw(SEC_GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ }));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+ },
+ )
+ };
+}
+
+/// Try to grant a key with `GRANT` access. Keystore2 system shouldn't allow to grant a key with
+/// `GRANT` access. Test should fail to grant a key with `PERMISSION_DENIED` error response code.
+#[test]
+fn keystore2_grant_key_fails_with_grant_perm_expect_perm_denied() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let access_vector = KeyPermission::GRANT.0;
+ let alias = format!("ks_grant_access_vec_key_{}", getuid());
+ let user_id = 98;
+ let application_id = 10001;
+ let grantee_uid = user_id * AID_USER_OFFSET + application_id;
+
+ let result = key_generations::map_ks_error(generate_ec_key_and_grant_to_users(
+ &keystore2,
+ &sec_level,
+ Some(alias),
+ vec![grantee_uid.try_into().unwrap()],
+ access_vector,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+}
+
+/// Try to grant a non-existing key to the user. Test should fail with `KEY_NOT_FOUND` error
+/// response.
+#[test]
+fn keystore2_grant_fails_with_non_existing_key_expect_key_not_found_err() {
+ let keystore2 = get_keystore_service();
+ let alias = format!("ks_grant_test_non_existing_key_5_{}", getuid());
+ let user_id = 98;
+ let application_id = 10001;
+ let grantee_uid = user_id * AID_USER_OFFSET + application_id;
+ let access_vector = KeyPermission::GET_INFO.0;
+
+ let result = key_generations::map_ks_error(keystore2.grant(
+ &KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+ alias: Some(alias),
+ blob: None,
+ },
+ grantee_uid.try_into().unwrap(),
+ access_vector,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Grant a key to the user and immediately ungrant the granted key. In grantee context try to load
+/// the key. Grantee should fail to load the ungranted key with `KEY_NOT_FOUND` error response.
+#[test]
+fn keystore2_ungrant_key_success() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+
+ // Generate a key and grant it to a user with GET_INFO permission.
+ let grant_key_nspace = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_ungrant_test_key_1{}", getuid());
+ let access_vector = KeyPermission::GET_INFO.0;
+ let mut grant_keys = generate_ec_key_and_grant_to_users(
+ &keystore2,
+ &sec_level,
+ Some(alias.to_string()),
+ vec![GRANTEE_UID.try_into().unwrap()],
+ access_vector,
+ )
+ .unwrap();
+
+ let grant_key_nspace = grant_keys.remove(0);
+
+ //Ungrant above granted key.
+ keystore2
+ .ungrant(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias),
+ blob: None,
+ },
+ GRANTEE_UID.try_into().unwrap(),
+ )
+ .unwrap();
+
+ grant_key_nspace
+ })
+ };
+
+ // Grantee context, try to load the ungranted key.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+ let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+ },
+ )
+ };
+}
+
+/// Generate a key, grant it to the user and then delete the granted key. Try to ungrant
+/// a deleted key. Test should fail to ungrant a non-existing key with `KEY_NOT_FOUND` error
+/// response. Generate a new key with the same alias and try to access the previously granted
+/// key in grantee context. Test should fail to load the granted key in grantee context as the
+/// associated key is deleted from grantor context.
+#[test]
+fn keystore2_ungrant_fails_with_non_existing_key_expect_key_not_found_error() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const APPLICATION_ID: u32 = 10001;
+ const USER_ID: u32 = 99;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+
+ let grant_key_nspace = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("{}{}", "ks_grant_delete_ungrant_test_key_1", getuid());
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias.to_string()),
+ None,
+ )
+ .unwrap();
+
+ let access_vector = KeyPermission::GET_INFO.0;
+ let grant_key = keystore2
+ .grant(&key_metadata.key, GRANTEE_UID.try_into().unwrap(), access_vector)
+ .unwrap();
+ assert_eq!(grant_key.domain, Domain::GRANT);
+
+ // Delete above granted key.
+ keystore2.deleteKey(&key_metadata.key).unwrap();
+
+ // Try to ungrant above granted key.
+ let result = key_generations::map_ks_error(
+ keystore2.ungrant(&key_metadata.key, GRANTEE_UID.try_into().unwrap()),
+ );
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+
+ // Generate a new key with the same alias and try to access the earlier granted key
+ // in grantee context.
+ let result = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias),
+ None,
+ );
+ assert!(result.is_ok());
+
+ grant_key.nspace
+ })
+ };
+
+ // Make sure grant did not persist, try to access the earlier granted key in grantee context.
+ // Grantee context should fail to load the granted key as its associated key is deleted in
+ // grantor context.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+ },
+ )
+ };
+}
+
+/// Grant a key to multiple users. Verify that all grantees should succeed in loading the key and
+/// use it for performing an operation successfully.
+#[test]
+fn keystore2_grant_key_to_multi_users_success() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const APPLICATION_ID: u32 = 10001;
+ const USER_ID_1: u32 = 99;
+ static GRANTEE_1_UID: u32 = USER_ID_1 * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_1_GID: u32 = GRANTEE_1_UID;
+
+ const USER_ID_2: u32 = 98;
+ static GRANTEE_2_UID: u32 = USER_ID_2 * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_2_GID: u32 = GRANTEE_2_UID;
+
+ // Generate a key and grant it to multiple users with GET_INFO|USE permissions.
+ let mut grant_keys = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_grant_test_key_2{}", getuid());
+ let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::USE.0;
+
+ generate_ec_key_and_grant_to_users(
+ &keystore2,
+ &sec_level,
+ Some(alias),
+ vec![GRANTEE_1_UID.try_into().unwrap(), GRANTEE_2_UID.try_into().unwrap()],
+ access_vector,
+ )
+ .unwrap()
+ })
+ };
+
+ for (grantee_uid, grantee_gid) in
+ &[(GRANTEE_1_UID, GRANTEE_1_GID), (GRANTEE_2_UID, GRANTEE_2_GID)]
+ {
+ let grant_key_nspace = grant_keys.remove(0);
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(*grantee_uid),
+ Gid::from_raw(*grantee_gid),
+ move || {
+ let keystore2 = get_keystore_service();
+ let sec_level =
+ keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(load_grant_key_and_perform_sign_operation(
+ &keystore2,
+ &sec_level,
+ grant_key_nspace
+ ))
+ );
+ },
+ )
+ };
+ }
+}
+
+/// Grant a key to multiple users with GET_INFO|DELETE permissions. In one of the grantee context
+/// use the key and delete it. Try to load the granted key in another grantee context. Test should
+/// fail to load the granted key with `KEY_NOT_FOUND` error response.
+#[test]
+fn keystore2_grant_key_to_multi_users_delete_fails_with_key_not_found_error() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const USER_ID_1: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_1_UID: u32 = USER_ID_1 * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_1_GID: u32 = GRANTEE_1_UID;
+
+ const USER_ID_2: u32 = 98;
+ static GRANTEE_2_UID: u32 = USER_ID_2 * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_2_GID: u32 = GRANTEE_2_UID;
+
+ // Generate a key and grant it to multiple users with GET_INFO permission.
+ let mut grant_keys = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_grant_test_key_2{}", getuid());
+ let access_vector =
+ KeyPermission::GET_INFO.0 | KeyPermission::USE.0 | KeyPermission::DELETE.0;
+
+ generate_ec_key_and_grant_to_users(
+ &keystore2,
+ &sec_level,
+ Some(alias),
+ vec![GRANTEE_1_UID.try_into().unwrap(), GRANTEE_2_UID.try_into().unwrap()],
+ access_vector,
+ )
+ .unwrap()
+ })
+ };
+
+ // Grantee #1 context
+ let grant_key1_nspace = grant_keys.remove(0);
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_1_UID),
+ Gid::from_raw(GRANTEE_1_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+ let sec_level =
+ keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(load_grant_key_and_perform_sign_operation(
+ &keystore2,
+ &sec_level,
+ grant_key1_nspace
+ ))
+ );
+
+ // Delete the granted key.
+ keystore2
+ .deleteKey(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key1_nspace,
+ alias: None,
+ blob: None,
+ })
+ .unwrap();
+ },
+ )
+ };
+
+ // Grantee #2 context
+ let grant_key2_nspace = grant_keys.remove(0);
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_2_UID),
+ Gid::from_raw(GRANTEE_2_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key2_nspace,
+ alias: None,
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+ },
+ )
+ };
+}
diff --git a/keystore2/tests/keystore2_client_test_utils.rs b/keystore2/tests/keystore2_client_test_utils.rs
index 56995e4..58e6b7d 100644
--- a/keystore2/tests/keystore2_client_test_utils.rs
+++ b/keystore2/tests/keystore2_client_test_utils.rs
@@ -90,6 +90,29 @@
.expect("Could not check for declared keymint interface")
}
+/// Generate EC key and grant it to the list of users with given access vector.
+/// Returns the list of granted keys `nspace` values in the order of given grantee uids.
+pub fn generate_ec_key_and_grant_to_users(
+ keystore2: &binder::Strong<dyn IKeystoreService>,
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ alias: Option<String>,
+ grantee_uids: Vec<i32>,
+ access_vector: i32,
+) -> Result<Vec<i64>, binder::Status> {
+ let key_metadata =
+ key_generations::generate_ec_p256_signing_key(sec_level, Domain::APP, -1, alias, None)?;
+
+ let mut granted_keys = Vec::new();
+
+ for uid in grantee_uids {
+ let granted_key = keystore2.grant(&key_metadata.key, uid, access_vector)?;
+ assert_eq!(granted_key.domain, Domain::GRANT);
+ granted_keys.push(granted_key.nspace);
+ }
+
+ Ok(granted_keys)
+}
+
/// Generate a EC_P256 key using given domain, namespace and alias.
/// Create an operation using the generated key and perform sample signing operation.
pub fn create_signing_operation(
diff --git a/keystore2/tests/keystore2_client_tests.rs b/keystore2/tests/keystore2_client_tests.rs
index d705aa4..45c5fb7 100644
--- a/keystore2/tests/keystore2_client_tests.rs
+++ b/keystore2/tests/keystore2_client_tests.rs
@@ -16,6 +16,7 @@
pub mod keystore2_client_3des_key_tests;
pub mod keystore2_client_aes_key_tests;
pub mod keystore2_client_attest_key_tests;
+pub mod keystore2_client_delete_key_tests;
pub mod keystore2_client_ec_key_tests;
pub mod keystore2_client_grant_key_tests;
pub mod keystore2_client_hmac_key_tests;
@@ -25,3 +26,4 @@
pub mod keystore2_client_operation_tests;
pub mod keystore2_client_rsa_key_tests;
pub mod keystore2_client_test_utils;
+pub mod keystore2_client_update_subcomponent_tests;
diff --git a/keystore2/tests/keystore2_client_update_subcomponent_tests.rs b/keystore2/tests/keystore2_client_update_subcomponent_tests.rs
new file mode 100644
index 0000000..c987f22
--- /dev/null
+++ b/keystore2/tests/keystore2_client_update_subcomponent_tests.rs
@@ -0,0 +1,230 @@
+// Copyright 2022, 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.
+
+use nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ ErrorCode::ErrorCode, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+ ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error, run_as};
+
+/// Generate a key and update its public certificate and certificate chain. Test should be able to
+/// load the key and able to verify whether its certificate and cert-chain are updated successfully.
+#[test]
+fn keystore2_update_subcomponent_success() {
+ let alias = "update_subcomponent_success_key";
+
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias.to_string()),
+ None,
+ )
+ .unwrap();
+
+ let other_cert: [u8; 32] = [123; 32];
+ let other_cert_chain: [u8; 32] = [12; 32];
+
+ keystore2
+ .updateSubcomponent(&key_metadata.key, Some(&other_cert), Some(&other_cert_chain))
+ .expect("updateSubcomponent should have succeeded.");
+
+ let key_entry_response = keystore2.getKeyEntry(&key_metadata.key).unwrap();
+ assert_eq!(Some(other_cert.to_vec()), key_entry_response.metadata.certificate);
+ assert_eq!(Some(other_cert_chain.to_vec()), key_entry_response.metadata.certificateChain);
+}
+
+/// Try to update non-existing asymmetric key public cert and certificate chain. Test should fail
+/// to update with error response code `KEY_NOT_FOUND`.
+#[test]
+fn keystore2_update_subcomponent_fail() {
+ let alias = "update_component_failure_key";
+
+ let keystore2 = get_keystore_service();
+
+ let other_cert: [u8; 32] = [123; 32];
+ let other_cert_chain: [u8; 32] = [12; 32];
+
+ let result = key_generations::map_ks_error(keystore2.updateSubcomponent(
+ &KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+ alias: Some(alias.to_string()),
+ blob: None,
+ },
+ Some(&other_cert),
+ Some(&other_cert_chain),
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Generate a key and grant it to two users. For one user grant it with only `GET_INFO` access
+/// permission and for another user grant it with GET_INFO and UPDATE access permissions. In a
+/// grantee context where key is granted with only GET_INFO access permission, try to update
+/// key's public certificate and certificate chain. Test should fail to update with error response
+/// code `PERMISSION_DENIED` because grantee does not possess UPDATE access permission for the
+/// specified key. In a grantee context where key is granted with UPDATE and GET_INFO access
+/// permissions, test should be able to update public certificate and cert-chain successfully.
+#[test]
+fn keystore2_update_subcomponent_fails_permission_denied() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const USER_ID_1: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_1_UID: u32 = USER_ID_1 * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_1_GID: u32 = GRANTEE_1_UID;
+
+ const USER_ID_2: u32 = 98;
+ static GRANTEE_2_UID: u32 = USER_ID_2 * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_2_GID: u32 = GRANTEE_2_UID;
+
+ // Generate a key and grant it to multiple users with different access permissions.
+ let mut granted_keys = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_update_subcompo_test_1_{}", getuid());
+ let mut granted_keys = Vec::new();
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias),
+ None,
+ )
+ .unwrap();
+
+ // Grant a key without update permission.
+ let access_vector = KeyPermission::GET_INFO.0;
+ let granted_key = keystore2
+ .grant(&key_metadata.key, GRANTEE_1_UID.try_into().unwrap(), access_vector)
+ .unwrap();
+ assert_eq!(granted_key.domain, Domain::GRANT);
+ granted_keys.push(granted_key.nspace);
+
+ // Grant a key with update permission.
+ let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::UPDATE.0;
+ let granted_key = keystore2
+ .grant(&key_metadata.key, GRANTEE_2_UID.try_into().unwrap(), access_vector)
+ .unwrap();
+ assert_eq!(granted_key.domain, Domain::GRANT);
+ granted_keys.push(granted_key.nspace);
+
+ granted_keys
+ })
+ };
+
+ // Grantee context, try to update the key public certs, permission denied error is expected.
+ let granted_key1_nspace = granted_keys.remove(0);
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_1_UID),
+ Gid::from_raw(GRANTEE_1_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+
+ let other_cert: [u8; 32] = [123; 32];
+ let other_cert_chain: [u8; 32] = [12; 32];
+
+ let result = key_generations::map_ks_error(keystore2.updateSubcomponent(
+ &KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: granted_key1_nspace,
+ alias: None,
+ blob: None,
+ },
+ Some(&other_cert),
+ Some(&other_cert_chain),
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+ },
+ )
+ };
+
+ // Grantee context, update granted key public certs. Update should happen successfully.
+ let granted_key2_nspace = granted_keys.remove(0);
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_2_UID),
+ Gid::from_raw(GRANTEE_2_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+
+ let other_cert: [u8; 32] = [124; 32];
+ let other_cert_chain: [u8; 32] = [13; 32];
+
+ keystore2
+ .updateSubcomponent(
+ &KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: granted_key2_nspace,
+ alias: None,
+ blob: None,
+ },
+ Some(&other_cert),
+ Some(&other_cert_chain),
+ )
+ .expect("updateSubcomponent should have succeeded.");
+
+ let key_entry_response = keystore2
+ .getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: granted_key2_nspace,
+ alias: None,
+ blob: None,
+ })
+ .unwrap();
+ assert_eq!(Some(other_cert.to_vec()), key_entry_response.metadata.certificate);
+ assert_eq!(
+ Some(other_cert_chain.to_vec()),
+ key_entry_response.metadata.certificateChain
+ );
+ },
+ )
+ };
+}
+
+#[test]
+fn keystore2_get_security_level_success() {
+ let keystore2 = get_keystore_service();
+ assert!(
+ keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).is_ok(),
+ "getSecurityLevel with SecurityLevel::TRUSTED_ENVIRONMENT should have succeeded."
+ );
+}
+
+#[test]
+fn keystore2_get_security_level_failure() {
+ let keystore2 = get_keystore_service();
+ let result = key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::SOFTWARE));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+}