Merge "Adding AIDL fuzzer for AuthorizationManager"
diff --git a/diced/Android.bp b/diced/Android.bp
index ddcc8c1..6e857f4 100644
--- a/diced/Android.bp
+++ b/diced/Android.bp
@@ -30,7 +30,7 @@
rustlibs: [
"android.hardware.security.dice-V1-rust",
"libanyhow",
- "libdiced_open_dice_cbor",
+ "libdiced_open_dice",
"libkeystore2_crypto_rust",
],
}
@@ -44,7 +44,7 @@
rustlibs: [
"android.hardware.security.dice-V1-rust",
"libanyhow",
- "libdiced_open_dice_cbor",
+ "libdiced_open_dice",
"libkeystore2_crypto_rust",
],
}
@@ -58,9 +58,8 @@
rustlibs: [
"android.hardware.security.dice-V1-rust",
"libanyhow",
- "libdiced_open_dice_cbor",
+ "libdiced_open_dice",
"libdiced_utils",
- "libkeystore2_crypto_rust",
],
}
@@ -73,29 +72,8 @@
rustlibs: [
"android.hardware.security.dice-V1-rust",
"libanyhow",
- "libdiced_open_dice_cbor",
+ "libdiced_open_dice",
"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",
],
}
@@ -107,7 +85,7 @@
vendor_available: true,
rustlibs: [
"android.hardware.security.dice-V1-rust",
- "libdiced_open_dice_cbor",
+ "libdiced_open_dice",
"libanyhow",
"libbinder_rs",
"libdiced_utils",
@@ -122,31 +100,6 @@
}
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",
srcs: ["src/lib_vendor.rs"],
@@ -155,7 +108,7 @@
rustlibs: [
"android.hardware.security.dice-V1-rust",
"libanyhow",
- "libdiced_open_dice_cbor",
+ "libdiced_open_dice",
"libdiced_sample_inputs",
"libdiced_utils",
"libbinder_rs",
@@ -185,7 +138,7 @@
"android.security.dice-rust",
"libanyhow",
"libbinder_rs",
- "libdiced_open_dice_cbor",
+ "libdiced_open_dice",
"libdiced_sample_inputs",
"libdiced_utils",
"libnix",
diff --git a/diced/OWNERS b/diced/OWNERS
new file mode 100644
index 0000000..387cd93
--- /dev/null
+++ b/diced/OWNERS
@@ -0,0 +1,3 @@
+alanstokes@google.com
+aliceywang@google.com
+ascull@google.com
diff --git a/diced/TEST_MAPPING b/diced/TEST_MAPPING
index b3d8ac4..8629b07 100644
--- a/diced/TEST_MAPPING
+++ b/diced/TEST_MAPPING
@@ -7,15 +7,18 @@
"name": "libdiced_open_dice_nostd.integration_test"
},
{
+ "name": "libopen_dice_cbor_bindgen_test"
+ },
+ {
+ "name": "libopen_dice_bcc_bindgen_test"
+ },
+ {
"name": "diced_utils_test"
},
{
"name": "diced_sample_inputs_test"
},
{
- "name": "diced_test"
- },
- {
"name": "diced_vendor_test"
}
]
diff --git a/diced/open_dice/Android.bp b/diced/open_dice/Android.bp
index e2c7dd1..2505b42 100644
--- a/diced/open_dice/Android.bp
+++ b/diced/open_dice/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_visibility: [":__subpackages__"],
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -22,11 +23,15 @@
rustlibs: [
"libopen_dice_bcc_bindgen_nostd",
"libopen_dice_cbor_bindgen_nostd",
+ "libzeroize_nostd",
],
- whole_static_libs: [
+ whole_static_libs: [
"libopen_dice_cbor",
"libcrypto_baremetal",
],
+ visibility: [
+ "//packages/modules/Virtualization:__subpackages__",
+ ],
}
rust_library {
@@ -35,15 +40,22 @@
rustlibs: [
"libopen_dice_bcc_bindgen",
"libopen_dice_cbor_bindgen",
- // For ZVec
- "libkeystore2_crypto_rust",
+ "libzeroize",
],
features: [
"std",
],
+ shared_libs: [
+ "libcrypto",
+ ],
whole_static_libs: [
"libopen_dice_bcc",
],
+ visibility: [
+ "//system/security/diced:__subpackages__",
+ "//packages/modules/Virtualization:__subpackages__",
+ "//hardware/interfaces/security/dice/aidl:__subpackages__",
+ ],
}
rust_defaults {
@@ -68,3 +80,177 @@
"libdiced_open_dice_nostd",
],
}
+
+rust_defaults {
+ name: "libopen_dice_bindgen_nostd.rust_defaults",
+ bindgen_flags: [
+ "--use-core",
+ "--ctypes-prefix=core::ffi",
+ "--raw-line=#![no_std]",
+ ],
+ no_stdlibs: true,
+ prefer_rlib: true,
+ stdlibs: [
+ "libcore.rust_sysroot",
+ "libcompiler_builtins.rust_sysroot",
+ ],
+ target: {
+ musl: {
+ enabled: false,
+ },
+ glibc: {
+ enabled: false,
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+rust_defaults {
+ name: "libopen_dice.rust_defaults",
+ host_supported: true,
+ vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.compos",
+ "com.android.virt",
+ ],
+}
+
+rust_defaults {
+ name: "libopen_dice_cbor_bindgen.rust_defaults",
+ defaults: ["libopen_dice.rust_defaults"],
+ wrapper_src: "bindgen/dice.h",
+ crate_name: "open_dice_cbor_bindgen",
+ source_stem: "bindings",
+ bindgen_flags: [
+ "--size_t-is-usize",
+ "--rustified-enum DiceConfigType",
+ "--rustified-enum DiceMode",
+ "--rustified-enum DiceResult",
+
+ // By generating only essential functions, we can make bindings concise and
+ // optimize compilation time.
+ "--allowlist-function=DiceDeriveCdiPrivateKeySeed",
+ "--allowlist-function=DiceDeriveCdiCertificateId",
+ "--allowlist-function=DiceMainFlow",
+ "--allowlist-function=DiceHash",
+ "--allowlist-function=DiceKdf",
+ "--allowlist-function=DiceKeypairFromSeed",
+ "--allowlist-function=DiceSign",
+ "--allowlist-function=DiceVerify",
+ "--allowlist-function=DiceGenerateCertificate",
+
+ // We also need some constants in addition to the functions.
+ "--allowlist-var=DICE_CDI_SIZE",
+ "--allowlist-var=DICE_HASH_SIZE",
+ "--allowlist-var=DICE_HIDDEN_SIZE",
+ "--allowlist-var=DICE_INLINE_CONFIG_SIZE",
+ "--allowlist-var=DICE_PRIVATE_KEY_SEED_SIZE",
+ "--allowlist-var=DICE_ID_SIZE",
+ "--allowlist-var=DICE_PUBLIC_KEY_SIZE",
+ "--allowlist-var=DICE_PRIVATE_KEY_SIZE",
+ "--allowlist-var=DICE_SIGNATURE_SIZE",
+ ],
+}
+
+rust_bindgen {
+ name: "libopen_dice_cbor_bindgen",
+ defaults: ["libopen_dice_cbor_bindgen.rust_defaults"],
+ whole_static_libs: ["libopen_dice_cbor"],
+}
+
+rust_bindgen {
+ name: "libopen_dice_cbor_bindgen_nostd",
+ defaults: [
+ "libopen_dice_cbor_bindgen.rust_defaults",
+ "libopen_dice_bindgen_nostd.rust_defaults",
+ ],
+ whole_static_libs: ["libopen_dice_cbor_baremetal"],
+}
+
+rust_defaults {
+ name: "libopen_dice_bcc_bindgen.rust_defaults",
+ defaults: ["libopen_dice.rust_defaults"],
+ wrapper_src: "bindgen/android/bcc.h",
+ crate_name: "open_dice_bcc_bindgen",
+ source_stem: "bindings",
+ bindgen_flags: [
+ "--size_t-is-usize",
+
+ // By generating only essential functions, we can make bindings concise and
+ // optimize compilation time.
+ "--allowlist-function=BccFormatConfigDescriptor",
+ "--allowlist-function=BccMainFlow",
+ "--allowlist-function=BccHandoverMainFlow",
+ "--allowlist-function=BccHandoverParse",
+
+ // We also need some constants in addition to the functions.
+ "--allowlist-var=BCC_INPUT_COMPONENT_NAME",
+ "--allowlist-var=BCC_INPUT_COMPONENT_VERSION",
+ "--allowlist-var=BCC_INPUT_RESETTABLE",
+
+ // Prevent DiceInputValues from being generated a second time and
+ // import it instead from open_dice_cbor_bindgen.
+ "--blocklist-type=DiceInputValues_",
+ "--blocklist-type=DiceInputValues",
+ "--raw-line",
+ "pub use open_dice_cbor_bindgen::DiceInputValues;",
+
+ // Prevent DiceResult from being generated a second time and
+ // import it instead from open_dice_cbor_bindgen.
+ "--blocklist-type=DiceResult",
+ "--raw-line",
+ "pub use open_dice_cbor_bindgen::DiceResult;",
+ ],
+
+}
+
+rust_bindgen {
+ name: "libopen_dice_bcc_bindgen",
+ defaults: ["libopen_dice_bcc_bindgen.rust_defaults"],
+ rustlibs: [
+ "libopen_dice_cbor_bindgen",
+ ],
+ whole_static_libs: ["libopen_dice_bcc"],
+}
+
+rust_bindgen {
+ name: "libopen_dice_bcc_bindgen_nostd",
+ defaults: [
+ "libopen_dice_bcc_bindgen.rust_defaults",
+ "libopen_dice_bindgen_nostd.rust_defaults",
+ ],
+ rustlibs: [
+ "libopen_dice_cbor_bindgen_nostd",
+ ],
+ whole_static_libs: ["libopen_dice_bcc_baremetal"],
+}
+
+rust_test {
+ name: "libopen_dice_cbor_bindgen_test",
+ srcs: [
+ ":libopen_dice_cbor_bindgen",
+ ],
+ crate_name: "open_dice_cbor_bindgen_test",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ clippy_lints: "none",
+ lints: "none",
+}
+
+rust_test {
+ name: "libopen_dice_bcc_bindgen_test",
+ srcs: [
+ ":libopen_dice_bcc_bindgen",
+ ],
+ crate_name: "open_dice_bcc_bindgen_test",
+ rustlibs: [
+ "libopen_dice_cbor_bindgen",
+ ],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ clippy_lints: "none",
+ lints: "none",
+}
diff --git a/diced/open_dice/bindgen/android/bcc.h b/diced/open_dice/bindgen/android/bcc.h
new file mode 100644
index 0000000..4dfc862
--- /dev/null
+++ b/diced/open_dice/bindgen/android/bcc.h
@@ -0,0 +1,17 @@
+// Copyright 2021 Google LLC
+//
+// 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
+//
+// https://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.
+
+#pragma once
+
+#include <dice/android/bcc.h>
diff --git a/diced/open_dice/bindgen/dice.h b/diced/open_dice/bindgen/dice.h
new file mode 100644
index 0000000..47fe911
--- /dev/null
+++ b/diced/open_dice/bindgen/dice.h
@@ -0,0 +1,18 @@
+// Copyright 2021 Google LLC
+//
+// 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
+//
+// https://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.
+
+#pragma once
+
+#include <dice/dice.h>
+#include <dice/ops.h>
diff --git a/diced/open_dice/src/bcc.rs b/diced/open_dice/src/bcc.rs
index 8bda225..1575113 100644
--- a/diced/open_dice/src/bcc.rs
+++ b/diced/open_dice/src/bcc.rs
@@ -14,10 +14,11 @@
//! This module mirrors the content in open-dice/include/dice/android/bcc.h
-use crate::error::{check_result, Result};
+use crate::dice::{Cdi, CdiValues, DiceArtifacts, InputValues, CDI_SIZE};
+use crate::error::{check_result, DiceError, Result};
use open_dice_bcc_bindgen::{
- BccConfigValues, BccFormatConfigDescriptor, BCC_INPUT_COMPONENT_NAME,
- BCC_INPUT_COMPONENT_VERSION, BCC_INPUT_RESETTABLE,
+ BccConfigValues, BccFormatConfigDescriptor, BccHandoverMainFlow, BccHandoverParse, BccMainFlow,
+ BCC_INPUT_COMPONENT_NAME, BCC_INPUT_COMPONENT_VERSION, BCC_INPUT_RESETTABLE,
};
use std::{ffi::CStr, ptr};
@@ -54,3 +55,138 @@
})?;
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)
+}
+
+/// Executes the main BCC handover flow.
+///
+/// A BCC handover combines the BCC and CDIs in a single CBOR object.
+/// This function takes the current boot stage's BCC handover bundle and produces a
+/// bundle for the next stage.
+pub fn bcc_handover_main_flow(
+ current_bcc_handover: &[u8],
+ input_values: &InputValues,
+ next_bcc_handover: &mut [u8],
+) -> Result<usize> {
+ let mut next_bcc_handover_size = 0;
+ // SAFETY - The function only reads `current_bcc_handover` and writes to `next_bcc_handover`
+ // 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 {
+ BccHandoverMainFlow(
+ ptr::null_mut(), // context
+ current_bcc_handover.as_ptr(),
+ current_bcc_handover.len(),
+ input_values.as_ptr(),
+ next_bcc_handover.len(),
+ next_bcc_handover.as_mut_ptr(),
+ &mut next_bcc_handover_size,
+ )
+ })?;
+
+ Ok(next_bcc_handover_size)
+}
+
+/// A BCC handover combines the BCC and CDIs in a single CBOR object.
+/// This struct is used as return of the function `bcc_handover_parse`, its lifetime is tied
+/// to the lifetime of the raw BCC handover slice.
+#[derive(Debug)]
+pub struct BccHandover<'a> {
+ /// Attestation CDI.
+ cdi_attest: &'a [u8; CDI_SIZE],
+ /// Sealing CDI.
+ cdi_seal: &'a [u8; CDI_SIZE],
+ /// Boot Certificate Chain.
+ bcc: Option<&'a [u8]>,
+}
+
+impl<'a> DiceArtifacts for BccHandover<'a> {
+ fn cdi_attest(&self) -> &[u8; CDI_SIZE] {
+ self.cdi_attest
+ }
+
+ fn cdi_seal(&self) -> &[u8; CDI_SIZE] {
+ self.cdi_seal
+ }
+
+ fn bcc(&self) -> Option<&[u8]> {
+ self.bcc
+ }
+}
+
+/// A BCC handover combines the BCC and CDIs in a single CBOR object.
+/// This function parses the `bcc_handover` to extracts the BCC and CDIs.
+/// The lifetime of the returned `BccHandover` is tied to the given `bcc_handover` slice.
+pub fn bcc_handover_parse(bcc_handover: &[u8]) -> Result<BccHandover> {
+ let mut cdi_attest: *const u8 = ptr::null();
+ let mut cdi_seal: *const u8 = ptr::null();
+ let mut bcc: *const u8 = ptr::null();
+ let mut bcc_size = 0;
+ // SAFETY: The `bcc_handover` is only read and never stored and the returned pointers should all
+ // point within the address range of the `bcc_handover` or be NULL.
+ check_result(unsafe {
+ BccHandoverParse(
+ bcc_handover.as_ptr(),
+ bcc_handover.len(),
+ &mut cdi_attest,
+ &mut cdi_seal,
+ &mut bcc,
+ &mut bcc_size,
+ )
+ })?;
+ let cdi_attest = sub_slice(bcc_handover, cdi_attest, CDI_SIZE)?;
+ let cdi_seal = sub_slice(bcc_handover, cdi_seal, CDI_SIZE)?;
+ let bcc = sub_slice(bcc_handover, bcc, bcc_size).ok();
+ Ok(BccHandover {
+ cdi_attest: cdi_attest.try_into().map_err(|_| DiceError::PlatformError)?,
+ cdi_seal: cdi_seal.try_into().map_err(|_| DiceError::PlatformError)?,
+ bcc,
+ })
+}
+
+/// Gets a slice the `addr` points to and of length `len`.
+/// The slice should be contained in the buffer.
+fn sub_slice(buffer: &[u8], addr: *const u8, len: usize) -> Result<&[u8]> {
+ if addr.is_null() || !buffer.as_ptr_range().contains(&addr) {
+ return Err(DiceError::PlatformError);
+ }
+ // SAFETY: This is safe because addr is not null and is within the range of the buffer.
+ let start: usize = unsafe {
+ addr.offset_from(buffer.as_ptr()).try_into().map_err(|_| DiceError::PlatformError)?
+ };
+ start.checked_add(len).and_then(|end| buffer.get(start..end)).ok_or(DiceError::PlatformError)
+}
diff --git a/diced/open_dice/src/dice.rs b/diced/open_dice/src/dice.rs
index 014d5fd..9266b6f 100644
--- a/diced/open_dice/src/dice.rs
+++ b/diced/open_dice/src/dice.rs
@@ -19,10 +19,12 @@
pub use open_dice_cbor_bindgen::DiceMode;
use open_dice_cbor_bindgen::{
DiceConfigType, DiceDeriveCdiCertificateId, DiceDeriveCdiPrivateKeySeed, DiceInputValues,
- DICE_CDI_SIZE, DICE_HASH_SIZE, DICE_HIDDEN_SIZE, DICE_ID_SIZE, DICE_INLINE_CONFIG_SIZE,
- DICE_PRIVATE_KEY_SEED_SIZE,
+ DiceMainFlow, 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,
};
use std::ptr;
+use zeroize::{Zeroize, ZeroizeOnDrop};
/// The size of a DICE hash.
pub const HASH_SIZE: usize = DICE_HASH_SIZE as usize;
@@ -34,6 +36,12 @@
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 a private key.
+pub const PRIVATE_KEY_SIZE: usize = DICE_PRIVATE_KEY_SIZE as usize;
+/// The size of a public key.
+pub const PUBLIC_KEY_SIZE: usize = DICE_PUBLIC_KEY_SIZE as usize;
+/// The size of a signature.
+pub const SIGNATURE_SIZE: usize = DICE_SIGNATURE_SIZE as usize;
/// The size of an ID.
pub const ID_SIZE: usize = DICE_ID_SIZE as usize;
@@ -45,11 +53,85 @@
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 the public key.
+pub type PublicKey = [u8; PUBLIC_KEY_SIZE];
+/// Array type of the signature.
+pub type Signature = [u8; SIGNATURE_SIZE];
/// Array type of DICE ID.
pub type DiceId = [u8; ID_SIZE];
+/// A trait for types that represent Dice artifacts, which include:
+///
+/// - Attestation CDI
+/// - Sealing CDI
+/// - Boot Certificate Chain
+///
+/// Types that implement this trait provide an access these artifacts.
+pub trait DiceArtifacts {
+ /// Returns a reference to the attestation CDI.
+ fn cdi_attest(&self) -> &[u8; CDI_SIZE];
+
+ /// Returns a reference to the sealing CDI.
+ fn cdi_seal(&self) -> &[u8; CDI_SIZE];
+
+ /// Returns a reference to the Boot Certificate Chain, if present.
+ fn bcc(&self) -> Option<&[u8]>;
+}
+
+/// TODO(b/268587826): Clean up the memory cache after zeroing out the memory
+/// for sensitive data like CDI values and private key.
+/// CDI Values.
+#[derive(Debug, Zeroize, ZeroizeOnDrop, Default)]
+pub struct CdiValues {
+ /// Attestation CDI.
+ pub cdi_attest: [u8; CDI_SIZE],
+ /// Sealing CDI.
+ pub cdi_seal: [u8; CDI_SIZE],
+}
+
+/// Private key seed. The data is zeroed out when the struct is dropped.
+#[derive(Zeroize, ZeroizeOnDrop, Default)]
+pub struct PrivateKeySeed([u8; PRIVATE_KEY_SEED_SIZE]);
+
+impl PrivateKeySeed {
+ /// Returns an array reference of the private key seed.
+ pub fn as_array(&self) -> &[u8; PRIVATE_KEY_SEED_SIZE] {
+ &self.0
+ }
+
+ /// Returns a mutable pointer to the slice buffer of the private key seed.
+ pub fn as_mut_ptr(&mut self) -> *mut u8 {
+ self.0.as_mut_ptr()
+ }
+}
+
+/// Private key. The data is zeroed out when the struct is dropped.
+#[derive(Zeroize, ZeroizeOnDrop)]
+pub struct PrivateKey([u8; PRIVATE_KEY_SIZE]);
+
+impl Default for PrivateKey {
+ /// Creates a new `PrivateKey` instance with all bytes set to 0.
+ ///
+ /// Since the size of the private key array is too large to be initialized
+ /// with a default value, this implementation sets all the bytes in the array
+ /// to 0 using the `[0u8; PRIVATE_KEY_SIZE]` syntax.
+ fn default() -> Self {
+ Self([0u8; PRIVATE_KEY_SIZE])
+ }
+}
+
+impl PrivateKey {
+ /// Returns an array reference of the private key.
+ pub fn as_array(&self) -> &[u8; PRIVATE_KEY_SIZE] {
+ &self.0
+ }
+
+ /// Returns a mutable pointer to the slice buffer of the private key.
+ pub fn as_mut_ptr(&mut self) -> *mut u8 {
+ self.0.as_mut_ptr()
+ }
+}
+
/// Configuration descriptor for DICE input values.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Config<'a> {
@@ -126,7 +208,7 @@
/// 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];
+ let mut seed = PrivateKeySeed::default();
// 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 {
@@ -154,3 +236,35 @@
})?;
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
index 7405c51..4c67335 100644
--- a/diced/open_dice/src/error.rs
+++ b/diced/open_dice/src/error.rs
@@ -29,16 +29,6 @@
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.
@@ -51,8 +41,6 @@
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}"),
}
}
}
diff --git a/diced/open_dice/src/lib.rs b/diced/open_dice/src/lib.rs
index 5a34698..e7ec56b 100644
--- a/diced/open_dice/src/lib.rs
+++ b/diced/open_dice/src/lib.rs
@@ -27,13 +27,19 @@
#[cfg(feature = "std")]
mod retry;
-pub use bcc::bcc_format_config_descriptor;
+pub use bcc::{
+ bcc_format_config_descriptor, bcc_handover_main_flow, bcc_handover_parse, bcc_main_flow,
+ BccHandover,
+};
pub use dice::{
- derive_cdi_certificate_id, derive_cdi_private_key_seed, Cdi, Config, DiceMode, Hash, Hidden,
- InlineConfig, InputValues, PrivateKeySeed, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE, ID_SIZE,
- PRIVATE_KEY_SEED_SIZE,
+ derive_cdi_certificate_id, derive_cdi_private_key_seed, dice_main_flow, Cdi, CdiValues, Config,
+ DiceArtifacts, DiceMode, Hash, Hidden, InlineConfig, InputValues, PrivateKey, PrivateKeySeed,
+ PublicKey, Signature, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
};
pub use error::{check_result, DiceError, Result};
-pub use ops::hash;
+pub use ops::{generate_certificate, hash, kdf, keypair_from_seed, sign, verify};
#[cfg(feature = "std")]
-pub use retry::retry_bcc_format_config_descriptor;
+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
index ca0e341..8222b26 100644
--- a/diced/open_dice/src/ops.rs
+++ b/diced/open_dice/src/ops.rs
@@ -16,9 +16,14 @@
//! It contains the set of functions that implement various operations that the
//! main DICE functions depend on.
-use crate::dice::{Hash, HASH_SIZE};
+use crate::dice::{
+ Hash, InputValues, PrivateKey, PublicKey, Signature, HASH_SIZE, PRIVATE_KEY_SEED_SIZE,
+ PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE, SIGNATURE_SIZE,
+};
use crate::error::{check_result, Result};
-use open_dice_cbor_bindgen::DiceHash;
+use open_dice_cbor_bindgen::{
+ DiceGenerateCertificate, DiceHash, DiceKdf, DiceKeypairFromSeed, DiceSign, DiceVerify,
+};
use std::ptr;
/// Hashes the provided input using DICE's hash function `DiceHash`.
@@ -36,3 +41,102 @@
})?;
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(),
+ )
+ })
+}
+
+/// Deterministically generates a public and private key pair from `seed`.
+/// Since this is deterministic, `seed` is as sensitive as a private key and can
+/// be used directly as the private key.
+pub fn keypair_from_seed(seed: &[u8; PRIVATE_KEY_SEED_SIZE]) -> Result<(PublicKey, PrivateKey)> {
+ let mut public_key = [0u8; PUBLIC_KEY_SIZE];
+ let mut private_key = PrivateKey::default();
+ // SAFETY: The function writes to the `public_key` and `private_key` within the given bounds,
+ // and only reads the `seed`. The first argument context is not used in this function.
+ check_result(unsafe {
+ DiceKeypairFromSeed(
+ ptr::null_mut(), // context
+ seed.as_ptr(),
+ public_key.as_mut_ptr(),
+ private_key.as_mut_ptr(),
+ )
+ })?;
+ Ok((public_key, private_key))
+}
+
+/// Signs the `message` with the give `private_key` using `DiceSign`.
+pub fn sign(message: &[u8], private_key: &[u8; PRIVATE_KEY_SIZE]) -> Result<Signature> {
+ let mut signature = [0u8; SIGNATURE_SIZE];
+ // SAFETY: The function writes to the `signature` within the given bounds, and only reads the
+ // message and the private key. The first argument context is not used in this function.
+ check_result(unsafe {
+ DiceSign(
+ ptr::null_mut(), // context
+ message.as_ptr(),
+ message.len(),
+ private_key.as_ptr(),
+ signature.as_mut_ptr(),
+ )
+ })?;
+ Ok(signature)
+}
+
+/// Verifies the `signature` of the `message` with the given `public_key` using `DiceVerify`.
+pub fn verify(message: &[u8], signature: &Signature, public_key: &PublicKey) -> Result<()> {
+ // SAFETY: only reads the messages, signature and public key as constant values.
+ // The first argument context is not used in this function.
+ check_result(unsafe {
+ DiceVerify(
+ ptr::null_mut(), // context
+ message.as_ptr(),
+ message.len(),
+ signature.as_ptr(),
+ public_key.as_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
index 648fd91..76a214c 100644
--- a/diced/open_dice/src/retry.rs
+++ b/diced/open_dice/src/retry.rs
@@ -17,10 +17,40 @@
//! memory allocation on heap, currently we only expose these functions in
//! std environment.
-use crate::bcc::bcc_format_config_descriptor;
+use crate::bcc::{bcc_format_config_descriptor, bcc_main_flow};
+use crate::dice::{
+ dice_main_flow, Cdi, CdiValues, DiceArtifacts, InputValues, CDI_SIZE, 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.
+#[derive(Debug)]
+pub struct OwnedDiceArtifacts {
+ /// CDI Values.
+ cdi_values: CdiValues,
+ /// Boot Certificate Chain.
+ bcc: Vec<u8>,
+}
+
+impl DiceArtifacts for OwnedDiceArtifacts {
+ fn cdi_attest(&self) -> &[u8; CDI_SIZE] {
+ &self.cdi_values.cdi_attest
+ }
+
+ fn cdi_seal(&self) -> &[u8; CDI_SIZE] {
+ &self.cdi_values.cdi_seal
+ }
+
+ fn bcc(&self) -> Option<&[u8]> {
+ Some(&self.bcc)
+ }
+}
+
/// Retries the given function with bigger output buffer size.
fn retry_with_bigger_buffer<F>(mut f: F) -> Result<Vec<u8>>
where
@@ -62,3 +92,69 @@
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
index 5b36f8d..a47265b 100644
--- a/diced/open_dice/tests/api_test.rs
+++ b/diced/open_dice/tests/api_test.rs
@@ -14,20 +14,33 @@
* limitations under the License.
*/
-use diced_open_dice::{derive_cdi_certificate_id, hash, ID_SIZE};
+use diced_open_dice::{
+ derive_cdi_certificate_id, derive_cdi_private_key_seed, hash, kdf, keypair_from_seed, sign,
+ verify, CDI_SIZE, HASH_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
+};
#[test]
fn hash_succeeds() {
- assert_eq!(
- hash(b"hello world").expect("hash failed"),
- [
- 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
- ]
- );
+ 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]
@@ -38,3 +51,57 @@
];
assert_eq!(EXPECTED_ID, derive_cdi_certificate_id(b"MyPubKey").unwrap());
}
+
+const EXPECTED_SEED: &[u8] = &[
+ 0xfa, 0x3c, 0x2f, 0x58, 0x37, 0xf5, 0x8e, 0x96, 0x16, 0x09, 0xf5, 0x22, 0xa1, 0xf1, 0xba, 0xaa,
+ 0x19, 0x95, 0x01, 0x79, 0x2e, 0x60, 0x56, 0xaf, 0xf6, 0x41, 0xe7, 0xff, 0x48, 0xf5, 0x3a, 0x08,
+ 0x84, 0x8a, 0x98, 0x85, 0x6d, 0xf5, 0x69, 0x21, 0x03, 0xcd, 0x09, 0xc3, 0x28, 0xd6, 0x06, 0xa7,
+ 0x57, 0xbd, 0x48, 0x4b, 0x0f, 0x79, 0x0f, 0xf8, 0x2f, 0xf0, 0x0a, 0x41, 0x94, 0xd8, 0x8c, 0xa8,
+];
+
+const EXPECTED_CDI_ATTEST: &[u8] = &[
+ 0xfa, 0x3c, 0x2f, 0x58, 0x37, 0xf5, 0x8e, 0x96, 0x16, 0x09, 0xf5, 0x22, 0xa1, 0xf1, 0xba, 0xaa,
+ 0x19, 0x95, 0x01, 0x79, 0x2e, 0x60, 0x56, 0xaf, 0xf6, 0x41, 0xe7, 0xff, 0x48, 0xf5, 0x3a, 0x08,
+];
+
+const EXPECTED_CDI_PRIVATE_KEY_SEED: &[u8] = &[
+ 0x5f, 0xcc, 0x8e, 0x1a, 0xd1, 0xc2, 0xb3, 0xe9, 0xfb, 0xe1, 0x68, 0xf0, 0xf6, 0x98, 0xfe, 0x0d,
+ 0xee, 0xd4, 0xb5, 0x18, 0xcb, 0x59, 0x70, 0x2d, 0xee, 0x06, 0xe5, 0x70, 0xf1, 0x72, 0x02, 0x6e,
+];
+
+const EXPECTED_PUB_KEY: &[u8] = &[
+ 0x47, 0x42, 0x4b, 0xbd, 0xd7, 0x23, 0xb4, 0xcd, 0xca, 0xe2, 0x8e, 0xdc, 0x6b, 0xfc, 0x23, 0xc9,
+ 0x21, 0x5c, 0x48, 0x21, 0x47, 0xee, 0x5b, 0xfa, 0xaf, 0x88, 0x9a, 0x52, 0xf1, 0x61, 0x06, 0x37,
+];
+const EXPECTED_PRIV_KEY: &[u8] = &[
+ 0x5f, 0xcc, 0x8e, 0x1a, 0xd1, 0xc2, 0xb3, 0xe9, 0xfb, 0xe1, 0x68, 0xf0, 0xf6, 0x98, 0xfe, 0x0d,
+ 0xee, 0xd4, 0xb5, 0x18, 0xcb, 0x59, 0x70, 0x2d, 0xee, 0x06, 0xe5, 0x70, 0xf1, 0x72, 0x02, 0x6e,
+ 0x47, 0x42, 0x4b, 0xbd, 0xd7, 0x23, 0xb4, 0xcd, 0xca, 0xe2, 0x8e, 0xdc, 0x6b, 0xfc, 0x23, 0xc9,
+ 0x21, 0x5c, 0x48, 0x21, 0x47, 0xee, 0x5b, 0xfa, 0xaf, 0x88, 0x9a, 0x52, 0xf1, 0x61, 0x06, 0x37,
+];
+
+const EXPECTED_SIGNATURE: &[u8] = &[
+ 0x44, 0xae, 0xcc, 0xe2, 0xb9, 0x96, 0x18, 0x39, 0x0e, 0x61, 0x0f, 0x53, 0x07, 0xbf, 0xf2, 0x32,
+ 0x3d, 0x44, 0xd4, 0xf2, 0x07, 0x23, 0x30, 0x85, 0x32, 0x18, 0xd2, 0x69, 0xb8, 0x29, 0x3c, 0x26,
+ 0xe6, 0x0d, 0x9c, 0xa5, 0xc2, 0x73, 0xcd, 0x8c, 0xb8, 0x3c, 0x3e, 0x5b, 0xfd, 0x62, 0x8d, 0xf6,
+ 0xc4, 0x27, 0xa6, 0xe9, 0x11, 0x06, 0x5a, 0xb2, 0x2b, 0x64, 0xf7, 0xfc, 0xbb, 0xab, 0x4a, 0x0e,
+];
+
+#[test]
+fn hash_derive_sign_verify() {
+ let seed = hash(b"MySeedString").unwrap();
+ assert_eq!(seed, EXPECTED_SEED);
+ let cdi_attest = &seed[..CDI_SIZE];
+ assert_eq!(cdi_attest, EXPECTED_CDI_ATTEST);
+ let cdi_private_key_seed = derive_cdi_private_key_seed(cdi_attest.try_into().unwrap()).unwrap();
+ assert_eq!(cdi_private_key_seed.as_array(), EXPECTED_CDI_PRIVATE_KEY_SEED);
+ let (pub_key, priv_key) = keypair_from_seed(cdi_private_key_seed.as_array()).unwrap();
+ assert_eq!(&pub_key, EXPECTED_PUB_KEY);
+ assert_eq!(priv_key.as_array(), EXPECTED_PRIV_KEY);
+ let mut signature = sign(b"MyMessage", priv_key.as_array()).unwrap();
+ assert_eq!(&signature, EXPECTED_SIGNATURE);
+ assert!(verify(b"MyMessage", &signature, &pub_key).is_ok());
+ assert!(verify(b"MyMessage_fail", &signature, &pub_key).is_err());
+ signature[0] += 1;
+ assert!(verify(b"MyMessage", &signature, &pub_key).is_err());
+}
diff --git a/diced/open_dice_cbor/Android.bp b/diced/open_dice_cbor/Android.bp
index 61d4d50..a2534dc 100644
--- a/diced/open_dice_cbor/Android.bp
+++ b/diced/open_dice_cbor/Android.bp
@@ -16,29 +16,6 @@
default_applicable_licenses: ["system_security_license"],
}
-rust_library {
- name: "libdiced_open_dice_cbor",
- crate_name: "diced_open_dice_cbor",
- srcs: ["lib.rs"],
-
- rustlibs: [
- "libdiced_open_dice",
- // For ZVec
- "libkeystore2_crypto_rust",
- "libopen_dice_bcc_bindgen",
- "libopen_dice_cbor_bindgen",
- ],
- static_libs: [
- "libopen_dice_bcc",
- "libopen_dice_cbor",
- ],
- vendor_available: true,
- apex_available: [
- "//apex_available:platform",
- "com.android.virt",
- ],
-}
-
rust_test {
name: "diced_open_dice_cbor_test",
crate_name: "diced_open_dice_cbor_test",
@@ -48,13 +25,5 @@
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 7b098a8..1650d93 100644
--- a/diced/open_dice_cbor/lib.rs
+++ b/diced/open_dice_cbor/lib.rs
@@ -12,501 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! Implements safe wrappers around the public API of libopen-dice.
-//! ## Example:
-//! ```
-//! use diced_open_dice_cbor as dice;
-//!
-//! 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::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)?;
-//! ```
-
-pub use diced_open_dice::{
- check_result, derive_cdi_private_key_seed, hash, retry_bcc_format_config_descriptor, Config,
- DiceError, Hash, Hidden, InputValues, Result, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE,
- PRIVATE_KEY_SEED_SIZE,
-};
-use keystore2_crypto::ZVec;
-use open_dice_bcc_bindgen::BccMainFlow;
-pub use open_dice_cbor_bindgen::DiceMode;
-use open_dice_cbor_bindgen::{
- DiceGenerateCertificate, DiceKdf, DiceKeypairFromSeed, DiceMainFlow, DiceSign, DiceVerify,
- DICE_PRIVATE_KEY_SIZE, DICE_PUBLIC_KEY_SIZE, DICE_SIGNATURE_SIZE,
-};
-use std::ffi::c_void;
-
-/// The size of a private key.
-pub const PRIVATE_KEY_SIZE: usize = DICE_PRIVATE_KEY_SIZE as usize;
-/// The size of a public key.
-pub const PUBLIC_KEY_SIZE: usize = DICE_PUBLIC_KEY_SIZE as usize;
-/// The size of a signature.
-pub const SIGNATURE_SIZE: usize = DICE_SIGNATURE_SIZE as usize;
-
-/// 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.
-/// TODO(b/267575445): Remove this method once we migrate all its callers to
-/// `retry_with_bigger_buffer` in `diced_open_dice`.
-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(DiceError::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.
-pub trait Context {
- /// # Safety
- /// The return value of get_context is passed to any open dice function.
- /// Implementations must explain why the context pointer returned is safe
- /// to be used by the open dice library.
- unsafe fn get_context(&mut self) -> *mut c_void;
-}
-
-impl<T: Context + Send> ContextImpl for T {}
-
-/// This represents a context for the open dice library. The wrapped open dice instance, which
-/// is based on boringssl and cbor, does not use a context, so that this type is empty.
-#[derive(Default)]
-pub struct OpenDiceCborContext();
-
-impl OpenDiceCborContext {
- /// Construct a new instance of OpenDiceCborContext.
- pub fn new() -> Self {
- Default::default()
- }
-}
-
-impl Context for OpenDiceCborContext {
- unsafe fn get_context(&mut self) -> *mut c_void {
- // # Safety
- // The open dice cbor implementation does not use a context. It is safe
- // to return NULL.
- std::ptr::null_mut()
- }
-}
-
-/// Type alias for ZVec indicating that it holds a CDI_ATTEST secret.
-pub type CdiAttest = ZVec;
-
-/// Type alias for ZVec indicating that it holds a CDI_SEAL secret.
-pub type CdiSeal = ZVec;
-
-/// Type alias for Vec<u8> indicating that it hold a DICE certificate.
-pub type Cert = Vec<u8>;
-
-/// 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 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(
- &mut self,
- current_cdi_attest: &[u8; CDI_SIZE],
- current_cdi_seal: &[u8; CDI_SIZE],
- input_values: &InputValues,
- ) -> 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.
- // * 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.
- 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.as_ptr(),
- 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 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)> {
- let mut private_key = ZVec::new(PRIVATE_KEY_SIZE)?;
- let mut public_key = vec![0u8; PUBLIC_KEY_SIZE];
-
- // SAFETY:
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument is a pointer to a const buffer of size `PRIVATE_KEY_SEED_SIZE`
- // fulfilled by the definition of the argument.
- // * The third argument and the fourth argument are mutable buffers of size
- // `PRIVATE_KEY_SIZE` and `PUBLIC_KEY_SIZE` respectively. This is fulfilled by the
- // allocations above.
- // * All pointers must be valid for the duration of the function call but not beyond.
- check_result(unsafe {
- DiceKeypairFromSeed(
- self.get_context(),
- seed.as_ptr(),
- public_key.as_mut_ptr(),
- private_key.as_mut_ptr(),
- )
- })?;
- Ok((public_key, private_key))
- }
-
- /// Safe wrapper around open-dice DiceSign, see open dice
- /// documentation for details.
- fn sign(&mut self, message: &[u8], private_key: &[u8; PRIVATE_KEY_SIZE]) -> Result<Vec<u8>> {
- let mut signature = vec![0u8; SIGNATURE_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
- // message buffer.
- // * The fourth argument is a const buffer of size `PRIVATE_KEY_SIZE`. This is fulfilled
- // by the definition of `private key`.
- // * The fifth argument is mutable buffer of size `SIGNATURE_SIZE`. This is fulfilled
- // by the allocation above.
- // * All pointers must be valid for the duration of the function call but not beyond.
- check_result(unsafe {
- DiceSign(
- self.get_context(),
- message.as_ptr(),
- message.len(),
- private_key.as_ptr(),
- signature.as_mut_ptr(),
- )
- })?;
- Ok(signature)
- }
-
- /// Safe wrapper around open-dice DiceVerify, see open dice
- /// documentation for details.
- fn verify(
- &mut self,
- message: &[u8],
- signature: &[u8; SIGNATURE_SIZE],
- public_key: &[u8; PUBLIC_KEY_SIZE],
- ) -> Result<()> {
- // 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
- // message buffer.
- // * The fourth argument is a const buffer of size `SIGNATURE_SIZE`. This is fulfilled
- // by the definition of `signature`.
- // * The fifth argument is a const buffer of size `PUBLIC_KEY_SIZE`. This is fulfilled
- // by the definition of `public_key`.
- // * All pointers must be valid for the duration of the function call but not beyond.
- check_result(unsafe {
- DiceVerify(
- self.get_context(),
- message.as_ptr(),
- message.len(),
- signature.as_ptr(),
- public_key.as_ptr(),
- )
- })
- }
-
- /// Safe wrapper around open-dice DiceGenerateCertificate, see open dice
- /// documentation for details.
- fn generate_certificate(
- &mut self,
- subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
- authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
- input_values: &InputValues,
- ) -> Result<Vec<u8>> {
- // SAFETY (DiceGenerateCertificate):
- // * 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.
- // * 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.
- 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.as_ptr(),
- 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(
- &mut self,
- current_cdi_attest: &[u8; CDI_SIZE],
- current_cdi_seal: &[u8; CDI_SIZE],
- bcc: &[u8],
- input_values: &InputValues,
- ) -> 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.
- // * 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.
- 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.as_ptr(),
- 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))
- }
-}
-
+// TODO(b/267575445): Move the tests to other target and remove the folder `open_dice_cbor` completely.
#[cfg(test)]
mod test {
- use super::*;
+ use diced_open_dice::DiceArtifacts;
use diced_sample_inputs::make_sample_bcc_and_cdis;
- use std::convert::TryInto;
-
- static SEED_TEST_VECTOR: &[u8] = &[
- 0xfa, 0x3c, 0x2f, 0x58, 0x37, 0xf5, 0x8e, 0x96, 0x16, 0x09, 0xf5, 0x22, 0xa1, 0xf1, 0xba,
- 0xaa, 0x19, 0x95, 0x01, 0x79, 0x2e, 0x60, 0x56, 0xaf, 0xf6, 0x41, 0xe7, 0xff, 0x48, 0xf5,
- 0x3a, 0x08, 0x84, 0x8a, 0x98, 0x85, 0x6d, 0xf5, 0x69, 0x21, 0x03, 0xcd, 0x09, 0xc3, 0x28,
- 0xd6, 0x06, 0xa7, 0x57, 0xbd, 0x48, 0x4b, 0x0f, 0x79, 0x0f, 0xf8, 0x2f, 0xf0, 0x0a, 0x41,
- 0x94, 0xd8, 0x8c, 0xa8,
- ];
-
- static CDI_ATTEST_TEST_VECTOR: &[u8] = &[
- 0xfa, 0x3c, 0x2f, 0x58, 0x37, 0xf5, 0x8e, 0x96, 0x16, 0x09, 0xf5, 0x22, 0xa1, 0xf1, 0xba,
- 0xaa, 0x19, 0x95, 0x01, 0x79, 0x2e, 0x60, 0x56, 0xaf, 0xf6, 0x41, 0xe7, 0xff, 0x48, 0xf5,
- 0x3a, 0x08,
- ];
- static CDI_PRIVATE_KEY_SEED_TEST_VECTOR: &[u8] = &[
- 0x5f, 0xcc, 0x8e, 0x1a, 0xd1, 0xc2, 0xb3, 0xe9, 0xfb, 0xe1, 0x68, 0xf0, 0xf6, 0x98, 0xfe,
- 0x0d, 0xee, 0xd4, 0xb5, 0x18, 0xcb, 0x59, 0x70, 0x2d, 0xee, 0x06, 0xe5, 0x70, 0xf1, 0x72,
- 0x02, 0x6e,
- ];
-
- static PUB_KEY_TEST_VECTOR: &[u8] = &[
- 0x47, 0x42, 0x4b, 0xbd, 0xd7, 0x23, 0xb4, 0xcd, 0xca, 0xe2, 0x8e, 0xdc, 0x6b, 0xfc, 0x23,
- 0xc9, 0x21, 0x5c, 0x48, 0x21, 0x47, 0xee, 0x5b, 0xfa, 0xaf, 0x88, 0x9a, 0x52, 0xf1, 0x61,
- 0x06, 0x37,
- ];
- static PRIV_KEY_TEST_VECTOR: &[u8] = &[
- 0x5f, 0xcc, 0x8e, 0x1a, 0xd1, 0xc2, 0xb3, 0xe9, 0xfb, 0xe1, 0x68, 0xf0, 0xf6, 0x98, 0xfe,
- 0x0d, 0xee, 0xd4, 0xb5, 0x18, 0xcb, 0x59, 0x70, 0x2d, 0xee, 0x06, 0xe5, 0x70, 0xf1, 0x72,
- 0x02, 0x6e, 0x47, 0x42, 0x4b, 0xbd, 0xd7, 0x23, 0xb4, 0xcd, 0xca, 0xe2, 0x8e, 0xdc, 0x6b,
- 0xfc, 0x23, 0xc9, 0x21, 0x5c, 0x48, 0x21, 0x47, 0xee, 0x5b, 0xfa, 0xaf, 0x88, 0x9a, 0x52,
- 0xf1, 0x61, 0x06, 0x37,
- ];
-
- static SIGNATURE_TEST_VECTOR: &[u8] = &[
- 0x44, 0xae, 0xcc, 0xe2, 0xb9, 0x96, 0x18, 0x39, 0x0e, 0x61, 0x0f, 0x53, 0x07, 0xbf, 0xf2,
- 0x32, 0x3d, 0x44, 0xd4, 0xf2, 0x07, 0x23, 0x30, 0x85, 0x32, 0x18, 0xd2, 0x69, 0xb8, 0x29,
- 0x3c, 0x26, 0xe6, 0x0d, 0x9c, 0xa5, 0xc2, 0x73, 0xcd, 0x8c, 0xb8, 0x3c, 0x3e, 0x5b, 0xfd,
- 0x62, 0x8d, 0xf6, 0xc4, 0x27, 0xa6, 0xe9, 0x11, 0x06, 0x5a, 0xb2, 0x2b, 0x64, 0xf7, 0xfc,
- 0xbb, 0xab, 0x4a, 0x0e,
- ];
-
- #[test]
- fn hash_derive_sign_verify() {
- let mut ctx = OpenDiceCborContext::new();
- 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 =
- 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();
- assert_eq!(&pub_key, PUB_KEY_TEST_VECTOR);
- assert_eq!(&priv_key[..], PRIV_KEY_TEST_VECTOR);
- let mut signature =
- ctx.sign("MyMessage".as_bytes(), priv_key[..].try_into().unwrap()).unwrap();
- assert_eq!(&signature, SIGNATURE_TEST_VECTOR);
- assert!(ctx
- .verify(
- "MyMessage".as_bytes(),
- signature[..].try_into().unwrap(),
- pub_key[..].try_into().unwrap()
- )
- .is_ok());
- assert!(ctx
- .verify(
- "MyMessage_fail".as_bytes(),
- signature[..].try_into().unwrap(),
- pub_key[..].try_into().unwrap()
- )
- .is_err());
- signature[0] += 1;
- assert!(ctx
- .verify(
- "MyMessage".as_bytes(),
- signature[..].try_into().unwrap(),
- pub_key[..].try_into().unwrap()
- )
- .is_err());
- }
static SAMPLE_CDI_ATTEST_TEST_VECTOR: &[u8] = &[
0x3e, 0x57, 0x65, 0x5d, 0x48, 0x02, 0xbd, 0x5c, 0x66, 0xcc, 0x1f, 0x0f, 0xbe, 0x5e, 0x32,
@@ -627,29 +137,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);
+ let dice_artifacts = make_sample_bcc_and_cdis().unwrap();
+ assert_eq!(dice_artifacts.cdi_attest(), SAMPLE_CDI_ATTEST_TEST_VECTOR);
+ assert_eq!(dice_artifacts.cdi_seal(), SAMPLE_CDI_SEAL_TEST_VECTOR);
+ assert_eq!(dice_artifacts.bcc(), Some(SAMPLE_BCC_TEST_VECTOR));
}
}
diff --git a/diced/src/diced_client_test.rs b/diced/src/diced_client_test.rs
index 054b2d1..7957f0a 100644
--- a/diced/src/diced_client_test.rs
+++ b/diced/src/diced_client_test.rs
@@ -19,7 +19,8 @@
use android_security_dice::aidl::android::security::dice::IDiceMaintenance::IDiceMaintenance;
use android_security_dice::aidl::android::security::dice::IDiceNode::IDiceNode;
use binder::Strong;
-use diced_open_dice_cbor as dice;
+use diced_open_dice as dice;
+use diced_open_dice::DiceArtifacts;
use nix::libc::uid_t;
use std::convert::TryInto;
use std::ffi::CString;
@@ -54,11 +55,10 @@
.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(),
- cdi_seal[..].try_into().unwrap(),
- &bcc,
+ artifacts.cdi_attest(),
+ artifacts.cdi_seal(),
+ artifacts.bcc().expect("bcc is none"),
)
.unwrap();
// TODO when we have a parser/verifier, check equivalence rather
@@ -94,11 +94,10 @@
.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(),
- cdi_seal[..].try_into().unwrap(),
- &bcc,
+ artifacts.cdi_attest(),
+ artifacts.cdi_seal(),
+ artifacts.bcc().expect("bcc is none"),
)
.unwrap();
// TODO b/204938506 when we have a parser/verifier, check equivalence rather
@@ -156,11 +155,10 @@
let client = [client];
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(),
- cdi_seal[..].try_into().unwrap(),
- &bcc,
+ artifacts.cdi_attest(),
+ artifacts.cdi_seal(),
+ artifacts.bcc().expect("bcc is none"),
)
.unwrap();
// TODO b/204938506 when we have a parser/verifier, check equivalence rather
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 1efee8b..33aeb2b 100644
--- a/diced/src/hal_node.rs
+++ b/diced/src/hal_node.rs
@@ -34,8 +34,8 @@
};
use anyhow::{Context, Result};
use binder::{BinderFeatures, Result as BinderResult, Strong};
-use dice::{ContextImpl, OpenDiceCborContext};
-use diced_open_dice_cbor as dice;
+use diced_open_dice as dice;
+pub use diced_open_dice::DiceArtifacts;
use diced_utils as utils;
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::{
@@ -47,7 +47,7 @@
use std::os::unix::io::RawFd;
use std::sync::{Arc, RwLock};
use utils::ResidentArtifacts;
-pub use utils::{DiceArtifacts, UpdatableDiceArtifacts};
+pub use utils::UpdatableDiceArtifacts;
/// PipeReader is a simple wrapper around raw pipe file descriptors.
/// It takes ownership of the file descriptor and closes it on drop. It provides `read_all`, which
@@ -202,7 +202,6 @@
let signature: Vec<u8> = self
.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(|| {
format!(
@@ -212,24 +211,11 @@
})?,
)
.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!(
- "In ResidentHal::sign: Failed to convert seed (length: {}).",
- seed.len()
- )
- })?)
+ let (_public_key, private_key) = dice::keypair_from_seed(seed.as_array())
.context("In ResidentHal::sign: Failed to derive keypair from seed.")?;
- dice.sign(
- message,
- private_key[..].try_into().with_context(|| {
- format!(
- "In ResidentHal::sign: Failed to convert private_key (length: {}).",
- private_key.len()
- )
- })?,
- )
- .context("In ResidentHal::sign: Failed to sign.")
+ let signature = dice::sign(message, private_key.as_array())
+ .context("In ResidentHal::sign: Failed to sign.")?;
+ Ok(signature.to_vec())
})
.context("In ResidentHal::sign:")?;
Ok(Signature { data: signature })
@@ -330,8 +316,8 @@
BccHandover::BccHandover, Config::Config as BinderConfig,
InputValues::InputValues as BinderInputValues, Mode::Mode as BinderMode,
};
- use anyhow::{Context, Result};
- use diced_open_dice_cbor as dice;
+ use anyhow::{anyhow, Context, Result};
+ use diced_open_dice as dice;
use diced_sample_inputs;
use diced_utils as utils;
use std::ffi::CStr;
@@ -343,6 +329,16 @@
bcc: Vec<u8>,
}
+ impl From<dice::OwnedDiceArtifacts> for InsecureSerializableArtifacts {
+ fn from(dice_artifacts: dice::OwnedDiceArtifacts) -> Self {
+ let mut cdi_attest = [0u8; dice::CDI_SIZE];
+ cdi_attest.copy_from_slice(dice_artifacts.cdi_attest());
+ let mut cdi_seal = [0u8; dice::CDI_SIZE];
+ cdi_seal.copy_from_slice(dice_artifacts.cdi_seal());
+ Self { cdi_attest, cdi_seal, bcc: dice_artifacts.bcc().expect("bcc is none").to_vec() }
+ }
+ }
+
impl DiceArtifacts for InsecureSerializableArtifacts {
fn cdi_attest(&self) -> &[u8; dice::CDI_SIZE] {
&self.cdi_attest
@@ -350,8 +346,8 @@
fn cdi_seal(&self) -> &[u8; dice::CDI_SIZE] {
&self.cdi_seal
}
- fn bcc(&self) -> Vec<u8> {
- self.bcc.clone()
+ fn bcc(&self) -> Option<&[u8]> {
+ Some(&self.bcc)
}
}
@@ -366,7 +362,7 @@
Ok(Self {
cdi_attest: *new_artifacts.cdi_attest(),
cdi_seal: *new_artifacts.cdi_seal(),
- bcc: new_artifacts.bcc(),
+ bcc: new_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?.to_vec(),
})
}
}
@@ -394,10 +390,8 @@
/// 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(
@@ -421,7 +415,7 @@
let result = utils::make_bcc_handover(
new_artifacts.cdi_attest(),
new_artifacts.cdi_seal(),
- &new_artifacts.bcc(),
+ new_artifacts.bcc().unwrap(),
)?;
assert_eq!(result, make_derive_test_vector());
@@ -433,18 +427,13 @@
/// 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(&[
@@ -474,18 +463,13 @@
/// 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(&[
diff --git a/diced/src/lib.rs b/diced/src/lib.rs
deleted file mode 100644
index 9e70ace..0000000
--- a/diced/src/lib.rs
+++ /dev/null
@@ -1,205 +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::ffi::CString;
-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> {
- let desc = CString::new(format!("{}", uid)).unwrap();
- Ok(BinderInputValues {
- codeHash: [0; dice::HASH_SIZE],
- config: BinderConfig {
- desc: dice::retry_bcc_format_config_descriptor(Some(desc.as_c_str()), None, true)
- .unwrap(),
- },
- 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/lib_vendor.rs b/diced/src/lib_vendor.rs
index 01c804b..a04bb80 100644
--- a/diced/src/lib_vendor.rs
+++ b/diced/src/lib_vendor.rs
@@ -17,4 +17,4 @@
mod error_vendor;
pub mod hal_node;
-pub use diced_open_dice_cbor as dice;
+pub use diced_open_dice as dice;
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 d65f30c..0000000
--- a/diced/src/resident_node.rs
+++ /dev/null
@@ -1,178 +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, 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 = demotion_db
- .get(&client_arr[0])
- .map(|v| v.iter())
- .unwrap_or_else(|| client_arr.iter())
- .chain(input_values.iter());
- artifacts.execute_steps(input_values).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();
-
- *artifacts = artifacts
- .try_clone()
- .context("In ResidentNode::demote_self: Failed to clone resident artifacts")?
- .execute_steps(input_values)
- .context("In ResidentNode::demote_self:")?;
- Ok(())
- }
-}
diff --git a/diced/src/sample_inputs.rs b/diced/src/sample_inputs.rs
index 5b61fed..6ad8ca9 100644
--- a/diced/src/sample_inputs.rs
+++ b/diced/src/sample_inputs.rs
@@ -18,12 +18,10 @@
use android_hardware_security_dice::aidl::android::hardware::security::dice::{
Config::Config as BinderConfig, InputValues::InputValues as BinderInputValues, Mode::Mode,
};
-use anyhow::{Context, Result};
-use dice::ContextImpl;
-use diced_open_dice_cbor as dice;
+use anyhow::{anyhow, Context, Result};
+use diced_open_dice as dice;
+use diced_open_dice::DiceArtifacts;
use diced_utils::{cbor, to_dice_input_values};
-use keystore2_crypto::ZVec;
-use std::convert::TryInto;
use std::ffi::CStr;
use std::io::Write;
@@ -66,23 +64,18 @@
/// 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>)> {
- let mut dice_ctx = dice::OpenDiceCborContext::new();
+pub fn make_sample_bcc_and_cdis() -> Result<dice::OwnedDiceArtifacts> {
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, _) =
- dice_ctx
- .keypair_from_seed(&private_key_seed[..].try_into().context(
- "In make_sample_bcc_and_cids: Failed to convert seed to array reference.",
- )?)
- .context("In make_sample_bcc_and_cids: Failed to generate key pair.")?;
+ let (public_key, _) = dice::keypair_from_seed(private_key_seed.as_array())
+ .context("In make_sample_bcc_and_cids: Failed to generate key pair.")?;
let input_values_vector = get_input_values_vector();
- let (cdi_attest, cdi_seal, mut cert) = dice_ctx
- .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 (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![];
@@ -93,30 +86,20 @@
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,
- &to_dice_input_values(&input_values_vector[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,
- &to_dice_input_values(&input_values_vector[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_attest(),
+ dice_artifacts.cdi_seal(),
+ dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?,
+ &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(
diff --git a/diced/src/utils.rs b/diced/src/utils.rs
index 3d35ae5..2789a60 100644
--- a/diced/src/utils.rs
+++ b/diced/src/utils.rs
@@ -18,9 +18,9 @@
Bcc::Bcc, BccHandover::BccHandover, InputValues::InputValues as BinderInputValues,
Mode::Mode as BinderMode,
};
-use anyhow::{Context, Result};
-use dice::{ContextImpl, DiceMode};
-use diced_open_dice_cbor as dice;
+use anyhow::{anyhow, Context, Result};
+use diced_open_dice as dice;
+use diced_open_dice::DiceArtifacts;
use keystore2_crypto::ZVec;
use std::convert::TryInto;
@@ -38,13 +38,13 @@
)
}
-fn to_dice_mode(binder_mode: BinderMode) -> DiceMode {
+fn to_dice_mode(binder_mode: BinderMode) -> dice::DiceMode {
match binder_mode {
- BinderMode::NOT_INITIALIZED => DiceMode::kDiceModeNotInitialized,
- BinderMode::NORMAL => DiceMode::kDiceModeNormal,
- BinderMode::DEBUG => DiceMode::kDiceModeDebug,
- BinderMode::RECOVERY => DiceMode::kDiceModeMaintenance,
- _ => DiceMode::kDiceModeNotInitialized,
+ BinderMode::NOT_INITIALIZED => dice::DiceMode::kDiceModeNotInitialized,
+ BinderMode::NORMAL => dice::DiceMode::kDiceModeNormal,
+ BinderMode::DEBUG => dice::DiceMode::kDiceModeDebug,
+ BinderMode::RECOVERY => dice::DiceMode::kDiceModeMaintenance,
+ _ => dice::DiceMode::kDiceModeNotInitialized,
}
}
@@ -62,12 +62,25 @@
/// and the BCC formatted attestation certificate chain. The sensitive secrets are
/// stored in zeroing vectors, and it implements functionality to perform DICE
/// derivation steps using libopen-dice-cbor.
+/// TODO(b/268322533): Remove this struct with the unused HAL service dice
pub struct ResidentArtifacts {
cdi_attest: ZVec,
cdi_seal: ZVec,
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_attest().to_vec().try_into()?,
+ cdi_seal: dice_artifacts.cdi_seal().to_vec().try_into()?,
+ bcc: dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?.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
@@ -93,9 +106,9 @@
/// because DiceArtifacts returns array references of appropriate size.
pub fn new_from<T: DiceArtifacts + ?Sized>(artifacts: &T) -> Result<Self> {
Ok(ResidentArtifacts {
- cdi_attest: artifacts.cdi_attest()[..].try_into()?,
- cdi_seal: artifacts.cdi_seal()[..].try_into()?,
- bcc: artifacts.bcc(),
+ cdi_attest: artifacts.cdi_attest().to_vec().try_into()?,
+ cdi_seal: artifacts.cdi_seal().to_vec().try_into()?,
+ bcc: artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?.to_vec(),
})
}
@@ -125,19 +138,18 @@
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,
- &to_dice_input_values(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
@@ -153,20 +165,6 @@
}
}
-/// An object that implements this trait provides the typical DICE artifacts.
-/// CDI_ATTEST, CDI_SEAL, and a certificate chain up to the public key that
-/// can be derived from CDI_ATTEST. Implementations should check the length of
-/// the stored CDI_* secrets on creation so that any valid instance returns the
-/// correct secrets in an infallible way.
-pub trait DiceArtifacts {
- /// Returns CDI_ATTEST.
- fn cdi_attest(&self) -> &[u8; dice::CDI_SIZE];
- /// Returns CDI_SEAL.
- fn cdi_seal(&self) -> &[u8; dice::CDI_SIZE];
- /// Returns the attestation certificate chain in BCC format.
- fn bcc(&self) -> Vec<u8>;
-}
-
/// Implement this trait to provide read and write access to a secure artifact
/// storage that can be used by the ResidentHal implementation.
pub trait UpdatableDiceArtifacts {
@@ -189,8 +187,8 @@
fn cdi_seal(&self) -> &[u8; dice::CDI_SIZE] {
self.cdi_seal[..].try_into().unwrap()
}
- fn bcc(&self) -> Vec<u8> {
- self.bcc.clone()
+ fn bcc(&self) -> Option<&[u8]> {
+ Some(&self.bcc)
}
}
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index fea4df9..e2b3cf4 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -47,7 +47,7 @@
bool useRkpd() {
return android::base::GetBoolProperty("remote_provisioning.enable_rkpd",
- /*default_value=*/false);
+ /*default_value=*/true);
}
} // namespace
diff --git a/keystore2/src/attestation_key_utils.rs b/keystore2/src/attestation_key_utils.rs
index d31fa82..8c4cdea 100644
--- a/keystore2/src/attestation_key_utils.rs
+++ b/keystore2/src/attestation_key_utils.rs
@@ -54,11 +54,9 @@
}
fn use_rkpd() -> bool {
- let mutable_property = "persist.device_config.remote_key_provisioning_native.enable_rkpd";
- let fixed_property = "remote_provisioning.enable_rkpd";
- let default_value = false;
- system_properties::read_bool(mutable_property, default_value).unwrap_or(default_value)
- || system_properties::read_bool(fixed_property, default_value).unwrap_or(default_value)
+ let property = "remote_provisioning.enable_rkpd";
+ let default_value = true;
+ system_properties::read_bool(property, default_value).unwrap_or(default_value)
}
/// This function loads and, optionally, assigns the caller's remote provisioned
diff --git a/keystore2/src/rkpd_client.rs b/keystore2/src/rkpd_client.rs
index f1e8e11..0ea2d39 100644
--- a/keystore2/src/rkpd_client.rs
+++ b/keystore2/src/rkpd_client.rs
@@ -57,8 +57,11 @@
fn send(&self, value: T) {
if let Some(inner) = self.inner.lock().unwrap().take() {
- // assert instead of unwrap, because on failure send returns Err(value)
- assert!(inner.send(value).is_ok(), "thread state is terminally broken");
+ // It's possible for the corresponding receiver to time out and be dropped. In this
+ // case send() will fail. This error is not actionable though, so only log the error.
+ if inner.send(value).is_err() {
+ log::error!("SafeSender::send() failed");
+ }
}
}
}
@@ -202,8 +205,14 @@
.context(ks_err!("Trying to get key."))?;
match timeout(RKPD_TIMEOUT, rx).await {
- Err(e) => Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
- .context(ks_err!("Waiting for RKPD key timed out: {:?}", e)),
+ Err(e) => {
+ // Make a best effort attempt to cancel the timed out request.
+ if let Err(e) = registration.cancelGetKey(&cb) {
+ log::error!("IRegistration::cancelGetKey failed: {:?}", e);
+ }
+ Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
+ .context(ks_err!("Waiting for RKPD key timed out: {:?}", e))
+ }
Ok(v) => v.unwrap(),
}
}
@@ -314,52 +323,68 @@
use keystore2_crypto::parse_subject_from_certificate;
use std::collections::HashMap;
use std::sync::atomic::{AtomicU32, Ordering};
+ use std::sync::{Arc, Mutex};
- #[derive(Default)]
- struct MockRegistration {
+ struct MockRegistrationValues {
key: RemotelyProvisionedKey,
latency: Option<Duration>,
+ thread_join_handles: Vec<Option<std::thread::JoinHandle<()>>>,
}
+ struct MockRegistration(Arc<Mutex<MockRegistrationValues>>);
+
impl MockRegistration {
pub fn new_native_binder(
key: &RemotelyProvisionedKey,
latency: Option<Duration>,
) -> Strong<dyn IRegistration> {
- let result = Self {
+ let result = Self(Arc::new(Mutex::new(MockRegistrationValues {
key: RemotelyProvisionedKey {
keyBlob: key.keyBlob.clone(),
encodedCertChain: key.encodedCertChain.clone(),
},
latency,
- };
+ thread_join_handles: Vec::new(),
+ })));
BnRegistration::new_binder(result, BinderFeatures::default())
}
}
+ impl Drop for MockRegistration {
+ fn drop(&mut self) {
+ let mut values = self.0.lock().unwrap();
+ for handle in values.thread_join_handles.iter_mut() {
+ // These are test threads. So, no need to worry too much about error handling.
+ handle.take().unwrap().join().unwrap();
+ }
+ }
+ }
+
impl Interface for MockRegistration {}
impl IRegistration for MockRegistration {
fn getKey(&self, _: i32, cb: &Strong<dyn IGetKeyCallback>) -> binder::Result<()> {
+ let mut values = self.0.lock().unwrap();
let key = RemotelyProvisionedKey {
- keyBlob: self.key.keyBlob.clone(),
- encodedCertChain: self.key.encodedCertChain.clone(),
+ keyBlob: values.key.keyBlob.clone(),
+ encodedCertChain: values.key.encodedCertChain.clone(),
};
- let latency = self.latency;
+ let latency = values.latency;
let get_key_cb = cb.clone();
// Need a separate thread to trigger timeout in the caller.
- std::thread::spawn(move || {
+ let join_handle = std::thread::spawn(move || {
if let Some(duration) = latency {
std::thread::sleep(duration);
}
get_key_cb.onSuccess(&key).unwrap();
});
+ values.thread_join_handles.push(Some(join_handle));
Ok(())
}
fn cancelGetKey(&self, _: &Strong<dyn IGetKeyCallback>) -> binder::Result<()> {
- todo!()
+ Ok(())
}
fn storeUpgradedKeyAsync(
@@ -370,8 +395,9 @@
) -> binder::Result<()> {
// We are primarily concerned with timing out correctly. Storing the key in this mock
// registration isn't particularly interesting, so skip that part.
+ let values = self.0.lock().unwrap();
let store_cb = cb.clone();
- let latency = self.latency;
+ let latency = values.latency;
std::thread::spawn(move || {
if let Some(duration) = latency {
@@ -528,7 +554,7 @@
fn test_get_mock_key_timeout() {
let mock_key =
RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
- let latency = RKPD_TIMEOUT + Duration::from_secs(10);
+ let latency = RKPD_TIMEOUT + Duration::from_secs(1);
let registration = get_mock_registration(&mock_key, Some(latency)).unwrap();
let result =
@@ -553,7 +579,7 @@
fn test_store_mock_key_timeout() {
let mock_key =
RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
- let latency = RKPD_TIMEOUT + Duration::from_secs(10);
+ let latency = RKPD_TIMEOUT + Duration::from_secs(1);
let registration = get_mock_registration(&mock_key, Some(latency)).unwrap();
let result = tokio_rt().block_on(store_rkpd_attestation_key_with_registration_async(
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index b928fb0..cc1f816 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -612,7 +612,7 @@
})
},
)
- .context("While generating Key with remote provisioned attestation key.")
+ .context(ks_err!("While generating Key with remote provisioned attestation key."))
.map(|(mut result, _)| {
result.certificateChain.push(attestation_certs);
result
@@ -635,7 +635,7 @@
self.keymint.generateKey(¶ms, dynamic_attest_key.as_ref())
})
})
- .context("While generating Key with remote provisioned attestation key.")
+ .context(ks_err!("While generating Key with remote provisioned attestation key."))
.map(|(mut result, _)| {
result.certificateChain.push(attestation_certs);
result
@@ -651,7 +651,7 @@
);
self.keymint.generateKey(¶ms, None)
})
- .context("While generating Key without explicit attestation key."),
+ .context(ks_err!("While generating Key without explicit attestation key.")),
}
.context(ks_err!())?;
@@ -684,7 +684,7 @@
};
// import_key requires the rebind permission.
- check_key_permission(KeyPerm::Rebind, &key, &None).context("In import_key.")?;
+ check_key_permission(KeyPerm::Rebind, &key, &None).context(ks_err!("In import_key."))?;
let params = self
.add_required_parameters(caller_uid, params, &key)
@@ -694,7 +694,7 @@
.iter()
.find(|p| p.tag == Tag::ALGORITHM)
.ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
- .context("No KeyParameter 'Algorithm'.")
+ .context(ks_err!("No KeyParameter 'Algorithm'."))
.and_then(|p| match &p.value {
KeyParameterValue::Algorithm(Algorithm::AES)
| KeyParameterValue::Algorithm(Algorithm::HMAC)
@@ -780,7 +780,7 @@
)
})
})
- .context("Failed to load wrapping key.")?;
+ .context(ks_err!("Failed to load wrapping key."))?;
let (wrapping_key_blob, wrapping_blob_metadata) =
wrapping_key_entry.take_key_blob_info().ok_or_else(error::Error::sys).context(
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index f43ba5c..1040228 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -205,17 +205,18 @@
let mut db = db.borrow_mut();
if let Some((key_id_guard, _key_entry)) = entry {
db.set_blob(&key_id_guard, SubComponentType::CERT, public_cert, None)
- .context("Failed to update cert subcomponent.")?;
+ .context(ks_err!("Failed to update cert subcomponent."))?;
db.set_blob(&key_id_guard, SubComponentType::CERT_CHAIN, certificate_chain, None)
- .context("Failed to update cert chain subcomponent.")?;
+ .context(ks_err!("Failed to update cert chain subcomponent."))?;
return Ok(());
}
// If we reach this point we have to check the special condition where a certificate
// entry may be made.
if !(public_cert.is_none() && certificate_chain.is_some()) {
- return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND)).context("No key to update.");
+ return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+ .context(ks_err!("No key to update."));
}
// So we know that we have a certificate chain and no public cert.
@@ -230,13 +231,13 @@
(Domain::SELINUX, Some(_)) => key.clone(),
_ => {
return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT))
- .context("Domain must be APP or SELINUX to insert a certificate.")
+ .context(ks_err!("Domain must be APP or SELINUX to insert a certificate."))
}
};
// Security critical: This must return on failure. Do not remove the `?`;
check_key_permission(KeyPerm::Rebind, &key, &None)
- .context("Caller does not have permission to insert this certificate.")?;
+ .context(ks_err!("Caller does not have permission to insert this certificate."))?;
db.store_new_certificate(
&key,
@@ -244,7 +245,7 @@
certificate_chain.unwrap(),
&KEYSTORE_UUID,
)
- .context("Failed to insert new certificate.")?;
+ .context(ks_err!("Failed to insert new certificate."))?;
Ok(())
})
.context(ks_err!())
@@ -295,7 +296,8 @@
DB.with(|db| {
LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || {
db.borrow_mut().unbind_key(key, KeyType::Client, caller_uid, |k, av| {
- check_key_permission(KeyPerm::Delete, k, &av).context("During delete_key.")
+ check_key_permission(KeyPerm::Delete, k, &av)
+ .context(ks_err!("During delete_key."))
})
})
})
diff --git a/keystore2/test_utils/key_generations.rs b/keystore2/test_utils/key_generations.rs
index f9aaabb..e4c4968 100644
--- a/keystore2/test_utils/key_generations.rs
+++ b/keystore2/test_utils/key_generations.rs
@@ -1047,3 +1047,38 @@
transport_key,
)
}
+
+/// Generate EC key with purpose AGREE_KEY.
+pub fn generate_ec_agree_key(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ ec_curve: EcCurve,
+ digest: Digest,
+ domain: Domain,
+ nspace: i64,
+ alias: Option<String>,
+) -> binder::Result<KeyMetadata> {
+ let gen_params = AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::AGREE_KEY)
+ .digest(digest)
+ .ec_curve(ec_curve);
+
+ match sec_level.generateKey(
+ &KeyDescriptor { domain, nspace, alias, blob: None },
+ None,
+ &gen_params,
+ 0,
+ b"entropy",
+ ) {
+ Ok(key_metadata) => {
+ assert!(key_metadata.certificate.is_some());
+ if domain == Domain::BLOB {
+ assert!(key_metadata.key.blob.is_some());
+ }
+
+ Ok(key_metadata)
+ }
+ Err(e) => Err(e),
+ }
+}
diff --git a/keystore2/tests/keystore2_client_key_agreement_tests.rs b/keystore2/tests/keystore2_client_key_agreement_tests.rs
new file mode 100644
index 0000000..6b2e3c2
--- /dev/null
+++ b/keystore2/tests/keystore2_client_key_agreement_tests.rs
@@ -0,0 +1,179 @@
+// 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 openssl::ec::{EcGroup, EcKey};
+use openssl::error::ErrorStack;
+use openssl::nid::Nid;
+use openssl::pkey::{PKey, PKeyRef, Private, Public};
+use openssl::pkey_ctx::PkeyCtx;
+use openssl::x509::X509;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Digest::Digest, EcCurve::EcCurve, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose,
+ SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+ KeyMetadata::KeyMetadata,
+};
+
+use keystore2_test_utils::{
+ authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+/// This macro is used to verify that the key agreement works for the given curve.
+macro_rules! test_ec_key_agree {
+ ( $test_name:ident, $ec_curve:expr ) => {
+ #[test]
+ fn $test_name() {
+ perform_ec_key_agreement($ec_curve);
+ }
+ };
+}
+
+// Get the KeyMint key's public part.
+fn get_keymint_public_key(keymint_key: &KeyMetadata) -> Result<PKey<Public>, ErrorStack> {
+ let cert_bytes = keymint_key.certificate.as_ref().unwrap();
+ let cert = X509::from_der(cert_bytes.as_ref()).unwrap();
+ cert.public_key()
+}
+
+// Perform local ECDH between the two keys and check the derived secrets are the same.
+fn check_agreement(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ keymint_key: &KeyDescriptor,
+ keymint_pub_key: &PKey<Public>,
+ local_key: &PKeyRef<Private>,
+ local_pub_key: &[u8],
+) {
+ let authorizations = authorizations::AuthSetBuilder::new().purpose(KeyPurpose::AGREE_KEY);
+ let key_agree_op = sec_level.createOperation(keymint_key, &authorizations, false).unwrap();
+ assert!(key_agree_op.iOperation.is_some());
+
+ let op = key_agree_op.iOperation.unwrap();
+ let secret = op.finish(Some(local_pub_key), None).unwrap();
+ assert!(secret.is_some());
+
+ let mut ctx = PkeyCtx::new(local_key).unwrap();
+ ctx.derive_init().unwrap();
+ ctx.derive_set_peer(keymint_pub_key).unwrap();
+ let mut peer_secret = vec![];
+ ctx.derive_to_vec(&mut peer_secret).unwrap();
+
+ assert_eq!(secret.unwrap(), peer_secret);
+}
+
+fn ec_curve_to_openrssl_curve_name(ec_curve: &EcCurve) -> Nid {
+ match *ec_curve {
+ EcCurve::P_224 => Nid::SECP224R1,
+ EcCurve::P_256 => Nid::X9_62_PRIME256V1,
+ EcCurve::P_384 => Nid::SECP384R1,
+ EcCurve::P_521 => Nid::SECP521R1,
+ _ => Nid::UNDEF,
+ }
+}
+
+/// Generate two EC keys with given curve from KeyMint and OpeanSSL. Perform local ECDH between
+/// them and verify that the derived secrets are the same.
+fn perform_ec_key_agreement(ec_curve: EcCurve) {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let openssl_ec_curve = ec_curve_to_openrssl_curve_name(&ec_curve);
+
+ let alias = format!("ks_ec_test_key_agree_{}", getuid());
+ let keymint_key = key_generations::generate_ec_agree_key(
+ &sec_level,
+ ec_curve,
+ Digest::SHA_2_256,
+ Domain::APP,
+ -1,
+ Some(alias),
+ )
+ .unwrap();
+
+ let keymint_pub_key = get_keymint_public_key(&keymint_key).unwrap();
+
+ let group = EcGroup::from_curve_name(openssl_ec_curve).unwrap();
+ let ec_key = EcKey::generate(&group).unwrap();
+ let local_key = PKey::from_ec_key(ec_key).unwrap();
+ let local_pub_key = local_key.public_key_to_der().unwrap();
+
+ check_agreement(&sec_level, &keymint_key.key, &keymint_pub_key, &local_key, &local_pub_key);
+}
+
+test_ec_key_agree!(test_ec_p224_key_agreement, EcCurve::P_224);
+test_ec_key_agree!(test_ec_p256_key_agreement, EcCurve::P_256);
+test_ec_key_agree!(test_ec_p384_key_agreement, EcCurve::P_384);
+test_ec_key_agree!(test_ec_p521_key_agreement, EcCurve::P_521);
+
+/// Generate two EC keys with curve `CURVE_25519` from KeyMint and OpeanSSL.
+/// Perform local ECDH between them and verify that the derived secrets are the same.
+#[test]
+fn keystore2_ec_25519_agree_key_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("ks_ec_25519_test_key_agree_{}", getuid());
+ let keymint_key = key_generations::generate_ec_agree_key(
+ &sec_level,
+ EcCurve::CURVE_25519,
+ Digest::NONE,
+ Domain::APP,
+ -1,
+ Some(alias),
+ )
+ .unwrap();
+
+ let keymint_pub_key = get_keymint_public_key(&keymint_key).unwrap();
+
+ let local_key = PKey::generate_x25519().unwrap();
+ let local_pub_key = local_key.public_key_to_der().unwrap();
+
+ check_agreement(&sec_level, &keymint_key.key, &keymint_pub_key, &local_key, &local_pub_key);
+}
+
+/// Generate two EC keys with different curves and try to perform local ECDH. Since keys are using
+/// different curves operation should fail with `ErrorCode:INVALID_ARGUMENT`.
+#[test]
+fn keystore2_ec_agree_key_with_different_curves_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("ks_test_key_agree_fail{}", getuid());
+ let keymint_key = key_generations::generate_ec_agree_key(
+ &sec_level,
+ EcCurve::P_256,
+ Digest::SHA_2_256,
+ Domain::APP,
+ -1,
+ Some(alias),
+ )
+ .unwrap();
+
+ let local_key = PKey::generate_x25519().unwrap();
+ let local_pub_key = local_key.public_key_to_der().unwrap();
+
+ // If the keys are using different curves KeyMint should fail with
+ // ErrorCode:INVALID_ARGUMENT.
+ let authorizations = authorizations::AuthSetBuilder::new().purpose(KeyPurpose::AGREE_KEY);
+ let key_agree_op = sec_level.createOperation(&keymint_key.key, &authorizations, false).unwrap();
+ assert!(key_agree_op.iOperation.is_some());
+
+ let op = key_agree_op.iOperation.unwrap();
+ let result = key_generations::map_ks_error(op.finish(Some(&local_pub_key), None));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INVALID_ARGUMENT), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_tests.rs b/keystore2/tests/keystore2_client_tests.rs
index 45c5fb7..07a298a 100644
--- a/keystore2/tests/keystore2_client_tests.rs
+++ b/keystore2/tests/keystore2_client_tests.rs
@@ -21,6 +21,7 @@
pub mod keystore2_client_grant_key_tests;
pub mod keystore2_client_hmac_key_tests;
pub mod keystore2_client_import_keys_tests;
+pub mod keystore2_client_key_agreement_tests;
pub mod keystore2_client_key_id_domain_tests;
pub mod keystore2_client_list_entries_tests;
pub mod keystore2_client_operation_tests;