Merge "Remove unused code in Keystore2's vintf library"
diff --git a/diced/Android.bp b/diced/Android.bp
index e13d863..6bd7c06 100644
--- a/diced/Android.bp
+++ b/diced/Android.bp
@@ -28,10 +28,7 @@
vendor_available: true,
rustlibs: [
- "android.hardware.security.dice-V1-rust",
"libanyhow",
- "libdiced_open_dice_cbor",
- "libkeystore2_crypto_rust",
],
}
@@ -42,10 +39,7 @@
test_suites: ["general-tests"],
auto_gen_config: true,
rustlibs: [
- "android.hardware.security.dice-V1-rust",
"libanyhow",
- "libdiced_open_dice_cbor",
- "libkeystore2_crypto_rust",
],
}
@@ -56,11 +50,9 @@
vendor_available: true,
rustlibs: [
- "android.hardware.security.dice-V1-rust",
"libanyhow",
- "libdiced_open_dice_cbor",
+ "libdiced_open_dice",
"libdiced_utils",
- "libkeystore2_crypto_rust",
],
}
@@ -71,158 +63,8 @@
test_suites: ["general-tests"],
auto_gen_config: true,
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",
- ],
-}
-
-rust_library {
- name: "libdiced_vendor",
- crate_name: "diced",
- srcs: ["src/lib_vendor.rs"],
-
- vendor_available: true,
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "libdiced_open_dice_cbor",
- "libanyhow",
- "libbinder_rs",
- "libdiced_utils",
- "libkeystore2_crypto_rust",
- "liblibc",
- "liblog_rust",
- "libnix",
- "libserde",
- "libserde_cbor",
- "libthiserror",
- ],
-}
-
-rust_binary {
- name: "diced",
- srcs: ["src/diced_main.rs"],
- prefer_rlib: true,
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "libandroid_logger",
- "libbinder_rs",
- "libdiced",
- "libdiced_open_dice_cbor",
- "libdiced_sample_inputs",
- "libdiced_utils",
- "liblog_rust",
- ],
- init_rc: ["diced.rc"],
-}
-
-rust_binary {
- name: "diced.microdroid",
- srcs: ["src/diced_main.rs"],
- prefer_rlib: true,
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "libandroid_logger",
- "libbinder_rs",
- "libdiced",
- "libdiced_open_dice_cbor",
- "libdiced_sample_inputs",
- "libdiced_utils",
- "liblog_rust",
- ],
- init_rc: ["diced.microdroid.rc"],
- bootstrap: true,
-}
-
-rust_test {
- name: "diced_test",
- crate_name: "diced_test",
- srcs: ["src/lib.rs"],
- test_suites: ["general-tests"],
- auto_gen_config: true,
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "android.security.dice-rust",
- "libanyhow",
- "libbinder_rs",
- "libdiced_open_dice_cbor",
- "libdiced_utils",
- "libkeystore2_crypto_rust",
- "libkeystore2_selinux",
- "libkeystore2_vintf_rust",
- "liblibc",
- "liblog_rust",
- "libnix",
- "libserde",
- "libserde_cbor",
- "libthiserror",
- ],
-}
-
-rust_test {
- name: "diced_vendor_test",
- crate_name: "diced_vendor_test",
- srcs: ["src/lib_vendor.rs"],
- test_suites: ["general-tests"],
- auto_gen_config: true,
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "libanyhow",
- "libdiced_open_dice_cbor",
- "libdiced_sample_inputs",
- "libdiced_utils",
- "libbinder_rs",
- "libkeystore2_crypto_rust",
- "liblibc",
- "liblog_rust",
- "libnix",
- "libserde",
- "libserde_cbor",
- "libthiserror",
- ],
-}
-
-rust_test {
- name: "diced_client_test",
- srcs: [
- "src/diced_client_test.rs",
- ],
- require_root: true,
- auto_gen_config: true,
- test_suites: [
- "general-tests",
- ],
-
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "android.security.dice-rust",
- "libanyhow",
- "libbinder_rs",
- "libdiced_open_dice_cbor",
- "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 d81efdd..1b078a4 100644
--- a/diced/TEST_MAPPING
+++ b/diced/TEST_MAPPING
@@ -1,16 +1,22 @@
{
"presubmit": [
{
+ "name": "libdiced_open_dice.integration_test"
+ },
+ {
+ "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/aidl/Android.bp b/diced/aidl/Android.bp
deleted file mode 100644
index 57dad53..0000000
--- a/diced/aidl/Android.bp
+++ /dev/null
@@ -1,51 +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.
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "system_security_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["system_security_license"],
-}
-
-aidl_interface {
- name: "android.security.dice",
- srcs: [ "android/security/dice/*.aidl" ],
- unstable: true,
- imports: ["android.hardware.security.dice-V1"],
- backend: {
- java: {
- enabled: false,
- platform_apis: false,
- },
- rust: {
- enabled: true,
- apex_available: [
- "//apex_available:platform",
- "com.android.compos",
- "com.android.virt",
- ],
- },
- ndk: {
- enabled: true,
- apps_enabled: false,
- apex_available: [
- "//apex_available:platform",
- "com.android.compos",
- ],
- }
- },
-}
diff --git a/diced/aidl/android/security/dice/IDiceMaintenance.aidl b/diced/aidl/android/security/dice/IDiceMaintenance.aidl
deleted file mode 100644
index c81fdea..0000000
--- a/diced/aidl/android/security/dice/IDiceMaintenance.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.security.dice;
-
-import android.hardware.security.dice.InputValues;
-
-/**
- * The maintenance allows callers to prompt the DICE node to demote itself.
- *
- * @hide
- */
-@SensitiveData
-interface IDiceMaintenance {
- /**
- * The implementation must demote itself by deriving new effective artifacts
- * based on the list of input data passed to the function.
- * As opposed to the IDiceNode::demote, this function effects all clients of
- * the implementation.
- *
- * ## Error as service specific exception:
- * ResponseCode::PERMISSION_DENIED if the caller does not have the demote_self permission.
- * May produce any ResponseCode if anything went wrong.
- */
- void demoteSelf(in InputValues[] input_values);
-}
diff --git a/diced/aidl/android/security/dice/IDiceNode.aidl b/diced/aidl/android/security/dice/IDiceNode.aidl
deleted file mode 100644
index 2b3ef76..0000000
--- a/diced/aidl/android/security/dice/IDiceNode.aidl
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.security.dice;
-
-import android.hardware.security.dice.Bcc;
-import android.hardware.security.dice.BccHandover;
-import android.hardware.security.dice.InputValues;
-import android.hardware.security.dice.Signature;
-
-/**
- * An implementation of IDiceNode provides access to DICE secrets to its clients. It
- * uses binder's caller UID and security context to identify its callers and assures
- * That clients can only access their specific DICE secrets.
- * It may operate in two different modes, resident mode and proxy mode.
- *
- * ## Resident mode.
- * In resident mode, the node is in possession of the secrets corresponding to its level in
- * the dice tree. It can act as root of the sub tree that it serves. The secrets are memory
- * resident in the node. It identifies its callers and prepends the caller's identity to the
- * request's vector of input values. It then derives the required secrets by iterating through
- * the request's vector of input values in ascending order.
- *
- * ## Proxy mode.
- * In proxy mode, the node has a connection to a parent node. It serves its callers by verifying
- * their identity, by prefixing the client's vector of input values with client's identity, and
- * forwarding the request to the next level up.
- *
- * The modes are implementation details that are completely transparent to the clients.
- *
- * Privacy: Unprivileged apps may not use this service ever because it may provide access to a
- * device specific id that is stable across reinstalls, reboots, and applications.
- *
- * @hide
- */
-@SensitiveData
-interface IDiceNode {
- /**
- * Uses the a key derived from the caller's attestation secret to sign the payload using
- * RFC 8032 PureEd25519 and returns the signature. The payload is limited to 1024 bytes.
- *
- * ## Error as service specific exception:
- * ResponseCode::PERMISSION_DENIED if the caller does not have the use_sign permission.
- */
- Signature sign(in InputValues[] id, in byte[] payload);
-
- /**
- * Returns the attestation certificate chain of the caller if `inputValues` is empty or the
- * chain to the given child of the caller identified by the `inputValues` vector.
- *
- * ## Error as service specific exception:
- * ResponseCode::PERMISSION_DENIED if the caller does not have the get_attestation_chain
- * permission.
- */
- Bcc getAttestationChain(in InputValues[] inputValues);
-
- /**
- * This function allows a client to become a resident node. Called with empty InputValues
- * vectors, an implementation returns the client's DICE secrets. If inputValues is
- * not empty, the appropriate derivations are performed starting from the client's level.
- * The function must never return secrets pertaining to the implementation or a parent
- * thereof in the DICE hierarchy.
- *
- * ## Error as service specific exception:
- * ResponseCode::PERMISSION_DENIED if the implementation does not allow resident nodes
- * at the client's level.
- */
- BccHandover derive(in InputValues[] inputValues);
-
- /**
- * The client demotes itself to the given identity. When serving the calling client,
- * the implementation must append the given identities. Essentially, the client assumes
- * the identity of one of its children. This operation is not reversible, i.e., there
- * is no promotion. Further demotion is possible.
- *
- * If the operation fails for any reason. No further services must be provided. Ideally,
- * a device shutdown/reboot is triggered.
- *
- * ## Error as service specific exception:
- * ResponseCode::PERMISSION_DENIED if the caller does not have the demote permission.
- */
- void demote(in InputValues[] inputValues);
-}
diff --git a/diced/aidl/android/security/dice/ResponseCode.aidl b/diced/aidl/android/security/dice/ResponseCode.aidl
deleted file mode 100644
index 7c66058..0000000
--- a/diced/aidl/android/security/dice/ResponseCode.aidl
+++ /dev/null
@@ -1,37 +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.
- */
-
-package android.security.dice;
-
-@Backing(type="int")
-/**
- * Service specific error codes.
- * @hide
- */
-enum ResponseCode {
- /**
- * The caller has insufficient privilege to access the DICE API.
- */
- PERMISSION_DENIED = 1,
- /**
- * An unexpected error occurred, likely with IO or IPC.
- */
- SYSTEM_ERROR = 2,
- /**
- * Returned if the called function is not implemented.
- */
- NOT_IMPLEMENTED = 3,
-}
diff --git a/diced/diced.microdroid.rc b/diced/diced.microdroid.rc
deleted file mode 100644
index 2226f47..0000000
--- a/diced/diced.microdroid.rc
+++ /dev/null
@@ -1,13 +0,0 @@
-# Start the Diced service.
-#
-# See system/core/init/README.md for information on the init.rc language.
-
-service diced /system/bin/diced.microdroid
- class main
- user diced
- group diced
- # The diced service must not be allowed to restart.
- # If it crashes for any reason security critical state is lost.
- # The only remedy is to restart the device.
- oneshot
- writepid /dev/cpuset/foreground/tasks
diff --git a/diced/diced.rc b/diced/diced.rc
deleted file mode 100644
index 8c43fa5..0000000
--- a/diced/diced.rc
+++ /dev/null
@@ -1,13 +0,0 @@
-# Start the Diced service.
-#
-# See system/core/init/README.md for information on the init.rc language.
-
-service diced /system/bin/diced
- class main
- user diced
- group diced
- # The diced service must not be allowed to restart.
- # If it crashes for any reason security critical state is lost.
- # The only remedy is to restart the device.
- oneshot
- writepid /dev/cpuset/foreground/tasks
diff --git a/diced/open_dice/Android.bp b/diced/open_dice/Android.bp
new file mode 100644
index 0000000..2505b42
--- /dev/null
+++ b/diced/open_dice/Android.bp
@@ -0,0 +1,256 @@
+package {
+ default_visibility: [":__subpackages__"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+ name: "libdiced_open_dice_defaults",
+ crate_name: "diced_open_dice",
+ srcs: ["src/lib.rs"],
+ static_libs: [
+ "libopen_dice_cbor",
+ ],
+ vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
+}
+
+rust_library_rlib {
+ name: "libdiced_open_dice_nostd",
+ defaults: ["libdiced_open_dice_defaults"],
+ rustlibs: [
+ "libopen_dice_bcc_bindgen_nostd",
+ "libopen_dice_cbor_bindgen_nostd",
+ "libzeroize_nostd",
+ ],
+ whole_static_libs: [
+ "libopen_dice_cbor",
+ "libcrypto_baremetal",
+ ],
+ visibility: [
+ "//packages/modules/Virtualization:__subpackages__",
+ ],
+}
+
+rust_library {
+ name: "libdiced_open_dice",
+ defaults: ["libdiced_open_dice_defaults"],
+ rustlibs: [
+ "libopen_dice_bcc_bindgen",
+ "libopen_dice_cbor_bindgen",
+ "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 {
+ name: "libdiced_open_dice_test_defaults",
+ crate_name: "diced_open_dice_test",
+ srcs: ["tests/*.rs"],
+ test_suites: ["general-tests"],
+}
+
+rust_test {
+ name: "libdiced_open_dice.integration_test",
+ defaults: ["libdiced_open_dice_test_defaults"],
+ rustlibs: [
+ "libdiced_open_dice",
+ ],
+}
+
+rust_test {
+ name: "libdiced_open_dice_nostd.integration_test",
+ defaults: ["libdiced_open_dice_test_defaults"],
+ rustlibs: [
+ "libdiced_open_dice_nostd",
+ ],
+}
+
+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
new file mode 100644
index 0000000..1575113
--- /dev/null
+++ b/diced/open_dice/src/bcc.rs
@@ -0,0 +1,192 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This module mirrors the content in open-dice/include/dice/android/bcc.h
+
+use crate::dice::{Cdi, CdiValues, DiceArtifacts, InputValues, CDI_SIZE};
+use crate::error::{check_result, DiceError, Result};
+use open_dice_bcc_bindgen::{
+ BccConfigValues, BccFormatConfigDescriptor, BccHandoverMainFlow, BccHandoverParse, BccMainFlow,
+ BCC_INPUT_COMPONENT_NAME, BCC_INPUT_COMPONENT_VERSION, BCC_INPUT_RESETTABLE,
+};
+use std::{ffi::CStr, ptr};
+
+/// Formats a configuration descriptor following the BCC's specification.
+/// See https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
+pub fn bcc_format_config_descriptor(
+ name: Option<&CStr>,
+ version: Option<u64>,
+ resettable: bool,
+ buffer: &mut [u8],
+) -> Result<usize> {
+ let mut inputs = 0;
+ if name.is_some() {
+ inputs |= BCC_INPUT_COMPONENT_NAME;
+ }
+ if version.is_some() {
+ inputs |= BCC_INPUT_COMPONENT_VERSION;
+ }
+ if resettable {
+ inputs |= BCC_INPUT_RESETTABLE;
+ }
+
+ let values = BccConfigValues {
+ inputs,
+ component_name: name.map_or(ptr::null(), |p| p.as_ptr()),
+ component_version: version.unwrap_or(0),
+ };
+
+ let mut buffer_size = 0;
+ // SAFETY: The function writes to the buffer, within the given bounds, and only reads the
+ // input values. It writes its result to buffer_size.
+ check_result(unsafe {
+ BccFormatConfigDescriptor(&values, buffer.len(), buffer.as_mut_ptr(), &mut buffer_size)
+ })?;
+ Ok(buffer_size)
+}
+
+/// Executes the main BCC flow.
+///
+/// Given a full set of input values along with the current BCC and CDI values,
+/// computes the next CDI values and matching updated BCC.
+pub fn bcc_main_flow(
+ current_cdi_attest: &Cdi,
+ current_cdi_seal: &Cdi,
+ current_bcc: &[u8],
+ input_values: &InputValues,
+ next_cdi_values: &mut CdiValues,
+ next_bcc: &mut [u8],
+) -> Result<usize> {
+ let mut next_bcc_size = 0;
+ // SAFETY: `BccMainFlow` only reads the current `bcc` and CDI values and writes
+ // to `next_bcc` and next CDI values within its bounds. It also reads
+ // `input_values` as a constant input and doesn't store any pointer.
+ // The first argument can be null and is not used in the current implementation.
+ check_result(unsafe {
+ BccMainFlow(
+ ptr::null_mut(), // context
+ current_cdi_attest.as_ptr(),
+ current_cdi_seal.as_ptr(),
+ current_bcc.as_ptr(),
+ current_bcc.len(),
+ input_values.as_ptr(),
+ next_bcc.len(),
+ next_bcc.as_mut_ptr(),
+ &mut next_bcc_size,
+ next_cdi_values.cdi_attest.as_mut_ptr(),
+ next_cdi_values.cdi_seal.as_mut_ptr(),
+ )
+ })?;
+ Ok(next_bcc_size)
+}
+
+/// 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
new file mode 100644
index 0000000..9266b6f
--- /dev/null
+++ b/diced/open_dice/src/dice.rs
@@ -0,0 +1,270 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Structs and functions about the types used in DICE.
+//! This module mirrors the content in open-dice/include/dice/dice.h
+
+use crate::error::{check_result, Result};
+pub use open_dice_cbor_bindgen::DiceMode;
+use open_dice_cbor_bindgen::{
+ DiceConfigType, DiceDeriveCdiCertificateId, DiceDeriveCdiPrivateKeySeed, DiceInputValues,
+ DiceMainFlow, DICE_CDI_SIZE, DICE_HASH_SIZE, DICE_HIDDEN_SIZE, DICE_ID_SIZE,
+ DICE_INLINE_CONFIG_SIZE, DICE_PRIVATE_KEY_SEED_SIZE, 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;
+/// The size of the DICE hidden value.
+pub const HIDDEN_SIZE: usize = DICE_HIDDEN_SIZE as usize;
+/// The size of a DICE inline config.
+const INLINE_CONFIG_SIZE: usize = DICE_INLINE_CONFIG_SIZE as usize;
+/// The size of a CDI.
+pub const CDI_SIZE: usize = DICE_CDI_SIZE as usize;
+/// The size of a private key seed.
+pub const PRIVATE_KEY_SEED_SIZE: usize = DICE_PRIVATE_KEY_SEED_SIZE as usize;
+/// The size of 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;
+
+/// Array type of hashes used by DICE.
+pub type Hash = [u8; HASH_SIZE];
+/// Array type of additional input.
+pub type Hidden = [u8; HIDDEN_SIZE];
+/// Array type of inline configuration values.
+pub type InlineConfig = [u8; INLINE_CONFIG_SIZE];
+/// Array type of CDIs.
+pub type Cdi = [u8; CDI_SIZE];
+/// Array type of 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> {
+ /// Reference to an inline descriptor.
+ Inline(&'a InlineConfig),
+ /// Reference to a free form descriptor that will be hashed by the implementation.
+ Descriptor(&'a [u8]),
+}
+
+impl Config<'_> {
+ fn dice_config_type(&self) -> DiceConfigType {
+ match self {
+ Self::Inline(_) => DiceConfigType::kDiceConfigTypeInline,
+ Self::Descriptor(_) => DiceConfigType::kDiceConfigTypeDescriptor,
+ }
+ }
+
+ fn inline_config(&self) -> InlineConfig {
+ match self {
+ Self::Inline(inline) => **inline,
+ Self::Descriptor(_) => [0u8; INLINE_CONFIG_SIZE],
+ }
+ }
+
+ fn descriptor_ptr(&self) -> *const u8 {
+ match self {
+ Self::Descriptor(descriptor) => descriptor.as_ptr(),
+ _ => ptr::null(),
+ }
+ }
+
+ fn descriptor_size(&self) -> usize {
+ match self {
+ Self::Descriptor(descriptor) => descriptor.len(),
+ _ => 0,
+ }
+ }
+}
+
+/// Wrap of `DiceInputValues`.
+#[derive(Clone, Debug)]
+pub struct InputValues(DiceInputValues);
+
+impl InputValues {
+ /// Creates a new `InputValues`.
+ pub fn new(
+ code_hash: Hash,
+ config: Config,
+ authority_hash: Hash,
+ mode: DiceMode,
+ hidden: Hidden,
+ ) -> Self {
+ Self(DiceInputValues {
+ code_hash,
+ code_descriptor: ptr::null(),
+ code_descriptor_size: 0,
+ config_type: config.dice_config_type(),
+ config_value: config.inline_config(),
+ config_descriptor: config.descriptor_ptr(),
+ config_descriptor_size: config.descriptor_size(),
+ authority_hash,
+ authority_descriptor: ptr::null(),
+ authority_descriptor_size: 0,
+ mode,
+ hidden,
+ })
+ }
+
+ /// Returns a raw pointer to the wrapped `DiceInputValues`.
+ pub fn as_ptr(&self) -> *const DiceInputValues {
+ &self.0 as *const DiceInputValues
+ }
+}
+
+/// Derives a CDI private key seed from a `cdi_attest` value.
+pub fn derive_cdi_private_key_seed(cdi_attest: &Cdi) -> Result<PrivateKeySeed> {
+ let mut seed = 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 {
+ DiceDeriveCdiPrivateKeySeed(
+ ptr::null_mut(), // context
+ cdi_attest.as_ptr(),
+ seed.as_mut_ptr(),
+ )
+ })?;
+ Ok(seed)
+}
+
+/// Derives an ID from the given `cdi_public_key` value.
+pub fn derive_cdi_certificate_id(cdi_public_key: &[u8]) -> Result<DiceId> {
+ let mut id = [0u8; ID_SIZE];
+ // SAFETY: The function writes to the buffer within the given bounds, and only reads the
+ // input values. The first argument context is not used in this function.
+ check_result(unsafe {
+ DiceDeriveCdiCertificateId(
+ ptr::null_mut(), // context
+ cdi_public_key.as_ptr(),
+ cdi_public_key.len(),
+ id.as_mut_ptr(),
+ )
+ })?;
+ Ok(id)
+}
+
+/// Executes the main DICE flow.
+///
+/// Given a full set of input values and the current CDI values, computes the
+/// next CDI values and a matching certificate.
+/// Returns the actual size of the next CDI certificate.
+pub fn dice_main_flow(
+ current_cdi_attest: &Cdi,
+ current_cdi_seal: &Cdi,
+ input_values: &InputValues,
+ next_cdi_certificate: &mut [u8],
+ next_cdi_values: &mut CdiValues,
+) -> Result<usize> {
+ let mut next_cdi_certificate_actual_size = 0;
+ // SAFETY: The function only reads the current CDI values and inputs and writes
+ // to `next_cdi_certificate` and next CDI values within its bounds.
+ // The first argument can be null and is not used in the current implementation.
+ check_result(unsafe {
+ DiceMainFlow(
+ ptr::null_mut(), // context
+ current_cdi_attest.as_ptr(),
+ current_cdi_seal.as_ptr(),
+ input_values.as_ptr(),
+ next_cdi_certificate.len(),
+ next_cdi_certificate.as_mut_ptr(),
+ &mut next_cdi_certificate_actual_size,
+ next_cdi_values.cdi_attest.as_mut_ptr(),
+ next_cdi_values.cdi_seal.as_mut_ptr(),
+ )
+ })?;
+ Ok(next_cdi_certificate_actual_size)
+}
diff --git a/diced/open_dice/src/error.rs b/diced/open_dice/src/error.rs
new file mode 100644
index 0000000..4c67335
--- /dev/null
+++ b/diced/open_dice/src/error.rs
@@ -0,0 +1,59 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Errors and relating functions thrown in this library.
+
+use open_dice_cbor_bindgen::DiceResult;
+use std::{fmt, result};
+
+#[cfg(feature = "std")]
+use std::error::Error;
+
+/// Error type used by DICE.
+#[derive(Debug)]
+pub enum DiceError {
+ /// Provided input was invalid.
+ InvalidInput,
+ /// Provided buffer was too small.
+ BufferTooSmall,
+ /// Platform error.
+ PlatformError,
+}
+
+/// This makes `DiceError` accepted by anyhow.
+#[cfg(feature = "std")]
+impl Error for DiceError {}
+
+impl fmt::Display for DiceError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::InvalidInput => write!(f, "invalid input"),
+ Self::BufferTooSmall => write!(f, "buffer too small"),
+ Self::PlatformError => write!(f, "platform error"),
+ }
+ }
+}
+
+/// DICE result type.
+pub type Result<T> = result::Result<T, DiceError>;
+
+/// Checks the given `DiceResult`. Returns an error if it's not OK.
+pub fn check_result(result: DiceResult) -> Result<()> {
+ match result {
+ DiceResult::kDiceResultOk => Ok(()),
+ DiceResult::kDiceResultInvalidInput => Err(DiceError::InvalidInput),
+ DiceResult::kDiceResultBufferTooSmall => Err(DiceError::BufferTooSmall),
+ DiceResult::kDiceResultPlatformError => Err(DiceError::PlatformError),
+ }
+}
diff --git a/diced/open_dice/src/lib.rs b/diced/open_dice/src/lib.rs
new file mode 100644
index 0000000..e7ec56b
--- /dev/null
+++ b/diced/open_dice/src/lib.rs
@@ -0,0 +1,45 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Implements safe wrappers around the public API of libopen-dice for
+//! both std and nostd usages.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#[cfg(not(feature = "std"))]
+extern crate core as std;
+
+mod bcc;
+mod dice;
+mod error;
+mod ops;
+#[cfg(feature = "std")]
+mod retry;
+
+pub use bcc::{
+ bcc_format_config_descriptor, bcc_handover_main_flow, bcc_handover_parse, bcc_main_flow,
+ BccHandover,
+};
+pub use dice::{
+ 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::{generate_certificate, hash, kdf, keypair_from_seed, sign, verify};
+#[cfg(feature = "std")]
+pub use retry::{
+ retry_bcc_format_config_descriptor, retry_bcc_main_flow, retry_dice_main_flow,
+ retry_generate_certificate, OwnedDiceArtifacts,
+};
diff --git a/diced/open_dice/src/ops.rs b/diced/open_dice/src/ops.rs
new file mode 100644
index 0000000..8222b26
--- /dev/null
+++ b/diced/open_dice/src/ops.rs
@@ -0,0 +1,142 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This module mirrors the content in open-dice/include/dice/ops.h
+//! It contains the set of functions that implement various operations that the
+//! main DICE functions depend on.
+
+use crate::dice::{
+ Hash, InputValues, 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::{
+ DiceGenerateCertificate, DiceHash, DiceKdf, DiceKeypairFromSeed, DiceSign, DiceVerify,
+};
+use std::ptr;
+
+/// Hashes the provided input using DICE's hash function `DiceHash`.
+pub fn hash(input: &[u8]) -> Result<Hash> {
+ let mut output: Hash = [0; HASH_SIZE];
+ // SAFETY: DiceHash takes a sized input buffer and writes to a constant-sized output buffer.
+ // The first argument context is not used in this function.
+ check_result(unsafe {
+ DiceHash(
+ ptr::null_mut(), // context
+ input.as_ptr(),
+ input.len(),
+ output.as_mut_ptr(),
+ )
+ })?;
+ Ok(output)
+}
+
+/// An implementation of HKDF-SHA512. Derives a key of `derived_key.len()` bytes from `ikm`, `salt`,
+/// and `info`. The derived key is written to the `derived_key`.
+pub fn kdf(ikm: &[u8], salt: &[u8], info: &[u8], derived_key: &mut [u8]) -> Result<()> {
+ // SAFETY: The function writes to the `derived_key`, within the given bounds, and only reads the
+ // input values. The first argument context is not used in this function.
+ check_result(unsafe {
+ DiceKdf(
+ ptr::null_mut(), // context
+ derived_key.len(),
+ ikm.as_ptr(),
+ ikm.len(),
+ salt.as_ptr(),
+ salt.len(),
+ info.as_ptr(),
+ info.len(),
+ derived_key.as_mut_ptr(),
+ )
+ })
+}
+
+/// 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
new file mode 100644
index 0000000..76a214c
--- /dev/null
+++ b/diced/open_dice/src/retry.rs
@@ -0,0 +1,160 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This module implements a retry version for multiple DICE functions that
+//! require preallocated output buffer. As the retry functions require
+//! memory allocation on heap, currently we only expose these functions in
+//! std environment.
+
+use crate::bcc::{bcc_format_config_descriptor, bcc_main_flow};
+use crate::dice::{
+ dice_main_flow, Cdi, CdiValues, 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
+ F: FnMut(&mut Vec<u8>) -> Result<usize>,
+{
+ const INITIAL_BUFFER_SIZE: usize = 256;
+ const MAX_BUFFER_SIZE: usize = 64 * 1024 * 1024;
+
+ let mut buffer = vec![0u8; INITIAL_BUFFER_SIZE];
+ while buffer.len() <= MAX_BUFFER_SIZE {
+ match f(&mut buffer) {
+ Err(DiceError::BufferTooSmall) => {
+ let new_size = buffer.len() * 2;
+ buffer.resize(new_size, 0);
+ }
+ Err(e) => return Err(e),
+ Ok(actual_size) => {
+ if actual_size > buffer.len() {
+ panic!(
+ "actual_size larger than buffer size: open-dice function
+ may have written past the end of the buffer."
+ );
+ }
+ buffer.truncate(actual_size);
+ return Ok(buffer);
+ }
+ }
+ }
+ Err(DiceError::PlatformError)
+}
+
+/// Formats a configuration descriptor following the BCC's specification.
+pub fn retry_bcc_format_config_descriptor(
+ name: Option<&CStr>,
+ version: Option<u64>,
+ resettable: bool,
+) -> Result<Vec<u8>> {
+ retry_with_bigger_buffer(|buffer| {
+ bcc_format_config_descriptor(name, version, resettable, buffer)
+ })
+}
+
+/// Executes the main BCC flow.
+///
+/// Given a full set of input values along with the current BCC and CDI values,
+/// computes the next CDI values and matching updated BCC.
+pub fn retry_bcc_main_flow(
+ current_cdi_attest: &Cdi,
+ current_cdi_seal: &Cdi,
+ bcc: &[u8],
+ input_values: &InputValues,
+) -> Result<OwnedDiceArtifacts> {
+ let mut next_cdi_values = CdiValues::default();
+ let next_bcc = retry_with_bigger_buffer(|next_bcc| {
+ bcc_main_flow(
+ current_cdi_attest,
+ current_cdi_seal,
+ bcc,
+ input_values,
+ &mut next_cdi_values,
+ next_bcc,
+ )
+ })?;
+ Ok(OwnedDiceArtifacts { cdi_values: next_cdi_values, bcc: next_bcc })
+}
+
+/// Executes the main DICE flow.
+///
+/// Given a full set of input values and the current CDI values, computes the
+/// next CDI values and a matching certificate.
+pub fn retry_dice_main_flow(
+ current_cdi_attest: &Cdi,
+ current_cdi_seal: &Cdi,
+ input_values: &InputValues,
+) -> Result<(CdiValues, Vec<u8>)> {
+ let mut next_cdi_values = CdiValues::default();
+ let next_cdi_certificate = retry_with_bigger_buffer(|next_cdi_certificate| {
+ dice_main_flow(
+ current_cdi_attest,
+ current_cdi_seal,
+ input_values,
+ next_cdi_certificate,
+ &mut next_cdi_values,
+ )
+ })?;
+ Ok((next_cdi_values, next_cdi_certificate))
+}
+
+/// Generates an X.509 certificate from the given `subject_private_key_seed` and
+/// `input_values`, and signed by `authority_private_key_seed`.
+/// The subject private key seed is supplied here so the implementation can choose
+/// between asymmetric mechanisms, for example ECDSA vs Ed25519.
+/// Returns the generated certificate.
+pub fn retry_generate_certificate(
+ subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+ authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+ input_values: &InputValues,
+) -> Result<Vec<u8>> {
+ retry_with_bigger_buffer(|certificate| {
+ generate_certificate(
+ subject_private_key_seed,
+ authority_private_key_seed,
+ input_values,
+ certificate,
+ )
+ })
+}
diff --git a/diced/open_dice/tests/api_test.rs b/diced/open_dice/tests/api_test.rs
new file mode 100644
index 0000000..a47265b
--- /dev/null
+++ b/diced/open_dice/tests/api_test.rs
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use diced_open_dice::{
+ derive_cdi_certificate_id, 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() {
+ const EXPECTED_HASH: [u8; HASH_SIZE] = [
+ 0x30, 0x9e, 0xcc, 0x48, 0x9c, 0x12, 0xd6, 0xeb, 0x4c, 0xc4, 0x0f, 0x50, 0xc9, 0x02, 0xf2,
+ 0xb4, 0xd0, 0xed, 0x77, 0xee, 0x51, 0x1a, 0x7c, 0x7a, 0x9b, 0xcd, 0x3c, 0xa8, 0x6d, 0x4c,
+ 0xd8, 0x6f, 0x98, 0x9d, 0xd3, 0x5b, 0xc5, 0xff, 0x49, 0x96, 0x70, 0xda, 0x34, 0x25, 0x5b,
+ 0x45, 0xb0, 0xcf, 0xd8, 0x30, 0xe8, 0x1f, 0x60, 0x5d, 0xcf, 0x7d, 0xc5, 0x54, 0x2e, 0x93,
+ 0xae, 0x9c, 0xd7, 0x6f,
+ ];
+ assert_eq!(EXPECTED_HASH, hash(b"hello world").expect("hash failed"));
+}
+
+#[test]
+fn kdf_succeeds() {
+ let mut derived_key = [0u8; PRIVATE_KEY_SEED_SIZE];
+ kdf(b"myInitialKeyMaterial", b"mySalt", b"myInfo", &mut derived_key).unwrap();
+ const EXPECTED_DERIVED_KEY: [u8; PRIVATE_KEY_SEED_SIZE] = [
+ 0x91, 0x9b, 0x8d, 0x29, 0xc4, 0x1b, 0x93, 0xd7, 0xeb, 0x09, 0xfa, 0xd7, 0xc9, 0x87, 0xb0,
+ 0xd1, 0xcc, 0x26, 0xef, 0x07, 0x83, 0x42, 0xcf, 0xa3, 0x45, 0x0a, 0x57, 0xe9, 0x19, 0x86,
+ 0xef, 0x48,
+ ];
+ assert_eq!(EXPECTED_DERIVED_KEY, derived_key);
+}
+
+#[test]
+fn derive_cdi_certificate_id_succeeds() {
+ const EXPECTED_ID: [u8; ID_SIZE] = [
+ 0x7a, 0x36, 0x45, 0x2c, 0x02, 0xf6, 0x2b, 0xec, 0xf9, 0x80, 0x06, 0x75, 0x87, 0xa5, 0xc1,
+ 0x44, 0x0c, 0xd3, 0xc0, 0x6d,
+ ];
+ assert_eq!(EXPECTED_ID, derive_cdi_certificate_id(b"MyPubKey").unwrap());
+}
+
+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 a84190a..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: [
- // For ZVec
- "libkeystore2_crypto_rust",
- "libopen_dice_bcc_bindgen",
- "libopen_dice_cbor_bindgen",
- "libthiserror",
- ],
- static_libs: [
- "libopen_dice_bcc",
- "libopen_dice_cbor",
- ],
- vendor_available: true,
- apex_available: [
- "//apex_available:platform",
- "com.android.virt",
- ],
-}
-
rust_test {
name: "diced_open_dice_cbor_test",
crate_name: "diced_open_dice_cbor_test",
@@ -46,14 +23,7 @@
test_suites: ["general-tests"],
auto_gen_config: true,
rustlibs: [
+ "libdiced_open_dice",
"libdiced_sample_inputs",
- "libkeystore2_crypto_rust",
- "libopen_dice_bcc_bindgen",
- "libopen_dice_cbor_bindgen",
- "libthiserror",
- ],
- static_libs: [
- "libopen_dice_bcc",
- "libopen_dice_cbor",
],
}
diff --git a/diced/open_dice_cbor/lib.rs b/diced/open_dice_cbor/lib.rs
index ffb8a48..1650d93 100644
--- a/diced/open_dice_cbor/lib.rs
+++ b/diced/open_dice_cbor/lib.rs
@@ -12,871 +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::InputValuesOwned {
-//! code_hash: [3u8, dice::HASH_SIZE],
-//! config: dice::ConfigOwned::Descriptor("My descriptor".as_bytes().to_vec()),
-//! authority_hash: [0u8, dice::HASH_SIZE],
-//! mode: dice::Mode::Normal,
-//! hidden: [0u8, dice::HIDDEN_SIZE],
-//! };
-//! let (cdi_attest, cdi_seal, cert_chain) = context
-//! .main_flow(&parent_cdi_attest, &parent_cdi_seal, &input_values)?;
-//! ```
-
-use keystore2_crypto::{zvec, ZVec};
-use open_dice_bcc_bindgen::BccMainFlow;
-use open_dice_cbor_bindgen::{
- DiceConfigType, DiceDeriveCdiCertificateId, DiceDeriveCdiPrivateKeySeed,
- DiceGenerateCertificate, DiceHash, DiceInputValues, DiceKdf, DiceKeypairFromSeed, DiceMainFlow,
- DiceMode, DiceResult, DiceSign, DiceVerify, DICE_CDI_SIZE, DICE_HASH_SIZE, DICE_HIDDEN_SIZE,
- DICE_ID_SIZE, DICE_INLINE_CONFIG_SIZE, DICE_PRIVATE_KEY_SEED_SIZE, DICE_PRIVATE_KEY_SIZE,
- DICE_PUBLIC_KEY_SIZE, DICE_SIGNATURE_SIZE,
-};
-use open_dice_cbor_bindgen::{
- DiceConfigType_kDiceConfigTypeDescriptor as DICE_CONFIG_TYPE_DESCRIPTOR,
- DiceConfigType_kDiceConfigTypeInline as DICE_CONFIG_TYPE_INLINE,
- DiceMode_kDiceModeDebug as DICE_MODE_DEBUG,
- DiceMode_kDiceModeMaintenance as DICE_MODE_RECOVERY,
- DiceMode_kDiceModeNormal as DICE_MODE_NORMAL,
- DiceMode_kDiceModeNotInitialized as DICE_MODE_NOT_CONFIGURED,
- DiceResult_kDiceResultBufferTooSmall as DICE_RESULT_BUFFER_TOO_SMALL,
- DiceResult_kDiceResultInvalidInput as DICE_RESULT_INVALID_INPUT,
- DiceResult_kDiceResultOk as DICE_RESULT_OK,
- DiceResult_kDiceResultPlatformError as DICE_RESULT_PLATFORM_ERROR,
-};
-use std::ffi::{c_void, NulError};
-
-/// The size of a DICE hash.
-pub const HASH_SIZE: usize = DICE_HASH_SIZE as usize;
-/// The size of the DICE hidden value.
-pub const HIDDEN_SIZE: usize = DICE_HIDDEN_SIZE as usize;
-/// The size of a DICE inline config.
-pub const INLINE_CONFIG_SIZE: usize = DICE_INLINE_CONFIG_SIZE as usize;
-/// The size of a private key seed.
-pub const PRIVATE_KEY_SEED_SIZE: usize = DICE_PRIVATE_KEY_SEED_SIZE as usize;
-/// The size of a CDI.
-pub const CDI_SIZE: usize = DICE_CDI_SIZE as usize;
-/// The size of an ID.
-pub const ID_SIZE: usize = DICE_ID_SIZE as usize;
-/// The size of a private key.
-pub const PRIVATE_KEY_SIZE: usize = DICE_PRIVATE_KEY_SIZE as usize;
-/// The size of a public key.
-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;
-
-/// Open dice wrapper error type.
-#[derive(Debug, thiserror::Error, PartialEq, Eq)]
-pub enum Error {
- /// The libopen-dice backend reported InvalidInput.
- #[error("Open dice backend: Invalid input")]
- InvalidInput,
- /// The libopen-dice backend reported BufferTooSmall.
- #[error("Open dice backend: Buffer too small")]
- BufferTooSmall,
- /// The libopen-dice backend reported PlatformError.
- #[error("Open dice backend: Platform error")]
- PlatformError,
- /// The libopen-dice backend reported an error that is outside of the defined range of errors.
- /// The returned error code is embedded in this value.
- #[error("Open dice backend returned an unexpected error code: {0:?}")]
- Unexpected(u32),
-
- /// The allocation of a ZVec failed. Most likely due to a failure during the call to mlock.
- #[error("ZVec allocation failed")]
- ZVec(#[from] zvec::Error),
-
- /// Functions that have to convert str to CString may fail if the string has an interior
- /// nul byte.
- #[error("Input string has an interior nul byte.")]
- CStrNulError(#[from] NulError),
-}
-
-/// Open dice result type.
-pub type Result<T> = std::result::Result<T, Error>;
-
-impl From<DiceResult> for Error {
- fn from(result: DiceResult) -> Self {
- match result {
- DICE_RESULT_INVALID_INPUT => Error::InvalidInput,
- DICE_RESULT_BUFFER_TOO_SMALL => Error::BufferTooSmall,
- DICE_RESULT_PLATFORM_ERROR => Error::PlatformError,
- r => Error::Unexpected(r),
- }
- }
-}
-
-fn check_result(result: DiceResult) -> Result<()> {
- if result == DICE_RESULT_OK {
- Ok(())
- } else {
- Err(result.into())
- }
-}
-
-/// Configuration descriptor for dice input values.
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub enum Config<'a> {
- /// A reference to an inline descriptor.
- Inline(&'a [u8; INLINE_CONFIG_SIZE]),
- /// A reference to a free form descriptor that will be hashed by the implementation.
- Descriptor(&'a [u8]),
-}
-
-enum ConfigOwned {
- Inline([u8; INLINE_CONFIG_SIZE]),
- Descriptor(Vec<u8>),
-}
-
-impl Config<'_> {
- fn get_type(&self) -> DiceConfigType {
- match self {
- Self::Inline(_) => DICE_CONFIG_TYPE_INLINE,
- Self::Descriptor(_) => DICE_CONFIG_TYPE_DESCRIPTOR,
- }
- }
-
- fn get_inline(&self) -> [u8; INLINE_CONFIG_SIZE] {
- match self {
- Self::Inline(inline) => **inline,
- _ => [0u8; INLINE_CONFIG_SIZE],
- }
- }
-
- fn get_descriptor_as_ptr(&self) -> *const u8 {
- match self {
- Self::Descriptor(descriptor) => descriptor.as_ptr(),
- _ => std::ptr::null(),
- }
- }
-
- fn get_descriptor_size(&self) -> usize {
- match self {
- Self::Descriptor(descriptor) => descriptor.len(),
- _ => 0,
- }
- }
-}
-
-impl From<Config<'_>> for ConfigOwned {
- fn from(config: Config) -> Self {
- match config {
- Config::Inline(inline) => ConfigOwned::Inline(*inline),
- Config::Descriptor(descriptor) => ConfigOwned::Descriptor(descriptor.to_owned()),
- }
- }
-}
-
-/// DICE modes as defined here:
-/// https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/specification.md#mode-value-details
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum Mode {
- /// See documentation linked above.
- NotConfigured = 0,
- /// See documentation linked above.
- Normal = 1,
- /// See documentation linked above.
- Debug = 2,
- /// See documentation linked above.
- Recovery = 3,
-}
-
-impl Mode {
- fn get_internal(&self) -> DiceMode {
- match self {
- Self::NotConfigured => DICE_MODE_NOT_CONFIGURED,
- Self::Normal => DICE_MODE_NORMAL,
- Self::Debug => DICE_MODE_DEBUG,
- Self::Recovery => DICE_MODE_RECOVERY,
- }
- }
-}
-
-/// This trait allows API users to supply DICE input values without copying.
-pub trait InputValues {
- /// Returns the code hash.
- fn code_hash(&self) -> &[u8; HASH_SIZE];
- /// Returns the config.
- fn config(&self) -> Config;
- /// Returns the authority hash.
- fn authority_hash(&self) -> &[u8; HASH_SIZE];
- /// Returns the authority descriptor.
- fn authority_descriptor(&self) -> Option<&[u8]>;
- /// Returns the mode.
- fn mode(&self) -> Mode;
- /// Returns the hidden value.
- fn hidden(&self) -> &[u8; HIDDEN_SIZE];
-}
-
-/// An owning convenience type implementing `InputValues`.
-pub struct InputValuesOwned {
- code_hash: [u8; HASH_SIZE],
- config: ConfigOwned,
- authority_hash: [u8; HASH_SIZE],
- authority_descriptor: Option<Vec<u8>>,
- mode: Mode,
- hidden: [u8; HIDDEN_SIZE],
-}
-
-impl InputValuesOwned {
- /// Construct a new instance of InputValuesOwned.
- pub fn new(
- code_hash: [u8; HASH_SIZE],
- config: Config,
- authority_hash: [u8; HASH_SIZE],
- authority_descriptor: Option<Vec<u8>>,
- mode: Mode,
- hidden: [u8; HIDDEN_SIZE],
- ) -> Self {
- Self {
- code_hash,
- config: config.into(),
- authority_hash,
- authority_descriptor,
- mode,
- hidden,
- }
- }
-}
-
-impl InputValues for InputValuesOwned {
- fn code_hash(&self) -> &[u8; HASH_SIZE] {
- &self.code_hash
- }
- fn config(&self) -> Config {
- match &self.config {
- ConfigOwned::Inline(inline) => Config::Inline(inline),
- ConfigOwned::Descriptor(descriptor) => Config::Descriptor(descriptor.as_slice()),
- }
- }
- fn authority_hash(&self) -> &[u8; HASH_SIZE] {
- &self.authority_hash
- }
- fn authority_descriptor(&self) -> Option<&[u8]> {
- self.authority_descriptor.as_deref()
- }
- fn mode(&self) -> Mode {
- self.mode
- }
- fn hidden(&self) -> &[u8; HIDDEN_SIZE] {
- &self.hidden
- }
-}
-
-fn call_with_input_values<T: InputValues + ?Sized, F, R>(input_values: &T, f: F) -> Result<R>
-where
- F: FnOnce(*const DiceInputValues) -> Result<R>,
-{
- let input_values = DiceInputValues {
- code_hash: *input_values.code_hash(),
- code_descriptor: std::ptr::null(),
- code_descriptor_size: 0,
- config_type: input_values.config().get_type(),
- config_value: input_values.config().get_inline(),
- config_descriptor: input_values.config().get_descriptor_as_ptr(),
- config_descriptor_size: input_values.config().get_descriptor_size(),
- authority_hash: *input_values.authority_hash(),
- authority_descriptor: input_values
- .authority_descriptor()
- .map_or_else(std::ptr::null, <[u8]>::as_ptr),
- authority_descriptor_size: input_values.authority_descriptor().map_or(0, <[u8]>::len),
- mode: input_values.mode().get_internal(),
- hidden: *input_values.hidden(),
- };
-
- f(&input_values as *const DiceInputValues)
-}
-
-/// Multiple of the open dice function required preallocated output buffer
-/// which may be too small, this function implements the retry logic to handle
-/// too small buffer allocations.
-/// The callback `F` must expect a mutable reference to a buffer and a size hint
-/// field. The callback is called repeatedly as long as it returns
-/// `Err(Error::BufferTooSmall)`. If the size hint remains 0, the buffer size is
-/// doubled with each iteration. If the size hint is set by the callback, the buffer
-/// will be set to accommodate at least this many bytes.
-/// If the callback returns `Ok(())`, the buffer is truncated to the size hint
-/// exactly.
-/// The function panics if the callback returns `Ok(())` and the size hint is
-/// larger than the buffer size.
-fn retry_while_adjusting_output_buffer<F>(mut f: F) -> Result<Vec<u8>>
-where
- F: FnMut(&mut Vec<u8>, &mut usize) -> Result<()>,
-{
- let mut buffer = vec![0; INITIAL_OUT_BUFFER_SIZE];
- let mut actual_size: usize = 0;
- loop {
- match f(&mut buffer, &mut actual_size) {
- // If Error::BufferTooSmall was returned, the allocated certificate
- // buffer was to small for the output. So the buffer is resized to the actual
- // size, and a second attempt is made with the new buffer.
- Err(Error::BufferTooSmall) => {
- let new_size = if actual_size == 0 {
- // Due to an off spec implementation of open dice cbor, actual size
- // does not return the required size if the buffer was too small. So
- // we have to try and approach it gradually.
- buffer.len() * 2
- } else {
- actual_size
- };
- buffer.resize(new_size, 0);
- continue;
- }
- Err(e) => return Err(e),
- Ok(()) => {
- if actual_size > buffer.len() {
- panic!(
- "actual_size larger than buffer size: open-dice function
- may have written past the end of the buffer."
- );
- }
- // Truncate the certificate buffer to the actual size because it may be
- // smaller than the original allocation.
- buffer.truncate(actual_size);
- return Ok(buffer);
- }
- }
- }
-}
-
-/// Some libopen-dice variants use a context. Developers that want to customize these
-/// bindings may want to implement their own Context factory that creates a context
-/// useable by their preferred backend.
-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 DiceDeriveCdiPrivateKeySeed, see open dice
- /// documentation for details.
- fn derive_cdi_private_key_seed(&mut self, cdi_attest: &[u8; CDI_SIZE]) -> Result<ZVec> {
- let mut seed = ZVec::new(PRIVATE_KEY_SEED_SIZE)?;
- // SAFETY:
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument is expected to be a const array of size CDI_SIZE.
- // * The third argument is expected to be a non const array of size
- // PRIVATE_KEY_SEED_SIZE which is fulfilled if the call to ZVec::new above
- // succeeds.
- // * No pointers are expected to be valid beyond the scope of the function
- // call.
- check_result(unsafe {
- DiceDeriveCdiPrivateKeySeed(self.get_context(), cdi_attest.as_ptr(), seed.as_mut_ptr())
- })?;
- Ok(seed)
- }
-
- /// Safe wrapper around open-dice DiceDeriveCdiCertificateId, see open dice
- /// documentation for details.
- fn derive_cdi_certificate_id(&mut self, cdi_public_key: &[u8]) -> Result<ZVec> {
- let mut id = ZVec::new(ID_SIZE)?;
- // SAFETY:
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument is expected to be a const array with a size given by the
- // third argument.
- // * The fourth argument is expected to be a non const array of size
- // ID_SIZE which is fulfilled if the call to ZVec::new above succeeds.
- // * No pointers are expected to be valid beyond the scope of the function
- // call.
- check_result(unsafe {
- DiceDeriveCdiCertificateId(
- self.get_context(),
- cdi_public_key.as_ptr(),
- cdi_public_key.len(),
- id.as_mut_ptr(),
- )
- })?;
- Ok(id)
- }
-
- /// Safe wrapper around open-dice DiceMainFlow, see open dice
- /// documentation for details.
- /// Returns a tuple of:
- /// * The next attestation CDI,
- /// * the next seal CDI, and
- /// * the next attestation certificate.
- /// `(next_attest_cdi, next_seal_cdi, next_attestation_cert)`
- fn main_flow<T: InputValues + ?Sized>(
- &mut self,
- current_cdi_attest: &[u8; CDI_SIZE],
- current_cdi_seal: &[u8; CDI_SIZE],
- input_values: &T,
- ) -> Result<(CdiAttest, CdiSeal, Cert)> {
- let mut next_attest = CdiAttest::new(CDI_SIZE)?;
- let mut next_seal = CdiSeal::new(CDI_SIZE)?;
-
- // SAFETY (DiceMainFlow):
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument and the third argument are const arrays of size CDI_SIZE.
- // This is fulfilled as per the definition of the arguments `current_cdi_attest`
- // and `current_cdi_seal.
- // * The fourth argument is a pointer to `DiceInputValues`. It, and its indirect
- // references must be valid for the duration of the function call which
- // is guaranteed by `call_with_input_values` which puts `DiceInputValues`
- // on the stack and initializes it from the `input_values` argument which
- // implements the `InputValues` trait.
- // * The fifth and sixth argument are the length of and the pointer to the
- // allocated certificate buffer respectively. They are used to return
- // the generated certificate.
- // * The seventh argument is a pointer to a mutable usize object. It is
- // used to return the actual size of the output certificate.
- // * The eighth argument and the ninth argument are pointers to mutable buffers of size
- // CDI_SIZE. This is fulfilled if the allocation above succeeded.
- // * No pointers are expected to be valid beyond the scope of the function
- // call.
- call_with_input_values(input_values, |input_values| {
- let cert = retry_while_adjusting_output_buffer(|cert, actual_size| {
- check_result(unsafe {
- DiceMainFlow(
- self.get_context(),
- current_cdi_attest.as_ptr(),
- current_cdi_seal.as_ptr(),
- input_values,
- cert.len(),
- cert.as_mut_ptr(),
- actual_size as *mut _,
- next_attest.as_mut_ptr(),
- next_seal.as_mut_ptr(),
- )
- })
- })?;
- Ok((next_attest, next_seal, cert))
- })
- }
-
- /// Safe wrapper around open-dice DiceHash, see open dice
- /// documentation for details.
- fn hash(&mut self, input: &[u8]) -> Result<Vec<u8>> {
- let mut output: Vec<u8> = vec![0; HASH_SIZE];
-
- // SAFETY:
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument and the third argument are the pointer to and length of the given
- // input buffer respectively.
- // * The fourth argument must be a pointer to a mutable buffer of size HASH_SIZE
- // which is fulfilled by the allocation above.
- check_result(unsafe {
- DiceHash(self.get_context(), input.as_ptr(), input.len(), output.as_mut_ptr())
- })?;
- Ok(output)
- }
-
- /// Safe wrapper around open-dice DiceKdf, see open dice
- /// documentation for details.
- fn kdf(&mut self, length: usize, input_key: &[u8], salt: &[u8], info: &[u8]) -> Result<ZVec> {
- let mut output = ZVec::new(length)?;
-
- // SAFETY:
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument is primitive.
- // * The third argument and the fourth argument are the pointer to and length of the given
- // input key.
- // * The fifth argument and the sixth argument are the pointer to and length of the given
- // salt.
- // * The seventh argument and the eighth argument are the pointer to and length of the
- // given info field.
- // * The ninth argument is a pointer to the output buffer which must have the
- // length given by the `length` argument (see second argument). This is
- // fulfilled if the allocation of `output` succeeds.
- // * All pointers must be valid for the duration of the function call, but not
- // longer.
- check_result(unsafe {
- DiceKdf(
- self.get_context(),
- length,
- input_key.as_ptr(),
- input_key.len(),
- salt.as_ptr(),
- salt.len(),
- info.as_ptr(),
- info.len(),
- output.as_mut_ptr(),
- )
- })?;
- Ok(output)
- }
-
- /// Safe wrapper around open-dice DiceKeyPairFromSeed, see open dice
- /// documentation for details.
- fn keypair_from_seed(&mut self, seed: &[u8; PRIVATE_KEY_SEED_SIZE]) -> Result<(Vec<u8>, ZVec)> {
- 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<T: InputValues>(
- &mut self,
- subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
- authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
- input_values: &T,
- ) -> Result<Vec<u8>> {
- // SAFETY (DiceMainFlow):
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument and the third argument are const arrays of size
- // `PRIVATE_KEY_SEED_SIZE`. This is fulfilled as per the definition of the arguments.
- // * The fourth argument is a pointer to `DiceInputValues` it, and its indirect
- // references must be valid for the duration of the function call which
- // is guaranteed by `call_with_input_values` which puts `DiceInputValues`
- // on the stack and initializes it from the `input_values` argument which
- // implements the `InputValues` trait.
- // * The fifth argument and the sixth argument are the length of and the pointer to the
- // allocated certificate buffer respectively. They are used to return
- // the generated certificate.
- // * The seventh argument is a pointer to a mutable usize object. It is
- // used to return the actual size of the output certificate.
- // * All pointers must be valid for the duration of the function call but not beyond.
- call_with_input_values(input_values, |input_values| {
- let cert = retry_while_adjusting_output_buffer(|cert, actual_size| {
- check_result(unsafe {
- DiceGenerateCertificate(
- self.get_context(),
- subject_private_key_seed.as_ptr(),
- authority_private_key_seed.as_ptr(),
- input_values,
- cert.len(),
- cert.as_mut_ptr(),
- actual_size as *mut _,
- )
- })
- })?;
- Ok(cert)
- })
- }
-
- /// Safe wrapper around open-dice BccDiceMainFlow, see open dice
- /// documentation for details.
- /// Returns a tuple of:
- /// * The next attestation CDI,
- /// * the next seal CDI, and
- /// * the next bcc adding the new certificate to the given bcc.
- /// `(next_attest_cdi, next_seal_cdi, next_bcc)`
- fn bcc_main_flow<T: InputValues + ?Sized>(
- &mut self,
- current_cdi_attest: &[u8; CDI_SIZE],
- current_cdi_seal: &[u8; CDI_SIZE],
- bcc: &[u8],
- input_values: &T,
- ) -> Result<(CdiAttest, CdiSeal, Bcc)> {
- let mut next_attest = CdiAttest::new(CDI_SIZE)?;
- let mut next_seal = CdiSeal::new(CDI_SIZE)?;
-
- // SAFETY (BccMainFlow):
- // * The first context argument may be NULL and is unused by the wrapped
- // implementation.
- // * The second argument and the third argument are const arrays of size CDI_SIZE.
- // This is fulfilled as per the definition of the arguments `current_cdi_attest`
- // and `current_cdi_seal`.
- // * The fourth argument and the fifth argument are the pointer to and size of the buffer
- // holding the current bcc.
- // * The sixth argument is a pointer to `DiceInputValues` it, and its indirect
- // references must be valid for the duration of the function call which
- // is guaranteed by `call_with_input_values` which puts `DiceInputValues`
- // on the stack and initializes it from the `input_values` argument which
- // implements the `InputValues` trait.
- // * The seventh argument and the eighth argument are the length of and the pointer to the
- // allocated certificate buffer respectively. They are used to return the generated
- // certificate.
- // * The ninth argument is a pointer to a mutable usize object. It is
- // used to return the actual size of the output certificate.
- // * The tenth argument and the eleventh argument are pointers to mutable buffers of
- // size CDI_SIZE. This is fulfilled if the allocation above succeeded.
- // * No pointers are expected to be valid beyond the scope of the function
- // call.
- call_with_input_values(input_values, |input_values| {
- let next_bcc = retry_while_adjusting_output_buffer(|next_bcc, actual_size| {
- check_result(unsafe {
- BccMainFlow(
- self.get_context(),
- current_cdi_attest.as_ptr(),
- current_cdi_seal.as_ptr(),
- bcc.as_ptr(),
- bcc.len(),
- input_values,
- next_bcc.len(),
- next_bcc.as_mut_ptr(),
- actual_size as *mut _,
- next_attest.as_mut_ptr(),
- next_seal.as_mut_ptr(),
- )
- })
- })?;
- Ok((next_attest, next_seal, next_bcc))
- })
- }
-}
-
-/// This submodule provides additional support for the Boot Certificate Chain (BCC)
-/// specification.
-/// See https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
-pub mod bcc {
- use super::{check_result, retry_while_adjusting_output_buffer, Result};
- use open_dice_bcc_bindgen::{
- BccConfigValues, BccFormatConfigDescriptor, BCC_INPUT_COMPONENT_NAME,
- BCC_INPUT_COMPONENT_VERSION, BCC_INPUT_RESETTABLE,
- };
- use std::ffi::CString;
-
- /// Safe wrapper around BccFormatConfigDescriptor, see open dice documentation for details.
- pub fn format_config_descriptor(
- component_name: Option<&str>,
- component_version: Option<u64>,
- resettable: bool,
- ) -> Result<Vec<u8>> {
- let component_name = match component_name {
- Some(n) => Some(CString::new(n)?),
- None => None,
- };
- let input = BccConfigValues {
- inputs: if component_name.is_some() { BCC_INPUT_COMPONENT_NAME } else { 0 }
- | if component_version.is_some() { BCC_INPUT_COMPONENT_VERSION } else { 0 }
- | if resettable { BCC_INPUT_RESETTABLE } else { 0 },
- // SAFETY: The as_ref() in the line below is vital to keep the component_name object
- // alive. Removing as_ref will move the component_name and the pointer will
- // become invalid after this statement.
- component_name: component_name.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
- component_version: component_version.unwrap_or(0),
- };
-
- // SAFETY:
- // * The first argument is a pointer to the BccConfigValues input assembled above.
- // It and its indirections must be valid for the duration of the function call.
- // * The second argument and the third argument are the length of and the pointer to the
- // allocated output buffer respectively. The buffer must be at least as long
- // as indicated by the size argument.
- // * The forth argument is a pointer to the actual size returned by the function.
- // * All pointers must be valid for the duration of the function call but not beyond.
- retry_while_adjusting_output_buffer(|config_descriptor, actual_size| {
- check_result(unsafe {
- BccFormatConfigDescriptor(
- &input as *const BccConfigValues,
- config_descriptor.len(),
- config_descriptor.as_mut_ptr(),
- actual_size as *mut _,
- )
- })
- })
- }
-}
-
+// 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 = ctx.hash("MySeedString".as_bytes()).unwrap();
- assert_eq!(seed, SEED_TEST_VECTOR);
- let cdi_attest = &seed[..CDI_SIZE];
- assert_eq!(cdi_attest, CDI_ATTEST_TEST_VECTOR);
- let cdi_private_key_seed =
- ctx.derive_cdi_private_key_seed(cdi_attest.try_into().unwrap()).unwrap();
- 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,
@@ -997,41 +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);
- }
-
- static CERT_ID_TEST_VECTOR: &[u8] = &[
- 0x7a, 0x36, 0x45, 0x2c, 0x02, 0xf6, 0x2b, 0xec, 0xf9, 0x80, 0x06, 0x75, 0x87, 0xa5, 0xc1,
- 0x44, 0x0c, 0xd3, 0xc0, 0x6d,
- ];
-
- #[test]
- fn derive_cdi_certificate_id() {
- let mut ctx = OpenDiceCborContext::new();
- let cert_id = ctx.derive_cdi_certificate_id("MyPubKey".as_bytes()).unwrap();
- assert_eq!(&cert_id[..], CERT_ID_TEST_VECTOR);
+ let dice_artifacts = make_sample_bcc_and_cdis().unwrap();
+ assert_eq!(dice_artifacts.cdi_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
deleted file mode 100644
index 3915508..0000000
--- a/diced/src/diced_client_test.rs
+++ /dev/null
@@ -1,188 +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_hardware_security_dice::aidl::android::hardware::security::dice::{
- Config::Config as BinderConfig, InputValues::InputValues as BinderInputValues,
- Mode::Mode as BinderMode,
-};
-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 nix::libc::uid_t;
-use std::convert::TryInto;
-
-static DICE_NODE_SERVICE_NAME: &str = "android.security.dice.IDiceNode";
-static DICE_MAINTENANCE_SERVICE_NAME: &str = "android.security.dice.IDiceMaintenance";
-
-fn get_dice_node() -> Strong<dyn IDiceNode> {
- binder::get_interface(DICE_NODE_SERVICE_NAME).unwrap()
-}
-
-fn get_dice_maintenance() -> Strong<dyn IDiceMaintenance> {
- binder::get_interface(DICE_MAINTENANCE_SERVICE_NAME).unwrap()
-}
-
-static TEST_MESSAGE: &[u8] = &[
- // "My test message!"
- 0x4d, 0x79, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x21,
- 0x0a,
-];
-
-// This test calls derive with an empty argument vector and with a set of three input values.
-// It then performs the same three derivation steps on the result of the former and compares
-// the result to the result of the latter.
-fn equivalence_test() {
- let node = get_dice_node();
- let input_values = diced_sample_inputs::get_input_values_vector();
- let former = node.derive(&[]).expect("Trying to call derive.");
- let latter = node.derive(&input_values).expect("Trying to call derive with input values.");
- let artifacts =
- diced_utils::ResidentArtifacts::new(&former.cdiAttest, &former.cdiSeal, &former.bcc.data)
- .unwrap();
-
- let input_values: Vec<diced_utils::InputValues> =
- input_values.iter().map(|v| v.into()).collect();
-
- let artifacts =
- artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
- let (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,
- )
- .unwrap();
- // TODO when we have a parser/verifier, check equivalence rather
- // than bit by bit equality.
- assert_eq!(latter, from_former);
-}
-
-fn sign_and_verify() {
- let node = get_dice_node();
- let _signature = node.sign(&[], TEST_MESSAGE).expect("Trying to call sign.");
-
- let _bcc = node.getAttestationChain(&[]).expect("Trying to call getAttestationChain.");
- // TODO b/204938506 check the signature with the bcc when the verifier is available.
-}
-
-// This test calls derive with an empty argument vector, then demotes the itself using
-// a set of three input values, and then calls derive with empty argument vector again.
-// It then performs the same three derivation steps on the result of the former and compares
-// the result to the result of the latter.
-fn demote_test() {
- let node = get_dice_node();
- let input_values = diced_sample_inputs::get_input_values_vector();
- let former = node.derive(&[]).expect("Trying to call derive.");
- node.demote(&input_values).expect("Trying to call demote with input values.");
-
- let latter = node.derive(&[]).expect("Trying to call derive after demote.");
-
- let artifacts = diced_utils::ResidentArtifacts::new(
- former.cdiAttest[..].try_into().unwrap(),
- former.cdiSeal[..].try_into().unwrap(),
- &former.bcc.data,
- )
- .unwrap();
-
- let input_values: Vec<diced_utils::InputValues> =
- input_values.iter().map(|v| v.into()).collect();
-
- let artifacts =
- artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
- let (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,
- )
- .unwrap();
- // TODO b/204938506 when we have a parser/verifier, check equivalence rather
- // than bit by bit equality.
- assert_eq!(latter, from_former);
-}
-
-fn client_input_values(uid: uid_t) -> BinderInputValues {
- BinderInputValues {
- codeHash: [0; dice::HASH_SIZE],
- config: BinderConfig {
- desc: dice::bcc::format_config_descriptor(Some(&format!("{}", uid)), None, true)
- .unwrap(),
- },
- authorityHash: [0; dice::HASH_SIZE],
- authorityDescriptor: None,
- mode: BinderMode::NORMAL,
- hidden: [0; dice::HIDDEN_SIZE],
- }
-}
-
-// This test calls derive with an empty argument vector `former` which look like this:
-// <common root> | <caller>
-// It then demotes diced using a set of three input values prefixed with the uid based input
-// values that diced would add to any call. It then calls derive with empty argument vector
-// again which will add another step using the identity of the caller. If diced was demoted
-// correctly the chain of `latter` will
-// look as follows:
-// <common root> | <caller> | <the three sample inputs> | <caller>
-//
-// It then performs the same three derivation steps followed by a set of caller input values
-// on `former` and compares it to `latter`.
-fn demote_self_test() {
- let maintenance = get_dice_maintenance();
- let node = get_dice_node();
- let input_values = diced_sample_inputs::get_input_values_vector();
- let former = node.derive(&[]).expect("Trying to call derive.");
-
- let client = client_input_values(nix::unistd::getuid().into());
-
- let mut demote_vector = vec![client.clone()];
- demote_vector.append(&mut input_values.clone());
- maintenance.demoteSelf(&demote_vector).expect("Trying to call demote_self with input values.");
-
- let latter = node.derive(&[]).expect("Trying to call derive after demote.");
-
- let artifacts = diced_utils::ResidentArtifacts::new(
- former.cdiAttest[..].try_into().unwrap(),
- former.cdiSeal[..].try_into().unwrap(),
- &former.bcc.data,
- )
- .unwrap();
-
- let client = [client];
- let input_values: Vec<diced_utils::InputValues> =
- input_values.iter().chain(client.iter()).map(|v| v.into()).collect();
-
- let artifacts =
- artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
- let (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,
- )
- .unwrap();
- // TODO b/204938506 when we have a parser/verifier, check equivalence rather
- // than bit by bit equality.
- assert_eq!(latter, from_former);
-}
-
-#[test]
-fn run_serialized_test() {
- equivalence_test();
- sign_and_verify();
- // The demote self test must run before the demote test or the test fails.
- // And since demotion is not reversible the test can only pass once per boot.
- demote_self_test();
- demote_test();
-}
diff --git a/diced/src/diced_main.rs b/diced/src/diced_main.rs
deleted file mode 100644
index c2cf02c..0000000
--- a/diced/src/diced_main.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2021, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Main entry point for diced, the friendly neighborhood DICE service.
-
-use binder::get_interface;
-use diced::{DiceMaintenance, DiceNode, DiceNodeImpl, ProxyNodeHal, ResidentNode};
-use std::convert::TryInto;
-use std::panic;
-use std::sync::Arc;
-
-static DICE_NODE_SERVICE_NAME: &str = "android.security.dice.IDiceNode";
-static DICE_MAINTENANCE_SERVICE_NAME: &str = "android.security.dice.IDiceMaintenance";
-static DICE_HAL_SERVICE_NAME: &str = "android.hardware.security.dice.IDiceDevice/default";
-
-fn main() {
- android_logger::init_once(
- android_logger::Config::default().with_tag("diced").with_min_level(log::Level::Debug),
- );
- // Redirect panic messages to logcat.
- panic::set_hook(Box::new(|panic_info| {
- log::error!("{}", panic_info);
- }));
-
- // Saying hi.
- log::info!("Diced, your friendly neighborhood DICE service, is starting.");
-
- let node_impl: Arc<dyn DiceNodeImpl + Send + Sync> = match get_interface(DICE_HAL_SERVICE_NAME)
- {
- Ok(dice_device) => {
- Arc::new(ProxyNodeHal::new(dice_device).expect("Failed to construct a proxy node."))
- }
- Err(e) => {
- log::warn!("Failed to connect to DICE HAL: {:?}", e);
- log::warn!("Using sample dice artifacts.");
- let (cdi_attest, cdi_seal, bcc) = diced_sample_inputs::make_sample_bcc_and_cdis()
- .expect("Failed to create sample dice artifacts.");
- Arc::new(
- ResidentNode::new(
- cdi_attest[..]
- .try_into()
- .expect("Failed to convert cdi_attest into array ref."),
- cdi_seal[..].try_into().expect("Failed to convert cdi_seal into array ref."),
- bcc,
- )
- .expect("Failed to construct a resident node."),
- )
- }
- };
-
- let node = DiceNode::new_as_binder(node_impl.clone())
- .expect("Failed to create IDiceNode service instance.");
-
- let maintenance = DiceMaintenance::new_as_binder(node_impl)
- .expect("Failed to create IDiceMaintenance service instance.");
-
- binder::add_service(DICE_NODE_SERVICE_NAME, node.as_binder())
- .expect("Failed to register IDiceNode Service");
-
- binder::add_service(DICE_MAINTENANCE_SERVICE_NAME, maintenance.as_binder())
- .expect("Failed to register IDiceMaintenance Service");
-
- log::info!("Joining thread pool now.");
- binder::ProcessState::join_thread_pool();
-}
diff --git a/diced/src/error.rs b/diced/src/error.rs
deleted file mode 100644
index 3e230e4..0000000
--- a/diced/src/error.rs
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2021, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use android_security_dice::aidl::android::security::dice::ResponseCode::ResponseCode;
-use anyhow::Result;
-use binder::{ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode};
-use keystore2_selinux as selinux;
-use std::ffi::CString;
-
-/// This is the main Diced error type. It wraps the Diced `ResponseCode` generated
-/// from AIDL in the `Rc` variant and Binder and BinderTransaction errors in the respective
-/// variants.
-#[allow(dead_code)] // Binder error forwarding will be needed when proxy nodes are implemented.
-#[derive(Debug, thiserror::Error, Eq, PartialEq, Clone)]
-pub enum Error {
- /// Wraps a dice `ResponseCode` as defined by the android.security.dice AIDL interface
- /// specification.
- #[error("Error::Rc({0:?})")]
- Rc(ResponseCode),
- /// Wraps a Binder exception code other than a service specific exception.
- #[error("Binder exception code {0:?}, {1:?}")]
- Binder(ExceptionCode, i32),
- /// Wraps a Binder status code.
- #[error("Binder transaction error {0:?}")]
- BinderTransaction(StatusCode),
-}
-
-/// This function should be used by dice service calls to translate error conditions
-/// into service specific exceptions.
-///
-/// All error conditions get logged by this function.
-///
-/// All `Error::Rc(x)` variants get mapped onto a service specific error code of x.
-/// `selinux::Error::PermissionDenied` is mapped on `ResponseCode::PERMISSION_DENIED`.
-///
-/// All non `Error` error conditions and the Error::Binder variant get mapped onto
-/// ResponseCode::SYSTEM_ERROR`.
-///
-/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
-/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
-/// typically returns Ok(value).
-///
-/// # Examples
-///
-/// ```
-/// fn do_something() -> anyhow::Result<Vec<u8>> {
-/// Err(anyhow!(Error::Rc(ResponseCode::NOT_IMPLEMENTED)))
-/// }
-///
-/// map_or_log_err(do_something(), Ok)
-/// ```
-pub fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
-where
- F: FnOnce(U) -> BinderResult<T>,
-{
- map_err_with(
- result,
- |e| {
- log::error!("{:?}", e);
- e
- },
- handle_ok,
- )
-}
-
-/// This function behaves similar to map_or_log_error, but it does not log the errors, instead
-/// it calls map_err on the error before mapping it to a binder result allowing callers to
-/// log or transform the error before mapping it.
-fn map_err_with<T, U, F1, F2>(result: Result<U>, map_err: F1, handle_ok: F2) -> BinderResult<T>
-where
- F1: FnOnce(anyhow::Error) -> anyhow::Error,
- F2: FnOnce(U) -> BinderResult<T>,
-{
- result.map_or_else(
- |e| {
- let e = map_err(e);
- let msg = match CString::new(format!("{:?}", e)) {
- Ok(msg) => Some(msg),
- Err(_) => {
- log::warn!(
- "Cannot convert error message to CStr. It contained a nul byte.
- Omitting message from service specific error."
- );
- None
- }
- };
- let rc = get_error_code(&e);
- Err(BinderStatus::new_service_specific_error(rc, msg.as_deref()))
- },
- handle_ok,
- )
-}
-
-/// Extracts the error code from an `anyhow::Error` mapping any error that does not have a
-/// root cause of `Error::Rc` onto `ResponseCode::SYSTEM_ERROR` and to `e` with `Error::Rc(e)`
-/// otherwise.
-fn get_error_code(e: &anyhow::Error) -> i32 {
- let root_cause = e.root_cause();
- match root_cause.downcast_ref::<Error>() {
- Some(Error::Rc(rcode)) => rcode.0,
- // If an Error::Binder reaches this stage we report a system error.
- // The exception code and possible service specific error will be
- // printed in the error log above.
- Some(Error::Binder(_, _)) | Some(Error::BinderTransaction(_)) => {
- ResponseCode::SYSTEM_ERROR.0
- }
- None => match root_cause.downcast_ref::<selinux::Error>() {
- Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
- _ => ResponseCode::SYSTEM_ERROR.0,
- },
- }
-}
diff --git a/diced/src/error_vendor.rs b/diced/src/error_vendor.rs
deleted file mode 100644
index e8657e0..0000000
--- a/diced/src/error_vendor.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.
-
-use android_hardware_security_dice::aidl::android::hardware::security::dice::ResponseCode::ResponseCode;
-use anyhow::Result;
-use binder::{ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode};
-use std::ffi::CString;
-
-/// This is the error type for DICE HAL implementations. It wraps
-/// `android::hardware::security::dice::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 Keystore 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 => ResponseCode::SYSTEM_ERROR.0,
- }
-}
diff --git a/diced/src/hal_node.rs b/diced/src/hal_node.rs
deleted file mode 100644
index 69cf4ac..0000000
--- a/diced/src/hal_node.rs
+++ /dev/null
@@ -1,725 +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 module provides `ResidentHal`, an implementation of a IDiceDevice HAL Interface.
-//! While the name implies that the DICE secrets are memory resident, the residency
-//! is augmented by the implementation of the traits `DiceArtifacts` and
-//! `UpdatableDiceArtifacts`. The implementation outsources all operations that
-//! involve the DICE secrets to a short lived child process. By implementing
-//! `UpdatableDiceArtifacts` accordingly, integrators can limit the exposure of
-//! the resident DICE secrets to user space memory. E.g., an implementation might only
-//! hold a path to a securefs file allowing the child to read and update the kernel state
-//! through this path directly.
-//!
-//! ## Important Safety Note.
-//! The module is not safe to use in multi threaded processes. It uses fork and runs
-//! code that is not async signal safe in the child. Implementing a HAL service without
-//! starting a thread pool is safe, but no secondary thread must be created.
-
-use crate::error_vendor::map_or_log_err;
-use android_hardware_security_dice::aidl::android::hardware::security::dice::{
- Bcc::Bcc, BccHandover::BccHandover, IDiceDevice::BnDiceDevice, IDiceDevice::IDiceDevice,
- InputValues::InputValues as BinderInputValues, Signature::Signature,
-};
-use anyhow::{Context, Result};
-use binder::{BinderFeatures, Result as BinderResult, Strong};
-use dice::{ContextImpl, OpenDiceCborContext};
-use diced_open_dice_cbor as dice;
-use diced_utils as utils;
-use nix::sys::wait::{waitpid, WaitStatus};
-use nix::unistd::{
- close, fork, pipe as nix_pipe, read as nix_read, write as nix_write, ForkResult,
-};
-use serde::{de::DeserializeOwned, Deserialize, Serialize};
-use std::convert::TryInto;
-use std::io::{Read, Write};
-use std::os::unix::io::RawFd;
-use std::sync::{Arc, RwLock};
-use utils::ResidentArtifacts;
-pub use utils::{DiceArtifacts, 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
-/// reads from the pipe into an expending vector, until no more data can be read.
-struct PipeReader(RawFd);
-
-impl Read for PipeReader {
- fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
- let bytes = nix_read(self.0, buf)?;
- Ok(bytes)
- }
-}
-
-impl Drop for PipeReader {
- fn drop(&mut self) {
- close(self.0).expect("Failed to close reader pipe fd.");
- }
-}
-
-/// PipeWriter is a simple wrapper around raw pipe file descriptors.
-/// It takes ownership of the file descriptor and closes it on drop. It provides `write`, which
-/// writes the given buffer into the pipe, returning the number of bytes written.
-struct PipeWriter(RawFd);
-
-impl Write for PipeWriter {
- fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
- let written = nix_write(self.0, buf)?;
- Ok(written)
- }
-
- fn flush(&mut self) -> std::io::Result<()> {
- // Flush is a NO-OP.
- Ok(())
- }
-}
-
-impl Drop for PipeWriter {
- fn drop(&mut self) {
- close(self.0).expect("Failed to close writer pipe fd.");
- }
-}
-
-fn pipe() -> Result<(PipeReader, PipeWriter), nix::Error> {
- let (read_fd, write_fd) = nix_pipe()?;
- Ok((PipeReader(read_fd), PipeWriter(write_fd)))
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, thiserror::Error)]
-enum RunForkedError {
- #[error("RunForkedError::String({0:?})")]
- String(String),
-}
-
-/// Run the given closure in a new process.
-/// Safety: The function runs code that is not async-signal-safe in the child after forking.
-/// This means, that this function must not be called by a multi threaded process.
-fn run_forked<F, R>(f: F) -> Result<R>
-where
- R: Serialize + DeserializeOwned,
- F: FnOnce() -> Result<R>,
-{
- let (reader, writer) = pipe().expect("Failed to create pipe.");
-
- match unsafe { fork() } {
- Ok(ForkResult::Parent { child, .. }) => {
- drop(writer);
- let status = waitpid(child, None).expect("Failed while waiting for child.");
- if let WaitStatus::Exited(_, 0) = status {
- // Child exited successfully.
- // Read the result from the pipe.
- // Deserialize the result and return it.
- let result: Result<R, RunForkedError> =
- serde_cbor::from_reader(reader).expect("Failed to deserialize result.");
-
- result.context("In run_forked:")
- } else {
- panic!("Child did not exit as expected {:?}", status);
- }
- }
- Ok(ForkResult::Child) => {
- // Run the closure.
- let result = f()
- .map_err(|err| RunForkedError::String(format! {"Nested anyhow error {:?}", err}));
-
- // Serialize the result of the closure.
- serde_cbor::to_writer(writer, &result).expect("Result serialization failed");
-
- // Set exit status to `0`.
- std::process::exit(0);
- }
- Err(errno) => {
- panic!("Failed to fork: {:?}", errno);
- }
- }
-}
-
-/// A DiceHal backend implementation.
-/// All functions, except `demote`, derive effective dice artifacts starting from
-/// this node and iterating through `input_values` in ascending order.
-pub trait DiceHalImpl {
- /// Signs the message using the effective dice artifacts and Ed25519Pure.
- fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> Result<Signature>;
- /// Returns the effective attestation chain.
- fn get_attestation_chain(&self, input_values: &[BinderInputValues]) -> Result<Bcc>;
- /// Returns the effective dice artifacts.
- fn derive(&self, input_values: &[BinderInputValues]) -> Result<BccHandover>;
- /// This demotes the implementation itself. I.e. a resident node would replace its resident
- /// artifacts 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, input_values: &[BinderInputValues]) -> Result<()>;
-}
-
-/// The ResidentHal implements a IDiceDevice backend with memory resident DICE secrets.
-pub struct ResidentHal<T: UpdatableDiceArtifacts + Serialize + DeserializeOwned + Clone + Send> {
- artifacts: RwLock<T>,
-}
-
-impl<T: UpdatableDiceArtifacts + Serialize + DeserializeOwned + Clone + Send> ResidentHal<T> {
- /// Creates a new Resident node with the given dice secrets and certificate chain.
- /// ## Safety
- /// It is not safe to use implementations of ResidentHal in multi threaded environments.
- /// If using this library to implement a HAL service make sure not to start a thread pool.
- pub unsafe fn new(artifacts: T) -> Result<Self> {
- Ok(ResidentHal { artifacts: RwLock::new(artifacts) })
- }
-
- fn with_effective_artifacts<R, F>(&self, input_values: &[BinderInputValues], f: F) -> Result<R>
- where
- R: Serialize + DeserializeOwned,
- F: FnOnce(ResidentArtifacts) -> Result<R>,
- {
- let artifacts = self.artifacts.read().unwrap().clone();
-
- // Safety: run_forked must not be be called by a multi threaded process.
- // This requirement is propagated to the public interface of this module through
- // `ResidentHal::new`
- run_forked(move || {
- let artifacts = artifacts.with_artifacts(|a| ResidentArtifacts::new_from(a))?;
- let input_values: Vec<utils::InputValues> =
- input_values.iter().map(|v| v.into()).collect();
- let artifacts = artifacts
- .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
- .context("In ResidentHal::get_effective_artifacts:")?;
- f(artifacts)
- })
- }
-}
-
-impl<T: UpdatableDiceArtifacts + Serialize + DeserializeOwned + Clone + Send> DiceHalImpl
- for ResidentHal<T>
-{
- fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> Result<Signature> {
- 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!(
- "In ResidentHal::sign: Failed to convert cdi_attest (length: {}).",
- cdi_attest.len()
- )
- })?)
- .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()
- )
- })?)
- .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.")
- })
- .context("In ResidentHal::sign:")?;
- Ok(Signature { data: signature })
- }
-
- fn get_attestation_chain(&self, input_values: &[BinderInputValues]) -> Result<Bcc> {
- let bcc = self
- .with_effective_artifacts(input_values, |artifacts| {
- let (_, _, bcc) = artifacts.into_tuple();
- Ok(bcc)
- })
- .context("In ResidentHal::get_attestation_chain: Failed to get effective_artifacts.")?;
-
- Ok(Bcc { data: bcc })
- }
-
- fn derive(&self, input_values: &[BinderInputValues]) -> Result<BccHandover> {
- let (cdi_attest, cdi_seal, bcc): (Vec<u8>, Vec<u8>, Vec<u8>) = self
- .with_effective_artifacts(input_values, |artifacts| {
- let (cdi_attest, cdi_seal, bcc) = artifacts.into_tuple();
- Ok((cdi_attest[..].to_vec(), cdi_seal[..].to_vec(), bcc))
- })?;
-
- utils::make_bcc_handover(
- &cdi_attest
- .as_slice()
- .try_into()
- .context("In ResidentHal::derive: Trying to convert cdi_attest to sized array.")?,
- &cdi_seal
- .as_slice()
- .try_into()
- .context("In ResidentHal::derive: Trying to convert cdi_seal to sized array.")?,
- &bcc,
- )
- .context("In ResidentHal::derive: Trying to construct BccHandover.")
- }
-
- fn demote(&self, input_values: &[BinderInputValues]) -> Result<()> {
- let mut artifacts = self.artifacts.write().unwrap();
-
- let artifacts_clone = (*artifacts).clone();
-
- // Safety: run_forked may not be called from a multi threaded process.
- // This requirement is propagated to the public interface of this module through
- // `ResidentHal::new`
- *artifacts = run_forked(|| {
- let new_artifacts =
- artifacts_clone.with_artifacts(|a| ResidentArtifacts::new_from(a))?;
- let input_values: Vec<utils::InputValues> =
- input_values.iter().map(|v| v.into()).collect();
-
- let new_artifacts = new_artifacts
- .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
- .context("In ResidentHal::get_effective_artifacts:")?;
- artifacts_clone.update(&new_artifacts)
- })?;
-
- Ok(())
- }
-}
-
-/// Implements android.hardware.security.dice.IDiceDevice. Forwards public API calls
-/// to the given DiceHalImpl backend.
-pub struct DiceDevice {
- hal_impl: Arc<dyn DiceHalImpl + Sync + Send>,
-}
-
-impl DiceDevice {
- /// Constructs an instance of DiceDevice, wraps it with a BnDiceDevice 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(
- hal_impl: Arc<dyn DiceHalImpl + Sync + Send>,
- ) -> Result<Strong<dyn IDiceDevice>> {
- let result = BnDiceDevice::new_binder(DiceDevice { hal_impl }, BinderFeatures::default());
- Ok(result)
- }
-}
-
-impl binder::Interface for DiceDevice {}
-
-impl IDiceDevice for DiceDevice {
- fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> BinderResult<Signature> {
- map_or_log_err(self.hal_impl.sign(input_values, message), Ok)
- }
- fn getAttestationChain(&self, input_values: &[BinderInputValues]) -> BinderResult<Bcc> {
- map_or_log_err(self.hal_impl.get_attestation_chain(input_values), Ok)
- }
- fn derive(&self, input_values: &[BinderInputValues]) -> BinderResult<BccHandover> {
- map_or_log_err(self.hal_impl.derive(input_values), Ok)
- }
- fn demote(&self, input_values: &[BinderInputValues]) -> BinderResult<()> {
- map_or_log_err(self.hal_impl.demote(input_values), Ok)
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
- use android_hardware_security_dice::aidl::android::hardware::security::dice::{
- 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 diced_sample_inputs;
- use diced_utils as utils;
-
- #[derive(Debug, Serialize, Deserialize, Clone)]
- struct InsecureSerializableArtifacts {
- cdi_attest: [u8; dice::CDI_SIZE],
- cdi_seal: [u8; dice::CDI_SIZE],
- bcc: Vec<u8>,
- }
-
- impl DiceArtifacts for InsecureSerializableArtifacts {
- fn cdi_attest(&self) -> &[u8; dice::CDI_SIZE] {
- &self.cdi_attest
- }
- fn cdi_seal(&self) -> &[u8; dice::CDI_SIZE] {
- &self.cdi_seal
- }
- fn bcc(&self) -> Vec<u8> {
- self.bcc.clone()
- }
- }
-
- impl UpdatableDiceArtifacts for InsecureSerializableArtifacts {
- fn with_artifacts<F, T>(&self, f: F) -> Result<T>
- where
- F: FnOnce(&dyn DiceArtifacts) -> Result<T>,
- {
- f(self)
- }
- fn update(self, new_artifacts: &impl DiceArtifacts) -> Result<Self> {
- Ok(Self {
- cdi_attest: *new_artifacts.cdi_attest(),
- cdi_seal: *new_artifacts.cdi_seal(),
- bcc: new_artifacts.bcc(),
- })
- }
- }
-
- fn make_input_values(
- code: &str,
- config_name: &str,
- authority: &str,
- ) -> Result<BinderInputValues> {
- let mut dice_ctx = dice::OpenDiceCborContext::new();
- Ok(BinderInputValues {
- codeHash: dice_ctx
- .hash(code.as_bytes())
- .context("In make_input_values: code hash failed.")?
- .as_slice()
- .try_into()?,
- config: BinderConfig {
- desc: dice::bcc::format_config_descriptor(Some(config_name), None, true)
- .context("In make_input_values: Failed to format config descriptor.")?,
- },
- authorityHash: dice_ctx
- .hash(authority.as_bytes())
- .context("In make_input_values: authority hash failed.")?
- .as_slice()
- .try_into()?,
- authorityDescriptor: None,
- mode: BinderMode::NORMAL,
- hidden: [0; dice::HIDDEN_SIZE],
- })
- }
-
- /// 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 input_values = &[
- make_input_values("component 1 code", "component 1", "component 1 authority")?,
- make_input_values("component 2 code", "component 2", "component 2 authority")?,
- make_input_values("component 3 code", "component 3", "component 3 authority")?,
- ];
-
- let input_values: Vec<utils::InputValues> = input_values.iter().map(|v| v.into()).collect();
-
- let new_artifacts =
- artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))?;
-
- let result = utils::make_bcc_handover(
- new_artifacts.cdi_attest(),
- new_artifacts.cdi_seal(),
- &new_artifacts.bcc(),
- )?;
-
- assert_eq!(result, make_derive_test_vector());
- Ok(())
- }
-
- /// Test the ResidentHal hal implementation which performs the derivation in a separate
- /// process and returns the result through a pipe. This test compares the result against
- /// 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()?;
-
- // 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 bcc_handover = hal_impl
- .derive(&[
- make_input_values("component 1 code", "component 1", "component 1 authority")?,
- make_input_values("component 2 code", "component 2", "component 2 authority")?,
- make_input_values("component 3 code", "component 3", "component 3 authority")?,
- ])
- .expect("Failed to derive artifacts.");
-
- assert_eq!(bcc_handover, make_derive_test_vector());
- Ok(())
- }
-
- /// Demoting the implementation two steps and then performing one step of child derivation
- /// 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()?;
-
- // 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.");
-
- hal_impl
- .demote(&[
- make_input_values("component 1 code", "component 1", "component 1 authority")?,
- make_input_values("component 2 code", "component 2", "component 2 authority")?,
- ])
- .expect("Failed to demote implementation.");
-
- let bcc_handover = hal_impl
- .derive(&[make_input_values(
- "component 3 code",
- "component 3",
- "component 3 authority",
- )?])
- .expect("Failed to derive artifacts.");
-
- assert_eq!(bcc_handover, make_derive_test_vector());
- Ok(())
- }
-
- fn make_derive_test_vector() -> BccHandover {
- utils::make_bcc_handover(
- &[
- // cdi_attest
- 0x8f, 0xdf, 0x93, 0x67, 0xd7, 0x0e, 0xf8, 0xb8, 0xd2, 0x9c, 0x30, 0xeb, 0x4e, 0x9b,
- 0x71, 0x5f, 0x9a, 0x5b, 0x67, 0xa6, 0x29, 0xe0, 0x00, 0x9b, 0x4d, 0xe6, 0x95, 0xcf,
- 0xf9, 0xed, 0x5e, 0x9b,
- ],
- &[
- // cdi_seal
- 0x15, 0x3e, 0xd6, 0x30, 0x5a, 0x8d, 0x4b, 0x6f, 0x07, 0x3f, 0x5d, 0x89, 0xc5, 0x6e,
- 0x30, 0xba, 0x05, 0x56, 0xfc, 0x66, 0xf4, 0xae, 0xce, 0x7f, 0x81, 0xb9, 0xc5, 0x21,
- 0x9b, 0x49, 0x3d, 0xe1,
- ],
- &[
- // bcc
- 0x87, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20,
- 0x3e, 0x85, 0xe5, 0x72, 0x75, 0x55, 0xe5, 0x1e, 0xe7, 0xf3, 0x35, 0x94, 0x8e, 0xbb,
- 0xbd, 0x74, 0x1e, 0x1d, 0xca, 0x49, 0x9c, 0x97, 0x39, 0x77, 0x06, 0xd3, 0xc8, 0x6e,
- 0x8b, 0xd7, 0x33, 0xf9, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8a, 0xa9,
- 0x01, 0x78, 0x28, 0x34, 0x32, 0x64, 0x38, 0x38, 0x36, 0x34, 0x66, 0x39, 0x37, 0x62,
- 0x36, 0x35, 0x34, 0x37, 0x61, 0x35, 0x30, 0x63, 0x31, 0x65, 0x30, 0x61, 0x37, 0x34,
- 0x39, 0x66, 0x38, 0x65, 0x66, 0x38, 0x62, 0x38, 0x31, 0x65, 0x63, 0x36, 0x32, 0x61,
- 0x66, 0x02, 0x78, 0x28, 0x31, 0x66, 0x36, 0x39, 0x36, 0x66, 0x30, 0x37, 0x32, 0x35,
- 0x32, 0x66, 0x32, 0x39, 0x65, 0x39, 0x33, 0x66, 0x65, 0x34, 0x64, 0x65, 0x31, 0x39,
- 0x65, 0x65, 0x33, 0x32, 0x63, 0x64, 0x38, 0x31, 0x64, 0x63, 0x34, 0x30, 0x34, 0x65,
- 0x37, 0x36, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0x16, 0x48, 0xf2, 0x55, 0x53,
- 0x23, 0xdd, 0x15, 0x2e, 0x83, 0x38, 0xc3, 0x64, 0x38, 0x63, 0x26, 0x0f, 0xcf, 0x5b,
- 0xd1, 0x3a, 0xd3, 0x40, 0x3e, 0x23, 0xf8, 0x34, 0x4c, 0x6d, 0xa2, 0xbe, 0x25, 0x1c,
- 0xb0, 0x29, 0xe8, 0xc3, 0xfb, 0xb8, 0x80, 0xdc, 0xb1, 0xd2, 0xb3, 0x91, 0x4d, 0xd3,
- 0xfb, 0x01, 0x0f, 0xe4, 0xe9, 0x46, 0xa2, 0xc0, 0x26, 0x57, 0x5a, 0xba, 0x30, 0xf7,
- 0x15, 0x98, 0x14, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x56, 0xa3, 0x3a, 0x00, 0x01, 0x11,
- 0x71, 0x63, 0x41, 0x42, 0x4c, 0x3a, 0x00, 0x01, 0x11, 0x72, 0x01, 0x3a, 0x00, 0x01,
- 0x11, 0x73, 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0x47, 0xae, 0x42, 0x27,
- 0x4c, 0xcb, 0x65, 0x4d, 0xee, 0x74, 0x2d, 0x05, 0x78, 0x2a, 0x08, 0x2a, 0xa5, 0xf0,
- 0xcf, 0xea, 0x3e, 0x60, 0xee, 0x97, 0x11, 0x4b, 0x5b, 0xe6, 0x05, 0x0c, 0xe8, 0x90,
- 0xf5, 0x22, 0xc4, 0xc6, 0x67, 0x7a, 0x22, 0x27, 0x17, 0xb3, 0x79, 0xcc, 0x37, 0x64,
- 0x5e, 0x19, 0x4f, 0x96, 0x37, 0x67, 0x3c, 0xd0, 0xc5, 0xed, 0x0f, 0xdd, 0xe7, 0x2e,
- 0x4f, 0x70, 0x97, 0x30, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0xf9, 0x00, 0x9d,
- 0xc2, 0x59, 0x09, 0xe0, 0xb6, 0x98, 0xbd, 0xe3, 0x97, 0x4a, 0xcb, 0x3c, 0xe7, 0x6b,
- 0x24, 0xc3, 0xe4, 0x98, 0xdd, 0xa9, 0x6a, 0x41, 0x59, 0x15, 0xb1, 0x23, 0xe6, 0xc8,
- 0xdf, 0xfb, 0x52, 0xb4, 0x52, 0xc1, 0xb9, 0x61, 0xdd, 0xbc, 0x5b, 0x37, 0x0e, 0x12,
- 0x12, 0xb2, 0xfd, 0xc1, 0x09, 0xb0, 0xcf, 0x33, 0x81, 0x4c, 0xc6, 0x29, 0x1b, 0x99,
- 0xea, 0xae, 0xfd, 0xaa, 0x0d, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a, 0x00,
- 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20,
- 0x06, 0x21, 0x58, 0x20, 0xb1, 0x02, 0xcc, 0x2c, 0xb2, 0x6a, 0x3b, 0xe9, 0xc1, 0xd3,
- 0x95, 0x10, 0xa0, 0xe1, 0xff, 0x51, 0xde, 0x57, 0xd5, 0x65, 0x28, 0xfd, 0x7f, 0xeb,
- 0xd4, 0xca, 0x15, 0xf3, 0xca, 0xdf, 0x37, 0x88, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41,
- 0x20, 0x58, 0x40, 0x58, 0xd8, 0x03, 0x24, 0x53, 0x60, 0x57, 0xa9, 0x09, 0xfa, 0xab,
- 0xdc, 0x57, 0x1e, 0xf0, 0xe5, 0x1e, 0x51, 0x6f, 0x9e, 0xa3, 0x42, 0xe6, 0x6a, 0x8c,
- 0xaa, 0xad, 0x08, 0x48, 0xde, 0x7f, 0x4f, 0x6e, 0x2f, 0x7f, 0x39, 0x6c, 0xa1, 0xf8,
- 0x42, 0x71, 0xfe, 0x17, 0x3d, 0xca, 0x31, 0x83, 0x92, 0xed, 0xbb, 0x40, 0xb8, 0x10,
- 0xe0, 0xf2, 0x5a, 0x99, 0x53, 0x38, 0x46, 0x33, 0x97, 0x78, 0x05, 0x84, 0x43, 0xa1,
- 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8a, 0xa9, 0x01, 0x78, 0x28, 0x31, 0x66, 0x36, 0x39,
- 0x36, 0x66, 0x30, 0x37, 0x32, 0x35, 0x32, 0x66, 0x32, 0x39, 0x65, 0x39, 0x33, 0x66,
- 0x65, 0x34, 0x64, 0x65, 0x31, 0x39, 0x65, 0x65, 0x33, 0x32, 0x63, 0x64, 0x38, 0x31,
- 0x64, 0x63, 0x34, 0x30, 0x34, 0x65, 0x37, 0x36, 0x02, 0x78, 0x28, 0x32, 0x35, 0x39,
- 0x34, 0x38, 0x39, 0x65, 0x36, 0x39, 0x37, 0x34, 0x38, 0x37, 0x30, 0x35, 0x64, 0x65,
- 0x33, 0x65, 0x32, 0x66, 0x34, 0x34, 0x32, 0x36, 0x37, 0x65, 0x61, 0x34, 0x39, 0x33,
- 0x38, 0x66, 0x66, 0x36, 0x61, 0x35, 0x37, 0x32, 0x35, 0x3a, 0x00, 0x47, 0x44, 0x50,
- 0x58, 0x40, 0xa4, 0x0c, 0xcb, 0xc1, 0xbf, 0xfa, 0xcc, 0xfd, 0xeb, 0xf4, 0xfc, 0x43,
- 0x83, 0x7f, 0x46, 0x8d, 0xd8, 0xd8, 0x14, 0xc1, 0x96, 0x14, 0x1f, 0x6e, 0xb3, 0xa0,
- 0xd9, 0x56, 0xb3, 0xbf, 0x2f, 0xfa, 0x88, 0x70, 0x11, 0x07, 0x39, 0xa4, 0xd2, 0xa9,
- 0x6b, 0x18, 0x28, 0xe8, 0x29, 0x20, 0x49, 0x0f, 0xbb, 0x8d, 0x08, 0x8c, 0xc6, 0x54,
- 0xe9, 0x71, 0xd2, 0x7e, 0xa4, 0xfe, 0x58, 0x7f, 0xd3, 0xc7, 0x3a, 0x00, 0x47, 0x44,
- 0x53, 0x56, 0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x63, 0x41, 0x56, 0x42, 0x3a, 0x00,
- 0x01, 0x11, 0x72, 0x01, 0x3a, 0x00, 0x01, 0x11, 0x73, 0xf6, 0x3a, 0x00, 0x47, 0x44,
- 0x52, 0x58, 0x40, 0x93, 0x17, 0xe1, 0x11, 0x27, 0x59, 0xd0, 0xef, 0x75, 0x0b, 0x2b,
- 0x1c, 0x0f, 0x5f, 0x52, 0xc3, 0x29, 0x23, 0xb5, 0x2a, 0xe6, 0x12, 0x72, 0x6f, 0x39,
- 0x86, 0x65, 0x2d, 0xf2, 0xe4, 0xe7, 0xd0, 0xaf, 0x0e, 0xa7, 0x99, 0x16, 0x89, 0x97,
- 0x21, 0xf7, 0xdc, 0x89, 0xdc, 0xde, 0xbb, 0x94, 0x88, 0x1f, 0xda, 0xe2, 0xf3, 0xe0,
- 0x54, 0xf9, 0x0e, 0x29, 0xb1, 0xbd, 0xe1, 0x0c, 0x0b, 0xd7, 0xf6, 0x3a, 0x00, 0x47,
- 0x44, 0x54, 0x58, 0x40, 0xb2, 0x69, 0x05, 0x48, 0x56, 0xb5, 0xfa, 0x55, 0x6f, 0xac,
- 0x56, 0xd9, 0x02, 0x35, 0x2b, 0xaa, 0x4c, 0xba, 0x28, 0xdd, 0x82, 0x3a, 0x86, 0xf5,
- 0xd4, 0xc2, 0xf1, 0xf9, 0x35, 0x7d, 0xe4, 0x43, 0x13, 0xbf, 0xfe, 0xd3, 0x36, 0xd8,
- 0x1c, 0x12, 0x78, 0x5c, 0x9c, 0x3e, 0xf6, 0x66, 0xef, 0xab, 0x3d, 0x0f, 0x89, 0xa4,
- 0x6f, 0xc9, 0x72, 0xee, 0x73, 0x43, 0x02, 0x8a, 0xef, 0xbc, 0x05, 0x98, 0x3a, 0x00,
- 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01,
- 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0x96, 0x6d, 0x96,
- 0x42, 0xda, 0x64, 0x51, 0xad, 0xfa, 0x00, 0xbc, 0xbc, 0x95, 0x8a, 0xb0, 0xb9, 0x76,
- 0x01, 0xe6, 0xbd, 0xc0, 0x26, 0x79, 0x26, 0xfc, 0x0f, 0x1d, 0x87, 0x65, 0xf1, 0xf3,
- 0x99, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40, 0x10, 0x7f, 0x77, 0xad,
- 0x70, 0xbd, 0x52, 0x81, 0x28, 0x8d, 0x24, 0x81, 0xb4, 0x3f, 0x21, 0x68, 0x9f, 0xc3,
- 0x80, 0x68, 0x86, 0x55, 0xfb, 0x2e, 0x6d, 0x96, 0xe1, 0xe1, 0xb7, 0x28, 0x8d, 0x63,
- 0x85, 0xba, 0x2a, 0x01, 0x33, 0x87, 0x60, 0x63, 0xbb, 0x16, 0x3f, 0x2f, 0x3d, 0xf4,
- 0x2d, 0x48, 0x5b, 0x87, 0xed, 0xda, 0x34, 0xeb, 0x9c, 0x4d, 0x14, 0xac, 0x65, 0xf4,
- 0xfa, 0xef, 0x45, 0x0b, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8f, 0xa9,
- 0x01, 0x78, 0x28, 0x32, 0x35, 0x39, 0x34, 0x38, 0x39, 0x65, 0x36, 0x39, 0x37, 0x34,
- 0x38, 0x37, 0x30, 0x35, 0x64, 0x65, 0x33, 0x65, 0x32, 0x66, 0x34, 0x34, 0x32, 0x36,
- 0x37, 0x65, 0x61, 0x34, 0x39, 0x33, 0x38, 0x66, 0x66, 0x36, 0x61, 0x35, 0x37, 0x32,
- 0x35, 0x02, 0x78, 0x28, 0x35, 0x64, 0x34, 0x65, 0x64, 0x37, 0x66, 0x34, 0x31, 0x37,
- 0x61, 0x39, 0x35, 0x34, 0x61, 0x31, 0x38, 0x31, 0x34, 0x30, 0x37, 0x62, 0x35, 0x38,
- 0x38, 0x35, 0x61, 0x66, 0x64, 0x37, 0x32, 0x61, 0x35, 0x62, 0x66, 0x34, 0x30, 0x64,
- 0x61, 0x36, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x58, 0x1a, 0xa3, 0x3a, 0x00, 0x01,
- 0x11, 0x71, 0x67, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x3a, 0x00, 0x01, 0x11,
- 0x72, 0x0c, 0x3a, 0x00, 0x01, 0x11, 0x73, 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58,
- 0x40, 0x26, 0x1a, 0xbd, 0x26, 0xd8, 0x37, 0x8f, 0x4a, 0xf2, 0x9e, 0x49, 0x4d, 0x93,
- 0x23, 0xc4, 0x6e, 0x02, 0xda, 0xe0, 0x00, 0x02, 0xe7, 0xed, 0x29, 0xdf, 0x2b, 0xb3,
- 0x69, 0xf3, 0x55, 0x0e, 0x4c, 0x22, 0xdc, 0xcf, 0xf5, 0x92, 0xc9, 0xfa, 0x78, 0x98,
- 0xf1, 0x0e, 0x55, 0x5f, 0xf4, 0x45, 0xed, 0xc0, 0x0a, 0x72, 0x2a, 0x7a, 0x3a, 0xd2,
- 0xb1, 0xf7, 0x76, 0xfe, 0x2a, 0x6b, 0x7b, 0x2a, 0x53, 0x3a, 0x00, 0x47, 0x44, 0x54,
- 0x58, 0x40, 0x04, 0x25, 0x5d, 0x60, 0x5f, 0x5c, 0x45, 0x0d, 0xf2, 0x9a, 0x6e, 0x99,
- 0x30, 0x03, 0xb8, 0xd6, 0xe1, 0x99, 0x71, 0x1b, 0xf8, 0x44, 0xfa, 0xb5, 0x31, 0x79,
- 0x1c, 0x37, 0x68, 0x4e, 0x1d, 0xc0, 0x24, 0x74, 0x68, 0xf8, 0x80, 0x20, 0x3e, 0x44,
- 0xb1, 0x43, 0xd2, 0x9c, 0xfc, 0x12, 0x9e, 0x77, 0x0a, 0xde, 0x29, 0x24, 0xff, 0x2e,
- 0xfa, 0xc7, 0x10, 0xd5, 0x73, 0xd4, 0xc6, 0xdf, 0x62, 0x9f, 0x3a, 0x00, 0x47, 0x44,
- 0x56, 0x41, 0x01, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03,
- 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0xdb, 0xe7, 0x5b, 0x3f, 0xa3,
- 0x42, 0xb0, 0x9c, 0xf8, 0x40, 0x8c, 0xb0, 0x9c, 0xf0, 0x0a, 0xaf, 0xdf, 0x6f, 0xe5,
- 0x09, 0x21, 0x11, 0x92, 0xe1, 0xf8, 0xc5, 0x09, 0x02, 0x3d, 0x1f, 0xb7, 0xc5, 0x3a,
- 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40, 0xc4, 0xc1, 0xd7, 0x1c, 0x2d, 0x26,
- 0x89, 0x22, 0xcf, 0xa6, 0x99, 0x77, 0x30, 0x84, 0x86, 0x27, 0x59, 0x8f, 0xd8, 0x08,
- 0x75, 0xe0, 0xb2, 0xef, 0xf9, 0xfa, 0xa5, 0x40, 0x8c, 0xd3, 0xeb, 0xbb, 0xda, 0xf2,
- 0xc8, 0xae, 0x41, 0x22, 0x50, 0x9c, 0xe8, 0xb2, 0x9c, 0x9b, 0x3f, 0x8a, 0x78, 0x76,
- 0xab, 0xd0, 0xbe, 0xfc, 0xe4, 0x79, 0xcb, 0x1b, 0x2b, 0xaa, 0x4d, 0xdd, 0x15, 0x61,
- 0x42, 0x06, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8d, 0xa9, 0x01, 0x78,
- 0x28, 0x35, 0x64, 0x34, 0x65, 0x64, 0x37, 0x66, 0x34, 0x31, 0x37, 0x61, 0x39, 0x35,
- 0x34, 0x61, 0x31, 0x38, 0x31, 0x34, 0x30, 0x37, 0x62, 0x35, 0x38, 0x38, 0x35, 0x61,
- 0x66, 0x64, 0x37, 0x32, 0x61, 0x35, 0x62, 0x66, 0x34, 0x30, 0x64, 0x61, 0x36, 0x02,
- 0x78, 0x28, 0x36, 0x39, 0x62, 0x31, 0x37, 0x36, 0x37, 0x35, 0x38, 0x61, 0x36, 0x66,
- 0x34, 0x34, 0x62, 0x35, 0x65, 0x38, 0x39, 0x39, 0x63, 0x64, 0x65, 0x33, 0x63, 0x66,
- 0x34, 0x35, 0x31, 0x39, 0x61, 0x39, 0x33, 0x35, 0x62, 0x63, 0x39, 0x66, 0x65, 0x34,
- 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0x31, 0x0d, 0x31, 0xfa, 0x78, 0x58, 0x33,
- 0xf2, 0xf8, 0x58, 0x6b, 0xe9, 0x68, 0x32, 0x44, 0xd0, 0xfc, 0x2d, 0xe1, 0xfc, 0xe1,
- 0xc2, 0x4e, 0x2b, 0xa8, 0x2c, 0xa1, 0xc1, 0x48, 0xc6, 0xaa, 0x91, 0x89, 0x4f, 0xb7,
- 0x9c, 0x40, 0x74, 0x21, 0x36, 0x31, 0x45, 0x09, 0xdf, 0x0c, 0xb4, 0xf9, 0x9a, 0x59,
- 0xae, 0x4f, 0x21, 0x10, 0xc1, 0x38, 0xa8, 0xa2, 0xbe, 0xc6, 0x36, 0xf0, 0x56, 0x58,
- 0xdb, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x58, 0x18, 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71,
- 0x6b, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x20, 0x31, 0x3a, 0x00,
- 0x01, 0x11, 0x73, 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0xce, 0x8a, 0x30,
- 0x4e, 0x31, 0x53, 0xea, 0xdd, 0x2f, 0xbd, 0x15, 0xbc, 0x6b, 0x0f, 0xe7, 0x43, 0x50,
- 0xef, 0x65, 0xec, 0x4e, 0x21, 0x64, 0x6e, 0x41, 0x22, 0xac, 0x87, 0xda, 0xf1, 0xf2,
- 0x80, 0xc6, 0x8a, 0xd8, 0x7b, 0xe8, 0xe2, 0x9b, 0x87, 0x21, 0x5e, 0x26, 0x23, 0x11,
- 0x89, 0x86, 0x57, 0x2d, 0x47, 0x73, 0x3f, 0x47, 0x87, 0xfa, 0x58, 0x5c, 0x78, 0x7b,
- 0xa3, 0xfc, 0x2b, 0x6c, 0xed, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0xd8, 0x40,
- 0xa0, 0x60, 0x45, 0x28, 0x5d, 0xd4, 0xc1, 0x08, 0x3c, 0xbc, 0x91, 0xf4, 0xa6, 0xa4,
- 0xde, 0xd3, 0x3d, 0xbb, 0x24, 0x46, 0xa3, 0x58, 0x49, 0x57, 0x4d, 0x2e, 0x6d, 0x7a,
- 0x78, 0x4b, 0x9d, 0x28, 0x9a, 0x4e, 0xf1, 0x23, 0x06, 0x35, 0xff, 0x8e, 0x1e, 0xb3,
- 0x02, 0x63, 0x62, 0x9a, 0x50, 0x6d, 0x18, 0x70, 0x8e, 0xe3, 0x2e, 0x29, 0xb4, 0x22,
- 0x71, 0x31, 0x39, 0x65, 0xd5, 0xb5, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a,
- 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02,
- 0x20, 0x06, 0x21, 0x58, 0x20, 0x51, 0x3c, 0x4b, 0x56, 0x0b, 0x49, 0x0b, 0xee, 0xc5,
- 0x71, 0xd4, 0xe7, 0xbc, 0x44, 0x27, 0x4f, 0x4e, 0x67, 0xfc, 0x3a, 0xb9, 0x47, 0x8c,
- 0x6f, 0x24, 0x29, 0xf8, 0xb8, 0x2f, 0xa7, 0xb3, 0x4d, 0x3a, 0x00, 0x47, 0x44, 0x58,
- 0x41, 0x20, 0x58, 0x40, 0x4e, 0x6d, 0x0e, 0x2b, 0x1d, 0x44, 0x99, 0xb6, 0x63, 0x07,
- 0x86, 0x1a, 0xce, 0x4b, 0xdc, 0xd1, 0x3a, 0xdc, 0xbf, 0xaa, 0xb3, 0x06, 0xd9, 0xb5,
- 0x5c, 0x75, 0xf0, 0x14, 0x63, 0xa9, 0x1e, 0x7c, 0x56, 0x62, 0x2c, 0xa5, 0xda, 0xc9,
- 0x81, 0xcb, 0x3d, 0x63, 0x32, 0x6b, 0x76, 0x81, 0xd2, 0x93, 0xeb, 0xac, 0xfe, 0x0c,
- 0x87, 0x66, 0x9e, 0x87, 0x82, 0xb4, 0x81, 0x6e, 0x33, 0xf1, 0x08, 0x01, 0x84, 0x43,
- 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8d, 0xa9, 0x01, 0x78, 0x28, 0x36, 0x39, 0x62,
- 0x31, 0x37, 0x36, 0x37, 0x35, 0x38, 0x61, 0x36, 0x66, 0x34, 0x34, 0x62, 0x35, 0x65,
- 0x38, 0x39, 0x39, 0x63, 0x64, 0x65, 0x33, 0x63, 0x66, 0x34, 0x35, 0x31, 0x39, 0x61,
- 0x39, 0x33, 0x35, 0x62, 0x63, 0x39, 0x66, 0x65, 0x34, 0x02, 0x78, 0x28, 0x32, 0x39,
- 0x65, 0x34, 0x62, 0x61, 0x63, 0x33, 0x30, 0x31, 0x65, 0x66, 0x36, 0x35, 0x61, 0x38,
- 0x31, 0x31, 0x62, 0x39, 0x39, 0x62, 0x30, 0x33, 0x64, 0x65, 0x39, 0x35, 0x34, 0x65,
- 0x61, 0x37, 0x36, 0x61, 0x38, 0x39, 0x31, 0x37, 0x38, 0x35, 0x3a, 0x00, 0x47, 0x44,
- 0x50, 0x58, 0x40, 0xa4, 0x03, 0xe3, 0xde, 0x44, 0x96, 0xed, 0x31, 0x41, 0xa0, 0xba,
- 0x59, 0xee, 0x2b, 0x03, 0x65, 0xcb, 0x63, 0x14, 0x78, 0xbe, 0xad, 0x24, 0x33, 0xb8,
- 0x6b, 0x52, 0xd8, 0xab, 0xd5, 0x79, 0x84, 0x98, 0x6c, 0xc2, 0x66, 0xeb, 0x6c, 0x24,
- 0xa6, 0xfa, 0x32, 0xa8, 0x16, 0xb8, 0x64, 0x37, 0x2b, 0xd4, 0xc0, 0xc4, 0xc2, 0x63,
- 0x25, 0x10, 0xce, 0x47, 0xe3, 0x49, 0xad, 0x41, 0xf5, 0xc8, 0xf6, 0x3a, 0x00, 0x47,
- 0x44, 0x53, 0x58, 0x18, 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x6b, 0x63, 0x6f, 0x6d,
- 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x20, 0x32, 0x3a, 0x00, 0x01, 0x11, 0x73, 0xf6,
- 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0xc7, 0x50, 0x09, 0xd0, 0xe0, 0xdd, 0x80,
- 0x77, 0xae, 0xa7, 0xc8, 0x88, 0x1e, 0x88, 0xd0, 0xc7, 0x0d, 0x7c, 0x49, 0xc5, 0xb5,
- 0x64, 0x32, 0x28, 0x2c, 0x48, 0x94, 0xc0, 0xd6, 0x7d, 0x9c, 0x86, 0xda, 0xf7, 0x98,
- 0xc7, 0xae, 0xa4, 0x0e, 0x61, 0xc8, 0xb0, 0x8b, 0x8a, 0xe4, 0xad, 0xcf, 0xcf, 0x6d,
- 0x60, 0x60, 0x31, 0xdd, 0xa7, 0x24, 0x9b, 0x27, 0x16, 0x31, 0x90, 0x80, 0x70, 0xc3,
- 0xba, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0xf8, 0x86, 0xc6, 0x94, 0xf9, 0x3f,
- 0x66, 0x3c, 0x43, 0x01, 0x29, 0x27, 0x8d, 0x3c, 0xb2, 0x11, 0xf2, 0x04, 0xb6, 0x67,
- 0x4f, 0x5f, 0x90, 0xcb, 0xc6, 0x73, 0xe6, 0x25, 0x14, 0x63, 0xa7, 0x95, 0x11, 0x0e,
- 0xa0, 0x1d, 0x3f, 0x6a, 0x58, 0x0a, 0x53, 0xaa, 0x68, 0x3b, 0x92, 0x64, 0x2b, 0x2e,
- 0x79, 0x80, 0x70, 0x0e, 0x41, 0xf5, 0xe9, 0x2a, 0x36, 0x0a, 0xa4, 0xe8, 0xb4, 0xe5,
- 0xdd, 0xa6, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a, 0x00, 0x47, 0x44, 0x57,
- 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58,
- 0x20, 0x9e, 0x04, 0x11, 0x24, 0x34, 0xba, 0x40, 0xed, 0x86, 0xe9, 0x48, 0x70, 0x3b,
- 0xe7, 0x76, 0xfa, 0xc5, 0xf6, 0x6d, 0xab, 0x86, 0x12, 0x00, 0xbe, 0xc7, 0x00, 0x69,
- 0x0e, 0x97, 0x97, 0xa6, 0x12, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40,
- 0xb7, 0x31, 0xd5, 0x4c, 0x7d, 0xf5, 0xd7, 0xb8, 0xb4, 0x4f, 0x93, 0x47, 0x2c, 0x3d,
- 0x50, 0xcc, 0xad, 0x28, 0x23, 0x68, 0xcf, 0xc2, 0x90, 0xd7, 0x02, 0x00, 0xd8, 0xf1,
- 0x00, 0x14, 0x03, 0x90, 0x9e, 0x0b, 0x91, 0xa7, 0x22, 0x28, 0xfe, 0x55, 0x42, 0x30,
- 0x93, 0x05, 0x66, 0xcd, 0xce, 0xb8, 0x48, 0x07, 0x56, 0x54, 0x67, 0xa5, 0xd7, 0xe3,
- 0x16, 0xd6, 0x75, 0x7c, 0x94, 0x98, 0x1b, 0x0b, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0,
- 0x59, 0x01, 0x8d, 0xa9, 0x01, 0x78, 0x28, 0x32, 0x39, 0x65, 0x34, 0x62, 0x61, 0x63,
- 0x33, 0x30, 0x31, 0x65, 0x66, 0x36, 0x35, 0x61, 0x38, 0x31, 0x31, 0x62, 0x39, 0x39,
- 0x62, 0x30, 0x33, 0x64, 0x65, 0x39, 0x35, 0x34, 0x65, 0x61, 0x37, 0x36, 0x61, 0x38,
- 0x39, 0x31, 0x37, 0x38, 0x35, 0x02, 0x78, 0x28, 0x31, 0x38, 0x37, 0x36, 0x63, 0x61,
- 0x63, 0x34, 0x32, 0x33, 0x39, 0x35, 0x37, 0x66, 0x33, 0x62, 0x66, 0x62, 0x32, 0x62,
- 0x32, 0x63, 0x39, 0x33, 0x37, 0x64, 0x31, 0x34, 0x62, 0x62, 0x38, 0x30, 0x64, 0x30,
- 0x36, 0x37, 0x33, 0x65, 0x66, 0x66, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0xf4,
- 0x7d, 0x11, 0x21, 0xc1, 0x19, 0x57, 0x23, 0x08, 0x6e, 0x5f, 0xe4, 0x55, 0xc5, 0x08,
- 0x16, 0x40, 0x5f, 0x2a, 0x6f, 0x04, 0x1e, 0x6f, 0x22, 0xde, 0x53, 0xbd, 0x37, 0xe2,
- 0xfb, 0xb4, 0x0b, 0x65, 0xf4, 0xdc, 0xc9, 0xf4, 0xce, 0x2d, 0x82, 0x2a, 0xbc, 0xaf,
- 0x37, 0x80, 0x0b, 0x7f, 0xff, 0x3a, 0x98, 0x9c, 0xa7, 0x70, 0x4f, 0xbc, 0x59, 0x4f,
- 0x4e, 0xb1, 0x6d, 0xdf, 0x60, 0x39, 0x11, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x58, 0x18,
- 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x6b, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65,
- 0x6e, 0x74, 0x20, 0x33, 0x3a, 0x00, 0x01, 0x11, 0x73, 0xf6, 0x3a, 0x00, 0x47, 0x44,
- 0x52, 0x58, 0x40, 0xa4, 0xd5, 0x6f, 0xc8, 0xd6, 0xc7, 0xe4, 0x22, 0xb4, 0x7a, 0x26,
- 0x49, 0xd5, 0xb4, 0xc1, 0xc6, 0x1b, 0xfa, 0x14, 0x8c, 0x49, 0x72, 0x2f, 0xfe, 0xbc,
- 0xc1, 0xc8, 0xc6, 0x65, 0x62, 0x86, 0xf7, 0xf2, 0x74, 0x45, 0x9b, 0x1a, 0xa0, 0x2b,
- 0xc4, 0x27, 0x13, 0xc5, 0xc3, 0xe5, 0x28, 0xc2, 0x16, 0xcd, 0x90, 0x6d, 0xa0, 0xf7,
- 0x27, 0x04, 0xa8, 0xa2, 0x62, 0xaa, 0x2c, 0x0c, 0x75, 0xd5, 0x9d, 0x3a, 0x00, 0x47,
- 0x44, 0x54, 0x58, 0x40, 0x1d, 0x92, 0x34, 0xfb, 0xfe, 0x74, 0xb7, 0xce, 0x3a, 0x95,
- 0x45, 0xe5, 0x3e, 0x1f, 0x5f, 0x18, 0x53, 0x5f, 0xe1, 0x85, 0xb0, 0x1d, 0xe3, 0x8d,
- 0x53, 0x77, 0xdc, 0x86, 0x32, 0x3d, 0x9b, 0xf9, 0xa5, 0x51, 0x17, 0x51, 0x9a, 0xd8,
- 0xa6, 0x7d, 0x45, 0x98, 0x47, 0xa2, 0x73, 0x54, 0x66, 0x28, 0x66, 0x92, 0x1d, 0x28,
- 0x8a, 0xe7, 0x5d, 0xb8, 0x96, 0x4b, 0x6a, 0x9d, 0xee, 0xc2, 0xe9, 0x20, 0x3a, 0x00,
- 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01,
- 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0x4d, 0xf5, 0x61,
- 0x1e, 0xa6, 0x64, 0x74, 0x0b, 0x6c, 0x99, 0x8b, 0x6d, 0x34, 0x42, 0x21, 0xdd, 0x82,
- 0x26, 0x13, 0xb4, 0xf0, 0xbc, 0x9a, 0x0b, 0xf6, 0x56, 0xbd, 0x5d, 0xea, 0xd5, 0x07,
- 0x7a, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40, 0x40, 0x4d, 0x09, 0x0d,
- 0x80, 0xba, 0x12, 0x94, 0x05, 0xfb, 0x1a, 0x23, 0xa3, 0xcb, 0x28, 0x6f, 0xd7, 0x29,
- 0x95, 0xda, 0x83, 0x07, 0x3c, 0xbe, 0x7c, 0x37, 0xeb, 0x9c, 0xb2, 0x77, 0x10, 0x3f,
- 0x6a, 0x41, 0x80, 0xce, 0x56, 0xb7, 0x55, 0x22, 0x81, 0x77, 0x2d, 0x3c, 0xf8, 0x16,
- 0x38, 0x49, 0xcc, 0x9a, 0xe8, 0x3a, 0x03, 0x33, 0x4c, 0xe6, 0x87, 0x72, 0xf6, 0x5a,
- 0x4a, 0x3f, 0x4e, 0x0a,
- ],
- )
- .unwrap()
- }
-}
diff --git a/diced/src/lib.rs b/diced/src/lib.rs
deleted file mode 100644
index 50e0e96..0000000
--- a/diced/src/lib.rs
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright 2021, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Implement the android.security.dice.IDiceNode service.
-
-mod error;
-mod permission;
-mod proxy_node_hal;
-mod resident_node;
-
-pub use crate::proxy_node_hal::ProxyNodeHal;
-pub use crate::resident_node::ResidentNode;
-use android_hardware_security_dice::aidl::android::hardware::security::dice::{
- Bcc::Bcc, BccHandover::BccHandover, Config::Config as BinderConfig,
- InputValues::InputValues as BinderInputValues, Mode::Mode, Signature::Signature,
-};
-use android_security_dice::aidl::android::security::dice::{
- IDiceMaintenance::BnDiceMaintenance, IDiceMaintenance::IDiceMaintenance, IDiceNode::BnDiceNode,
- IDiceNode::IDiceNode, ResponseCode::ResponseCode,
-};
-use anyhow::{Context, Result};
-use binder::{BinderFeatures, Result as BinderResult, Strong, ThreadState};
-pub use diced_open_dice_cbor as dice;
-use error::{map_or_log_err, Error};
-use keystore2_selinux as selinux;
-use libc::uid_t;
-use permission::Permission;
-use std::sync::Arc;
-
-/// A DiceNode backend implementation.
-/// All functions except demote_self derive effective dice artifacts staring from
-/// this node and iterating through `{ [client | demotion path], input_values }`
-/// in ascending order.
-pub trait DiceNodeImpl {
- /// Signs the message using the effective dice artifacts and Ed25519Pure.
- fn sign(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- message: &[u8],
- ) -> Result<Signature>;
- /// Returns the effective attestation chain.
- fn get_attestation_chain(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- ) -> Result<Bcc>;
- /// Returns the effective dice artifacts.
- fn derive(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- ) -> Result<BccHandover>;
- /// Adds [ `client` | `input_values` ] to the demotion path of the given client.
- /// This changes the effective dice artifacts for all subsequent API calls of the
- /// given client.
- fn demote(&self, client: BinderInputValues, input_values: &[BinderInputValues]) -> Result<()>;
- /// This demotes the implementation itself. I.e. a resident node would replace its resident
- /// with the effective artifacts derived using `input_values`. A proxy node would
- /// simply call `demote` on its parent node. This is not reversible and changes
- /// the effective dice artifacts of all clients.
- fn demote_self(&self, input_values: &[BinderInputValues]) -> Result<()>;
-}
-
-/// Wraps a DiceNodeImpl and implements the actual IDiceNode AIDL API.
-pub struct DiceNode {
- node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
-}
-
-/// This function uses its namesake in the permission module and in
-/// combination with with_calling_sid from the binder crate to check
-/// if the caller has the given keystore permission.
-pub fn check_caller_permission<T: selinux::ClassPermission>(perm: T) -> Result<()> {
- ThreadState::with_calling_sid(|calling_sid| {
- let target_context =
- selinux::getcon().context("In check_caller_permission: getcon failed.")?;
-
- selinux::check_permission(
- calling_sid.ok_or(Error::Rc(ResponseCode::SYSTEM_ERROR)).context(
- "In check_keystore_permission: Cannot check permission without calling_sid.",
- )?,
- &target_context,
- perm,
- )
- })
-}
-
-fn client_input_values(uid: uid_t) -> Result<BinderInputValues> {
- Ok(BinderInputValues {
- codeHash: [0; dice::HASH_SIZE],
- config: BinderConfig {
- desc: dice::bcc::format_config_descriptor(Some(&format!("{}", uid)), None, false)
- .context("In client_input_values: failed to format config descriptor")?,
- },
- authorityHash: [0; dice::HASH_SIZE],
- authorityDescriptor: None,
- hidden: [0; dice::HIDDEN_SIZE],
- mode: Mode::NORMAL,
- })
-}
-
-impl DiceNode {
- /// Constructs an instance of DiceNode, wraps it with a BnDiceNode object and
- /// returns a strong pointer to the binder. The result can be used to register
- /// the service with service manager.
- pub fn new_as_binder(
- node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
- ) -> Result<Strong<dyn IDiceNode>> {
- let result = BnDiceNode::new_binder(
- DiceNode { node_impl },
- BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
- );
- Ok(result)
- }
-
- fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> Result<Signature> {
- check_caller_permission(Permission::UseSign).context("In DiceNode::sign:")?;
- let client =
- client_input_values(ThreadState::get_calling_uid()).context("In DiceNode::sign:")?;
- self.node_impl.sign(client, input_values, message)
- }
- fn get_attestation_chain(&self, input_values: &[BinderInputValues]) -> Result<Bcc> {
- check_caller_permission(Permission::GetAttestationChain)
- .context("In DiceNode::get_attestation_chain:")?;
- let client = client_input_values(ThreadState::get_calling_uid())
- .context("In DiceNode::get_attestation_chain:")?;
- self.node_impl.get_attestation_chain(client, input_values)
- }
- fn derive(&self, input_values: &[BinderInputValues]) -> Result<BccHandover> {
- check_caller_permission(Permission::Derive).context("In DiceNode::derive:")?;
- let client =
- client_input_values(ThreadState::get_calling_uid()).context("In DiceNode::extend:")?;
- self.node_impl.derive(client, input_values)
- }
- fn demote(&self, input_values: &[BinderInputValues]) -> Result<()> {
- check_caller_permission(Permission::Demote).context("In DiceNode::demote:")?;
- let client =
- client_input_values(ThreadState::get_calling_uid()).context("In DiceNode::demote:")?;
- self.node_impl.demote(client, input_values)
- }
-}
-
-impl binder::Interface for DiceNode {}
-
-impl IDiceNode for DiceNode {
- fn sign(&self, input_values: &[BinderInputValues], message: &[u8]) -> BinderResult<Signature> {
- map_or_log_err(self.sign(input_values, message), Ok)
- }
- fn getAttestationChain(&self, input_values: &[BinderInputValues]) -> BinderResult<Bcc> {
- map_or_log_err(self.get_attestation_chain(input_values), Ok)
- }
- fn derive(&self, input_values: &[BinderInputValues]) -> BinderResult<BccHandover> {
- map_or_log_err(self.derive(input_values), Ok)
- }
- fn demote(&self, input_values: &[BinderInputValues]) -> BinderResult<()> {
- map_or_log_err(self.demote(input_values), Ok)
- }
-}
-
-/// Wraps a DiceNodeImpl and implements the IDiceMaintenance AIDL API.
-pub struct DiceMaintenance {
- node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
-}
-
-impl DiceMaintenance {
- /// Constructs an instance of DiceMaintenance, wraps it with a BnDiceMaintenance object and
- /// returns a strong pointer to the binder. The result can be used to register the service
- /// with service manager.
- pub fn new_as_binder(
- node_impl: Arc<dyn DiceNodeImpl + Sync + Send>,
- ) -> Result<Strong<dyn IDiceMaintenance>> {
- let result = BnDiceMaintenance::new_binder(
- DiceMaintenance { node_impl },
- BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
- );
- Ok(result)
- }
-
- fn demote_self(&self, input_values: &[BinderInputValues]) -> Result<()> {
- check_caller_permission(Permission::DemoteSelf)
- .context("In DiceMaintenance::demote_self:")?;
- self.node_impl.demote_self(input_values)
- }
-}
-
-impl binder::Interface for DiceMaintenance {}
-
-impl IDiceMaintenance for DiceMaintenance {
- fn demoteSelf(&self, input_values: &[BinderInputValues]) -> BinderResult<()> {
- map_or_log_err(self.demote_self(input_values), Ok)
- }
-}
diff --git a/diced/src/lib_vendor.rs b/diced/src/lib_vendor.rs
deleted file mode 100644
index 01c804b..0000000
--- a/diced/src/lib_vendor.rs
+++ /dev/null
@@ -1,20 +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 implements the android.hardware.security.dice.IDiceDevice interface
-//! and provides support for implementing a DICE HAL service.
-
-mod error_vendor;
-pub mod hal_node;
-pub use diced_open_dice_cbor 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 99a6dc9..0000000
--- a/diced/src/resident_node.rs
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright 2021, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! A resident dice node keeps CDI_attest and CDI_seal memory resident and can serve
-//! its clients directly by performing all crypto operations including derivations and
-//! certificate generation itself.
-
-use crate::DiceNodeImpl;
-use android_hardware_security_dice::aidl::android::hardware::security::dice::{
- Bcc::Bcc, BccHandover::BccHandover, InputValues::InputValues as BinderInputValues,
- Signature::Signature,
-};
-use anyhow::{Context, Result};
-use dice::{ContextImpl, OpenDiceCborContext};
-use diced_open_dice_cbor as dice;
-use diced_utils::{self as utils, InputValues, ResidentArtifacts};
-use std::collections::HashMap;
-use std::convert::TryInto;
-use std::sync::RwLock;
-
-/// The ResidentNode implements a IDiceNode backend with memory resident DICE secrets.
-pub struct ResidentNode {
- artifacts: RwLock<ResidentArtifacts>,
- demotion_db: RwLock<HashMap<BinderInputValues, Vec<BinderInputValues>>>,
-}
-
-impl ResidentNode {
- /// Creates a new Resident node with the given dice secrets and certificate chain.
- pub fn new(
- cdi_attest: &[u8; dice::CDI_SIZE],
- cdi_seal: &[u8; dice::CDI_SIZE],
- bcc: Vec<u8>,
- ) -> Result<Self> {
- Ok(ResidentNode {
- artifacts: RwLock::new(
- ResidentArtifacts::new(cdi_attest, cdi_seal, &bcc)
- .context("In ResidentNode::new: Trying to initialize ResidentArtifacts")?,
- ),
- demotion_db: Default::default(),
- })
- }
-
- fn get_effective_artifacts(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- ) -> Result<ResidentArtifacts> {
- let artifacts = self.artifacts.read().unwrap().try_clone()?;
- let demotion_db = self.demotion_db.read().unwrap();
-
- let client_arr = [client];
-
- let input_values: Vec<utils::InputValues> = demotion_db
- .get(&client_arr[0])
- .map(|v| v.iter())
- .unwrap_or_else(|| client_arr.iter())
- .chain(input_values.iter())
- .map(|v| v.into())
- .collect();
-
- artifacts
- .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
- .context("In get_effective_artifacts:")
- }
-}
-
-impl DiceNodeImpl for ResidentNode {
- fn sign(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- message: &[u8],
- ) -> Result<Signature> {
- let (cdi_attest, _, _) = self
- .get_effective_artifacts(client, input_values)
- .context("In ResidentNode::sign: Failed to get effective_artifacts.")?
- .into_tuple();
- let mut dice = OpenDiceCborContext::new();
- let seed = dice
- .derive_cdi_private_key_seed(cdi_attest[..].try_into().with_context(|| {
- format!(
- "In ResidentNode::sign: Failed to convert cdi_attest (length: {}).",
- cdi_attest.len()
- )
- })?)
- .context("In ResidentNode::sign: Failed to derive seed from cdi_attest.")?;
- let (_public_key, private_key) = dice
- .keypair_from_seed(seed[..].try_into().with_context(|| {
- format!("In ResidentNode::sign: Failed to convert seed (length: {}).", seed.len())
- })?)
- .context("In ResidentNode::sign: Failed to derive keypair from seed.")?;
- Ok(Signature {
- data: dice
- .sign(
- message,
- private_key[..].try_into().with_context(|| {
- format!(
- "In ResidentNode::sign: Failed to convert private_key (length: {}).",
- private_key.len()
- )
- })?,
- )
- .context("In ResidentNode::sign: Failed to sign.")?,
- })
- }
-
- fn get_attestation_chain(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- ) -> Result<Bcc> {
- let (_, _, bcc) = self
- .get_effective_artifacts(client, input_values)
- .context("In ResidentNode::get_attestation_chain: Failed to get effective_artifacts.")?
- .into_tuple();
-
- Ok(Bcc { data: bcc })
- }
-
- fn derive(
- &self,
- client: BinderInputValues,
- input_values: &[BinderInputValues],
- ) -> Result<BccHandover> {
- let (cdi_attest, cdi_seal, bcc) =
- self.get_effective_artifacts(client, input_values)?.into_tuple();
-
- utils::make_bcc_handover(
- &cdi_attest[..]
- .try_into()
- .context("In ResidentNode::derive: Trying to convert cdi_attest to sized array.")?,
- &cdi_seal[..]
- .try_into()
- .context("In ResidentNode::derive: Trying to convert cdi_attest to sized array.")?,
- &bcc,
- )
- .context("In ResidentNode::derive: Trying to format bcc handover.")
- }
-
- fn demote(&self, client: BinderInputValues, input_values: &[BinderInputValues]) -> Result<()> {
- let mut demotion_db = self.demotion_db.write().unwrap();
-
- let client_arr = [client];
-
- // The following statement consults demotion database which yields an optional demotion
- // path. It then constructs an iterator over the following elements, then clones and
- // collects them into a new vector:
- // [ demotion path | client ], input_values
- let new_path: Vec<BinderInputValues> = demotion_db
- .get(&client_arr[0])
- .map(|v| v.iter())
- .unwrap_or_else(|| client_arr.iter())
- .chain(input_values)
- .cloned()
- .collect();
-
- let [client] = client_arr;
- demotion_db.insert(client, new_path);
- Ok(())
- }
-
- fn demote_self(&self, input_values: &[BinderInputValues]) -> Result<()> {
- let mut artifacts = self.artifacts.write().unwrap();
-
- let input_values = input_values
- .iter()
- .map(|v| {
- v.try_into().with_context(|| format!("Failed to convert input values: {:#?}", v))
- })
- .collect::<Result<Vec<InputValues>>>()
- .context("In ResidentNode::demote_self:")?;
-
- *artifacts = artifacts
- .try_clone()
- .context("In ResidentNode::demote_self: Failed to clone resident artifacts")?
- .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
- .context("In ResidentNode::demote_self:")?;
- Ok(())
- }
-}
diff --git a/diced/src/sample_inputs.rs b/diced/src/sample_inputs.rs
index ff239ed..19200a5 100644
--- a/diced/src/sample_inputs.rs
+++ b/diced/src/sample_inputs.rs
@@ -15,24 +15,65 @@
//! This module provides a set of sample input values for a DICE chain, a sample UDS,
//! as well as tuple of CDIs and BCC derived thereof.
-use android_hardware_security_dice::aidl::android::hardware::security::dice::{
- Config::Config as BinderConfig, InputValues::InputValues as BinderInputValues, Mode::Mode,
+use anyhow::{anyhow, Context, Result};
+use diced_open_dice::{
+ derive_cdi_private_key_seed, keypair_from_seed, retry_bcc_format_config_descriptor,
+ retry_bcc_main_flow, retry_dice_main_flow, Config, DiceArtifacts, DiceMode, InputValues,
+ OwnedDiceArtifacts, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE,
};
-use anyhow::{Context, Result};
-use dice::ContextImpl;
-use diced_open_dice_cbor as dice;
use diced_utils::cbor;
-use diced_utils::InputValues;
-use keystore2_crypto::ZVec;
-use std::convert::{TryFrom, TryInto};
+use std::ffi::CStr;
use std::io::Write;
/// Sample UDS used to perform the root dice flow by `make_sample_bcc_and_cdis`.
-pub static UDS: &[u8; dice::CDI_SIZE] = &[
+pub const UDS: &[u8; CDI_SIZE] = &[
0x65, 0x4f, 0xab, 0xa9, 0xa5, 0xad, 0x0f, 0x5e, 0x15, 0xc3, 0x12, 0xf7, 0x77, 0x45, 0xfa, 0x55,
0x18, 0x6a, 0xa6, 0x34, 0xb6, 0x7c, 0x82, 0x7b, 0x89, 0x4c, 0xc5, 0x52, 0xd3, 0x27, 0x35, 0x8e,
];
+const CODE_HASH_ABL: [u8; HASH_SIZE] = [
+ 0x16, 0x48, 0xf2, 0x55, 0x53, 0x23, 0xdd, 0x15, 0x2e, 0x83, 0x38, 0xc3, 0x64, 0x38, 0x63, 0x26,
+ 0x0f, 0xcf, 0x5b, 0xd1, 0x3a, 0xd3, 0x40, 0x3e, 0x23, 0xf8, 0x34, 0x4c, 0x6d, 0xa2, 0xbe, 0x25,
+ 0x1c, 0xb0, 0x29, 0xe8, 0xc3, 0xfb, 0xb8, 0x80, 0xdc, 0xb1, 0xd2, 0xb3, 0x91, 0x4d, 0xd3, 0xfb,
+ 0x01, 0x0f, 0xe4, 0xe9, 0x46, 0xa2, 0xc0, 0x26, 0x57, 0x5a, 0xba, 0x30, 0xf7, 0x15, 0x98, 0x14,
+];
+const AUTHORITY_HASH_ABL: [u8; HASH_SIZE] = [
+ 0xf9, 0x00, 0x9d, 0xc2, 0x59, 0x09, 0xe0, 0xb6, 0x98, 0xbd, 0xe3, 0x97, 0x4a, 0xcb, 0x3c, 0xe7,
+ 0x6b, 0x24, 0xc3, 0xe4, 0x98, 0xdd, 0xa9, 0x6a, 0x41, 0x59, 0x15, 0xb1, 0x23, 0xe6, 0xc8, 0xdf,
+ 0xfb, 0x52, 0xb4, 0x52, 0xc1, 0xb9, 0x61, 0xdd, 0xbc, 0x5b, 0x37, 0x0e, 0x12, 0x12, 0xb2, 0xfd,
+ 0xc1, 0x09, 0xb0, 0xcf, 0x33, 0x81, 0x4c, 0xc6, 0x29, 0x1b, 0x99, 0xea, 0xae, 0xfd, 0xaa, 0x0d,
+];
+const HIDDEN_ABL: [u8; HIDDEN_SIZE] = [
+ 0xa2, 0x01, 0xd0, 0xc0, 0xaa, 0x75, 0x3c, 0x06, 0x43, 0x98, 0x6c, 0xc3, 0x5a, 0xb5, 0x5f, 0x1f,
+ 0x0f, 0x92, 0x44, 0x3b, 0x0e, 0xd4, 0x29, 0x75, 0xe3, 0xdb, 0x36, 0xda, 0xc8, 0x07, 0x97, 0x4d,
+ 0xff, 0xbc, 0x6a, 0xa4, 0x8a, 0xef, 0xc4, 0x7f, 0xf8, 0x61, 0x7d, 0x51, 0x4d, 0x2f, 0xdf, 0x7e,
+ 0x8c, 0x3d, 0xa3, 0xfc, 0x63, 0xd4, 0xd4, 0x74, 0x8a, 0xc4, 0x14, 0x45, 0x83, 0x6b, 0x12, 0x7e,
+];
+const CODE_HASH_AVB: [u8; HASH_SIZE] = [
+ 0xa4, 0x0c, 0xcb, 0xc1, 0xbf, 0xfa, 0xcc, 0xfd, 0xeb, 0xf4, 0xfc, 0x43, 0x83, 0x7f, 0x46, 0x8d,
+ 0xd8, 0xd8, 0x14, 0xc1, 0x96, 0x14, 0x1f, 0x6e, 0xb3, 0xa0, 0xd9, 0x56, 0xb3, 0xbf, 0x2f, 0xfa,
+ 0x88, 0x70, 0x11, 0x07, 0x39, 0xa4, 0xd2, 0xa9, 0x6b, 0x18, 0x28, 0xe8, 0x29, 0x20, 0x49, 0x0f,
+ 0xbb, 0x8d, 0x08, 0x8c, 0xc6, 0x54, 0xe9, 0x71, 0xd2, 0x7e, 0xa4, 0xfe, 0x58, 0x7f, 0xd3, 0xc7,
+];
+const AUTHORITY_HASH_AVB: [u8; HASH_SIZE] = [
+ 0xb2, 0x69, 0x05, 0x48, 0x56, 0xb5, 0xfa, 0x55, 0x6f, 0xac, 0x56, 0xd9, 0x02, 0x35, 0x2b, 0xaa,
+ 0x4c, 0xba, 0x28, 0xdd, 0x82, 0x3a, 0x86, 0xf5, 0xd4, 0xc2, 0xf1, 0xf9, 0x35, 0x7d, 0xe4, 0x43,
+ 0x13, 0xbf, 0xfe, 0xd3, 0x36, 0xd8, 0x1c, 0x12, 0x78, 0x5c, 0x9c, 0x3e, 0xf6, 0x66, 0xef, 0xab,
+ 0x3d, 0x0f, 0x89, 0xa4, 0x6f, 0xc9, 0x72, 0xee, 0x73, 0x43, 0x02, 0x8a, 0xef, 0xbc, 0x05, 0x98,
+];
+const HIDDEN_AVB: [u8; HIDDEN_SIZE] = [
+ 0x5b, 0x3f, 0xc9, 0x6b, 0xe3, 0x95, 0x59, 0x40, 0x5e, 0x64, 0xe5, 0x64, 0x3f, 0xfd, 0x21, 0x09,
+ 0x9d, 0xf3, 0xcd, 0xc7, 0xa4, 0x2a, 0xe2, 0x97, 0xdd, 0xe2, 0x4f, 0xb0, 0x7d, 0x7e, 0xf5, 0x8e,
+ 0xd6, 0x4d, 0x84, 0x25, 0x54, 0x41, 0x3f, 0x8f, 0x78, 0x64, 0x1a, 0x51, 0x27, 0x9d, 0x55, 0x8a,
+ 0xe9, 0x90, 0x35, 0xab, 0x39, 0x80, 0x4b, 0x94, 0x40, 0x84, 0xa2, 0xfd, 0x73, 0xeb, 0x35, 0x7a,
+];
+const AUTHORITY_HASH_ANDROID: [u8; HASH_SIZE] = [
+ 0x04, 0x25, 0x5d, 0x60, 0x5f, 0x5c, 0x45, 0x0d, 0xf2, 0x9a, 0x6e, 0x99, 0x30, 0x03, 0xb8, 0xd6,
+ 0xe1, 0x99, 0x71, 0x1b, 0xf8, 0x44, 0xfa, 0xb5, 0x31, 0x79, 0x1c, 0x37, 0x68, 0x4e, 0x1d, 0xc0,
+ 0x24, 0x74, 0x68, 0xf8, 0x80, 0x20, 0x3e, 0x44, 0xb1, 0x43, 0xd2, 0x9c, 0xfc, 0x12, 0x9e, 0x77,
+ 0x0a, 0xde, 0x29, 0x24, 0xff, 0x2e, 0xfa, 0xc7, 0x10, 0xd5, 0x73, 0xd4, 0xc6, 0xdf, 0x62, 0x9f,
+];
+
fn encode_pub_key_ed25519(pub_key: &[u8], stream: &mut dyn Write) -> Result<()> {
cbor::encode_header(5 /* CBOR MAP */, 5, stream)
.context("In encode_pub_key_ed25519: Trying to encode map header.")?;
@@ -64,185 +105,77 @@
Ok(())
}
-/// 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();
- let private_key_seed = dice_ctx
- .derive_cdi_private_key_seed(UDS)
+/// Makes a DICE chain (BCC) from the sample input.
+///
+/// The DICE chain is of the following format:
+/// public key derived from UDS -> ABL certificate -> AVB certificate -> Android certificate
+pub fn make_sample_bcc_and_cdis() -> Result<OwnedDiceArtifacts> {
+ let private_key_seed = 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 input_values_vector = get_input_values_vector();
-
- let (cdi_attest, cdi_seal, mut cert) = dice_ctx
- .main_flow(
- UDS,
- UDS,
- &InputValues::try_from(&input_values_vector[0])
- .context("In make_sample_bcc_and_cdis: Trying to convert input values. (0)")?,
- )
- .context("In make_sample_bcc_and_cdis: Trying to run first main flow.")?;
-
+ // Sets the root public key in DICE chain (BCC).
+ let (public_key, _) = keypair_from_seed(private_key_seed.as_array())
+ .context("In make_sample_bcc_and_cids: Failed to generate key pair.")?;
let mut bcc: Vec<u8> = vec![];
-
cbor::encode_header(4 /* CBOR ARRAY */, 2, &mut bcc)
.context("In make_sample_bcc_and_cdis: Trying to encode array header.")?;
encode_pub_key_ed25519(&public_key, &mut bcc)
.context("In make_sample_bcc_and_cdis: Trying encode pub_key.")?;
+ // Appends ABL certificate to DICE chain.
+ let config_descriptor = retry_bcc_format_config_descriptor(
+ Some(CStr::from_bytes_with_nul(b"ABL\0").unwrap()),
+ Some(1), // version
+ true,
+ )?;
+ let input_values = InputValues::new(
+ CODE_HASH_ABL,
+ Config::Descriptor(config_descriptor.as_slice()),
+ AUTHORITY_HASH_ABL,
+ DiceMode::kDiceModeNormal,
+ HIDDEN_ABL,
+ );
+ let (cdi_values, mut cert) = retry_dice_main_flow(UDS, UDS, &input_values)
+ .context("In make_sample_bcc_and_cdis: Trying to run first main flow.")?;
bcc.append(&mut cert);
- let (cdi_attest, cdi_seal, bcc) = dice_ctx
- .bcc_main_flow(
- &cdi_attest[..].try_into().context(
- "In make_sample_bcc_and_cdis: Failed to convert cdi_attest to array reference. (1)",
- )?,
- &cdi_seal[..].try_into().context(
- "In make_sample_bcc_and_cdis: Failed to convert cdi_seal to array reference. (1)",
- )?,
- &bcc,
- &InputValues::try_from(&input_values_vector[1])
- .context("In make_sample_bcc_and_cdis: Trying to convert input values. (1)")?,
- )
- .context("In make_sample_bcc_and_cdis: Trying to run first bcc main flow.")?;
- dice_ctx
- .bcc_main_flow(
- &cdi_attest[..].try_into().context(
- "In make_sample_bcc_and_cdis: Failed to convert cdi_attest to array reference. (2)",
- )?,
- &cdi_seal[..].try_into().context(
- "In make_sample_bcc_and_cdis: Failed to convert cdi_seal to array reference. (2)",
- )?,
- &bcc,
- &InputValues::try_from(&input_values_vector[2])
- .context("In make_sample_bcc_and_cdis: Trying to convert input values. (2)")?,
- )
- .context("In make_sample_bcc_and_cdis: Trying to run second bcc main flow.")
-}
+ // Appends AVB certificate to DICE chain.
+ let config_descriptor = retry_bcc_format_config_descriptor(
+ Some(CStr::from_bytes_with_nul(b"AVB\0").unwrap()),
+ Some(1), // version
+ true,
+ )?;
+ let input_values = InputValues::new(
+ CODE_HASH_AVB,
+ Config::Descriptor(config_descriptor.as_slice()),
+ AUTHORITY_HASH_AVB,
+ DiceMode::kDiceModeNormal,
+ HIDDEN_AVB,
+ );
+ let dice_artifacts =
+ retry_bcc_main_flow(&cdi_values.cdi_attest, &cdi_values.cdi_seal, &bcc, &input_values)
+ .context("In make_sample_bcc_and_cdis: Trying to run first bcc main flow.")?;
-fn make_input_values(
- code_hash: &[u8; dice::HASH_SIZE],
- authority_hash: &[u8; dice::HASH_SIZE],
- config_name: &str,
- config_version: u64,
- config_resettable: bool,
- mode: Mode,
- hidden: &[u8; dice::HIDDEN_SIZE],
-) -> Result<BinderInputValues> {
- Ok(BinderInputValues {
- codeHash: *code_hash,
- config: BinderConfig {
- desc: dice::bcc::format_config_descriptor(
- Some(config_name),
- Some(config_version),
- config_resettable,
- )
- .context("In make_input_values: Failed to format config descriptor.")?,
- },
- authorityHash: *authority_hash,
- authorityDescriptor: None,
- hidden: *hidden,
- mode,
- })
-}
-
-/// Returns a set of sample input for a dice chain comprising the android boot loader ABL,
-/// the verified boot information AVB, and Android S.
-pub fn get_input_values_vector() -> Vec<BinderInputValues> {
- vec![
- make_input_values(
- &[
- // code hash
- 0x16, 0x48, 0xf2, 0x55, 0x53, 0x23, 0xdd, 0x15, 0x2e, 0x83, 0x38, 0xc3, 0x64, 0x38,
- 0x63, 0x26, 0x0f, 0xcf, 0x5b, 0xd1, 0x3a, 0xd3, 0x40, 0x3e, 0x23, 0xf8, 0x34, 0x4c,
- 0x6d, 0xa2, 0xbe, 0x25, 0x1c, 0xb0, 0x29, 0xe8, 0xc3, 0xfb, 0xb8, 0x80, 0xdc, 0xb1,
- 0xd2, 0xb3, 0x91, 0x4d, 0xd3, 0xfb, 0x01, 0x0f, 0xe4, 0xe9, 0x46, 0xa2, 0xc0, 0x26,
- 0x57, 0x5a, 0xba, 0x30, 0xf7, 0x15, 0x98, 0x14,
- ],
- &[
- // authority hash
- 0xf9, 0x00, 0x9d, 0xc2, 0x59, 0x09, 0xe0, 0xb6, 0x98, 0xbd, 0xe3, 0x97, 0x4a, 0xcb,
- 0x3c, 0xe7, 0x6b, 0x24, 0xc3, 0xe4, 0x98, 0xdd, 0xa9, 0x6a, 0x41, 0x59, 0x15, 0xb1,
- 0x23, 0xe6, 0xc8, 0xdf, 0xfb, 0x52, 0xb4, 0x52, 0xc1, 0xb9, 0x61, 0xdd, 0xbc, 0x5b,
- 0x37, 0x0e, 0x12, 0x12, 0xb2, 0xfd, 0xc1, 0x09, 0xb0, 0xcf, 0x33, 0x81, 0x4c, 0xc6,
- 0x29, 0x1b, 0x99, 0xea, 0xae, 0xfd, 0xaa, 0x0d,
- ],
- "ABL", // config name
- 1, // config version
- true, // resettable
- Mode::NORMAL,
- &[
- // hidden
- 0xa2, 0x01, 0xd0, 0xc0, 0xaa, 0x75, 0x3c, 0x06, 0x43, 0x98, 0x6c, 0xc3, 0x5a, 0xb5,
- 0x5f, 0x1f, 0x0f, 0x92, 0x44, 0x3b, 0x0e, 0xd4, 0x29, 0x75, 0xe3, 0xdb, 0x36, 0xda,
- 0xc8, 0x07, 0x97, 0x4d, 0xff, 0xbc, 0x6a, 0xa4, 0x8a, 0xef, 0xc4, 0x7f, 0xf8, 0x61,
- 0x7d, 0x51, 0x4d, 0x2f, 0xdf, 0x7e, 0x8c, 0x3d, 0xa3, 0xfc, 0x63, 0xd4, 0xd4, 0x74,
- 0x8a, 0xc4, 0x14, 0x45, 0x83, 0x6b, 0x12, 0x7e,
- ],
- )
- .unwrap(),
- make_input_values(
- &[
- // code hash
- 0xa4, 0x0c, 0xcb, 0xc1, 0xbf, 0xfa, 0xcc, 0xfd, 0xeb, 0xf4, 0xfc, 0x43, 0x83, 0x7f,
- 0x46, 0x8d, 0xd8, 0xd8, 0x14, 0xc1, 0x96, 0x14, 0x1f, 0x6e, 0xb3, 0xa0, 0xd9, 0x56,
- 0xb3, 0xbf, 0x2f, 0xfa, 0x88, 0x70, 0x11, 0x07, 0x39, 0xa4, 0xd2, 0xa9, 0x6b, 0x18,
- 0x28, 0xe8, 0x29, 0x20, 0x49, 0x0f, 0xbb, 0x8d, 0x08, 0x8c, 0xc6, 0x54, 0xe9, 0x71,
- 0xd2, 0x7e, 0xa4, 0xfe, 0x58, 0x7f, 0xd3, 0xc7,
- ],
- &[
- // authority hash
- 0xb2, 0x69, 0x05, 0x48, 0x56, 0xb5, 0xfa, 0x55, 0x6f, 0xac, 0x56, 0xd9, 0x02, 0x35,
- 0x2b, 0xaa, 0x4c, 0xba, 0x28, 0xdd, 0x82, 0x3a, 0x86, 0xf5, 0xd4, 0xc2, 0xf1, 0xf9,
- 0x35, 0x7d, 0xe4, 0x43, 0x13, 0xbf, 0xfe, 0xd3, 0x36, 0xd8, 0x1c, 0x12, 0x78, 0x5c,
- 0x9c, 0x3e, 0xf6, 0x66, 0xef, 0xab, 0x3d, 0x0f, 0x89, 0xa4, 0x6f, 0xc9, 0x72, 0xee,
- 0x73, 0x43, 0x02, 0x8a, 0xef, 0xbc, 0x05, 0x98,
- ],
- "AVB", // config name
- 1, // config version
- true, // resettable
- Mode::NORMAL,
- &[
- // hidden
- 0x5b, 0x3f, 0xc9, 0x6b, 0xe3, 0x95, 0x59, 0x40, 0x5e, 0x64, 0xe5, 0x64, 0x3f, 0xfd,
- 0x21, 0x09, 0x9d, 0xf3, 0xcd, 0xc7, 0xa4, 0x2a, 0xe2, 0x97, 0xdd, 0xe2, 0x4f, 0xb0,
- 0x7d, 0x7e, 0xf5, 0x8e, 0xd6, 0x4d, 0x84, 0x25, 0x54, 0x41, 0x3f, 0x8f, 0x78, 0x64,
- 0x1a, 0x51, 0x27, 0x9d, 0x55, 0x8a, 0xe9, 0x90, 0x35, 0xab, 0x39, 0x80, 0x4b, 0x94,
- 0x40, 0x84, 0xa2, 0xfd, 0x73, 0xeb, 0x35, 0x7a,
- ],
- )
- .unwrap(),
- make_input_values(
- &[
- // code hash
- 0; dice::HASH_SIZE
- ],
- &[
- // authority hash
- 0x04, 0x25, 0x5d, 0x60, 0x5f, 0x5c, 0x45, 0x0d, 0xf2, 0x9a, 0x6e, 0x99, 0x30, 0x03,
- 0xb8, 0xd6, 0xe1, 0x99, 0x71, 0x1b, 0xf8, 0x44, 0xfa, 0xb5, 0x31, 0x79, 0x1c, 0x37,
- 0x68, 0x4e, 0x1d, 0xc0, 0x24, 0x74, 0x68, 0xf8, 0x80, 0x20, 0x3e, 0x44, 0xb1, 0x43,
- 0xd2, 0x9c, 0xfc, 0x12, 0x9e, 0x77, 0x0a, 0xde, 0x29, 0x24, 0xff, 0x2e, 0xfa, 0xc7,
- 0x10, 0xd5, 0x73, 0xd4, 0xc6, 0xdf, 0x62, 0x9f,
- ],
- "Android", // config name
- 12, // config version
- true, // resettable
- Mode::NORMAL,
- &[
- // hidden
- 0; dice::HIDDEN_SIZE
- ],
- )
- .unwrap(),
- ]
+ // Appends Android certificate to DICE chain.
+ let config_descriptor = retry_bcc_format_config_descriptor(
+ Some(CStr::from_bytes_with_nul(b"Android\0").unwrap()),
+ Some(12), // version
+ true,
+ )?;
+ let input_values = InputValues::new(
+ [0u8; HASH_SIZE], // code_hash
+ Config::Descriptor(config_descriptor.as_slice()),
+ AUTHORITY_HASH_ANDROID,
+ DiceMode::kDiceModeNormal,
+ [0u8; HIDDEN_SIZE], // hidden
+ );
+ retry_bcc_main_flow(
+ dice_artifacts.cdi_attest(),
+ dice_artifacts.cdi_seal(),
+ dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?,
+ &input_values,
+ )
+ .context("In make_sample_bcc_and_cdis: Trying to run second bcc main flow.")
}
#[cfg(test)]
@@ -250,7 +183,7 @@
use super::*;
// This simple test checks if the invocation succeeds, essentially it tests
- // if the initial bcc is accepted by `DiceContext::bcc_main_flow`.
+ // if the initial bcc is accepted by `diced_open_dice::retry_bcc_main_flow`.
#[test]
fn make_sample_bcc_and_cdis_test() {
make_sample_bcc_and_cdis().unwrap();
diff --git a/diced/src/utils.rs b/diced/src/utils.rs
index 03e8969..0931e68 100644
--- a/diced/src/utils.rs
+++ b/diced/src/utils.rs
@@ -14,217 +14,6 @@
//! Implements utility functions and types for diced and the dice HAL.
-use android_hardware_security_dice::aidl::android::hardware::security::dice::{
- Bcc::Bcc, BccHandover::BccHandover, InputValues::InputValues as BinderInputValues,
- Mode::Mode as BinderMode,
-};
-use anyhow::{Context, Result};
-use dice::ContextImpl;
-use diced_open_dice_cbor as dice;
-use keystore2_crypto::ZVec;
-use std::convert::TryInto;
-
-/// This new type wraps a reference to BinderInputValues and implements the open dice
-/// InputValues trait.
-#[derive(Debug)]
-pub struct InputValues<'a>(&'a BinderInputValues);
-
-impl<'a> From<&'a BinderInputValues> for InputValues<'a> {
- fn from(input_values: &'a BinderInputValues) -> InputValues<'a> {
- Self(input_values)
- }
-}
-
-impl From<&InputValues<'_>> for BinderInputValues {
- fn from(input_values: &InputValues) -> BinderInputValues {
- input_values.0.clone()
- }
-}
-impl From<InputValues<'_>> for BinderInputValues {
- fn from(input_values: InputValues) -> BinderInputValues {
- input_values.0.clone()
- }
-}
-
-impl dice::InputValues for InputValues<'_> {
- fn code_hash(&self) -> &[u8; dice::HASH_SIZE] {
- &self.0.codeHash
- }
-
- fn config(&self) -> dice::Config {
- dice::Config::Descriptor(self.0.config.desc.as_slice())
- }
-
- fn authority_hash(&self) -> &[u8; dice::HASH_SIZE] {
- &self.0.authorityHash
- }
-
- fn authority_descriptor(&self) -> Option<&[u8]> {
- self.0.authorityDescriptor.as_deref()
- }
-
- fn mode(&self) -> dice::Mode {
- match self.0.mode {
- BinderMode::NOT_INITIALIZED => dice::Mode::NotConfigured,
- BinderMode::NORMAL => dice::Mode::Normal,
- BinderMode::DEBUG => dice::Mode::Debug,
- BinderMode::RECOVERY => dice::Mode::Recovery,
- _ => dice::Mode::NotConfigured,
- }
- }
-
- fn hidden(&self) -> &[u8; dice::HIDDEN_SIZE] {
- // If `self` was created using try_from the length was checked and this cannot panic.
- &self.0.hidden
- }
-}
-
-/// Initializes an aidl defined BccHandover object with the arguments `cdi_attest`, `cdi_seal`,
-/// and `bcc`.
-pub fn make_bcc_handover(
- cdi_attest: &[u8; dice::CDI_SIZE],
- cdi_seal: &[u8; dice::CDI_SIZE],
- bcc: &[u8],
-) -> Result<BccHandover> {
- Ok(BccHandover { cdiAttest: *cdi_attest, cdiSeal: *cdi_seal, bcc: Bcc { data: bcc.to_vec() } })
-}
-
-/// ResidentArtifacts stores a set of dice artifacts comprising CDI_ATTEST, CDI_SEAL,
-/// 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.
-pub struct ResidentArtifacts {
- cdi_attest: ZVec,
- cdi_seal: ZVec,
- bcc: Vec<u8>,
-}
-
-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
- /// cannot fail.
- pub fn new(
- cdi_attest: &[u8; dice::CDI_SIZE],
- cdi_seal: &[u8; dice::CDI_SIZE],
- bcc: &[u8],
- ) -> Result<Self> {
- Ok(ResidentArtifacts {
- cdi_attest: cdi_attest[..]
- .try_into()
- .context("In ResidentArtifacts::new: Trying to convert cdi_attest to ZVec.")?,
- cdi_seal: cdi_seal[..]
- .try_into()
- .context("In ResidentArtifacts::new: Trying to convert cdi_seal to ZVec.")?,
- bcc: bcc.to_vec(),
- })
- }
-
- /// Creates a ResidentArtifacts object from another one implementing the DiceArtifacts
- /// trait. Like `new` this function can only create artifacts of appropriate size
- /// 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(),
- })
- }
-
- /// Attempts to clone the artifacts. This operation is fallible due to the fallible
- /// nature of ZVec.
- pub fn try_clone(&self) -> Result<Self> {
- Ok(ResidentArtifacts {
- cdi_attest: self
- .cdi_attest
- .try_clone()
- .context("In ResidentArtifacts::new: Trying to clone cdi_attest.")?,
- cdi_seal: self
- .cdi_seal
- .try_clone()
- .context("In ResidentArtifacts::new: Trying to clone cdi_seal.")?,
- bcc: self.bcc.clone(),
- })
- }
-
- /// Deconstruct the Artifacts into a tuple.
- /// (CDI_ATTEST, CDI_SEAL, BCC)
- pub fn into_tuple(self) -> (ZVec, ZVec, Vec<u8>) {
- let ResidentArtifacts { cdi_attest, cdi_seal, bcc } = self;
- (cdi_attest, cdi_seal, bcc)
- }
-
- fn execute_step(self, input_values: &dyn dice::InputValues) -> Result<Self> {
- let ResidentArtifacts { cdi_attest, cdi_seal, bcc } = self;
-
- let (cdi_attest, cdi_seal, bcc) = dice::OpenDiceCborContext::new()
- .bcc_main_flow(
- cdi_attest[..].try_into().with_context(|| {
- format!("Trying to convert cdi_attest. (length: {})", cdi_attest.len())
- })?,
- cdi_seal[..].try_into().with_context(|| {
- format!("Trying to convert cdi_seal. (length: {})", cdi_seal.len())
- })?,
- &bcc,
- input_values,
- )
- .context("In ResidentArtifacts::execute_step:")?;
- Ok(ResidentArtifacts { cdi_attest, cdi_seal, bcc })
- }
-
- /// Iterate through the iterator of dice input values performing one
- /// BCC main flow step on each element.
- pub fn execute_steps<'a, Iter>(self, input_values: Iter) -> Result<Self>
- where
- Iter: IntoIterator<Item = &'a dyn dice::InputValues>,
- {
- input_values
- .into_iter()
- .try_fold(self, |acc, input_values| acc.execute_step(input_values))
- .context("In ResidentArtifacts::execute_step:")
- }
-}
-
-/// 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 {
- /// With artifacts provides access to the stored artifacts for the duration
- /// of the function call by means of calling the callback.
- fn with_artifacts<F, T>(&self, f: F) -> Result<T>
- where
- F: FnOnce(&dyn DiceArtifacts) -> Result<T>;
-
- /// Consumes the object and returns a an updated version of itself.
- fn update(self, new_artifacts: &impl DiceArtifacts) -> Result<Self>
- where
- Self: Sized;
-}
-
-impl DiceArtifacts for ResidentArtifacts {
- fn cdi_attest(&self) -> &[u8; dice::CDI_SIZE] {
- self.cdi_attest[..].try_into().unwrap()
- }
- fn cdi_seal(&self) -> &[u8; dice::CDI_SIZE] {
- self.cdi_seal[..].try_into().unwrap()
- }
- fn bcc(&self) -> Vec<u8> {
- self.bcc.clone()
- }
-}
-
/// This submodule implements a limited set of CBOR generation functionality. Essentially,
/// a cbor header generator and some convenience functions for number and BSTR encoding.
pub mod cbor {
@@ -256,18 +45,17 @@
pub fn encode_header(t: u8, n: u64, buffer: &mut dyn Write) -> Result<()> {
match n {
n if n < 24 => {
- let written = buffer
- .write(&u8::to_be_bytes(((t as u8) << 5) | (n as u8 & 0x1F)))
- .with_context(|| {
- format!("In encode_header: Failed to write header ({}, {})", t, n)
- })?;
+ let written =
+ buffer.write(&u8::to_be_bytes((t << 5) | (n as u8 & 0x1F))).with_context(
+ || format!("In encode_header: Failed to write header ({}, {})", t, n),
+ )?;
if written != 1 {
return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
}
}
n if n <= 0xFF => {
let written =
- buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (24u8 & 0x1F))).with_context(
+ buffer.write(&u8::to_be_bytes((t << 5) | (24u8 & 0x1F))).with_context(
|| format!("In encode_header: Failed to write header ({}, {})", t, n),
)?;
if written != 1 {
@@ -286,7 +74,7 @@
}
n if n <= 0xFFFF => {
let written =
- buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (25u8 & 0x1F))).with_context(
+ buffer.write(&u8::to_be_bytes((t << 5) | (25u8 & 0x1F))).with_context(
|| format!("In encode_header: Failed to write header ({}, {})", t, n),
)?;
if written != 1 {
@@ -305,7 +93,7 @@
}
n if n <= 0xFFFFFFFF => {
let written =
- buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (26u8 & 0x1F))).with_context(
+ buffer.write(&u8::to_be_bytes((t << 5) | (26u8 & 0x1F))).with_context(
|| format!("In encode_header: Failed to write header ({}, {})", t, n),
)?;
if written != 1 {
@@ -324,13 +112,13 @@
}
n => {
let written =
- buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (27u8 & 0x1F))).with_context(
+ buffer.write(&u8::to_be_bytes((t << 5) | (27u8 & 0x1F))).with_context(
|| format!("In encode_header: Failed to write header ({}, {})", t, n),
)?;
if written != 1 {
return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
}
- let written = buffer.write(&u64::to_be_bytes(n as u64)).with_context(|| {
+ let written = buffer.write(&u64::to_be_bytes(n)).with_context(|| {
format!("In encode_header: Failed to write size ({}, {})", t, n)
})?;
if written != 8 {
diff --git a/identity/Android.bp b/identity/Android.bp
index 7b81685..4f203e6 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -47,7 +47,7 @@
"android.hardware.identity-support-lib",
"android.hardware.keymaster@4.0",
"android.security.authorization-ndk",
- "android.security.remoteprovisioning-cpp",
+ "android.security.remoteprovisioning-cpp",
"libbase",
"libbinder",
"libbinder_ndk",
@@ -59,7 +59,6 @@
"libutils",
"libutilscallstack",
"libvintf",
- "server_configurable_flags",
],
static_libs: [
"android.hardware.security.rkp-V3-cpp",
diff --git a/identity/CredentialData.cpp b/identity/CredentialData.cpp
index fb08333..1bf1527 100644
--- a/identity/CredentialData.cpp
+++ b/identity/CredentialData.cpp
@@ -581,13 +581,17 @@
vector<vector<uint8_t>> keysNeedingCert;
- int64_t nowMilliSeconds =
- std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) * 1000;
+ time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+ int64_t nowMilliseconds;
+ if (__builtin_mul_overflow(int64_t(now), int64_t(1000), &nowMilliseconds)) {
+ LOG(ERROR) << "Overflow converting " << now << " to milliseconds";
+ return {};
+ }
for (AuthKeyData& data : authKeyDatas_) {
bool keyExceedUseCount = (data.useCount >= maxUsesPerKey_);
int64_t expirationDateAdjusted = data.expirationDateMillisSinceEpoch - minValidTimeMillis_;
- bool keyBeyondAdjustedExpirationDate = (nowMilliSeconds > expirationDateAdjusted);
+ bool keyBeyondAdjustedExpirationDate = (nowMilliseconds > expirationDateAdjusted);
bool newKeyNeeded =
(data.certificate.size() == 0) || keyExceedUseCount || keyBeyondAdjustedExpirationDate;
bool certificationPending = (data.pendingCertificate.size() > 0);
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index eb9bdb6..e2b3cf4 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -20,18 +20,19 @@
#include <optional>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
#include <android/hardware/security/keymint/RpcHardwareInfo.h>
#include <android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.h>
#include <android/security/remoteprovisioning/RemotelyProvisionedKey.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
-#include <server_configurable_flags/get_flags.h>
#include <vintf/VintfObject.h>
#include "Credential.h"
#include "CredentialData.h"
#include "CredentialStore.h"
+#include "RemotelyProvisionedKey.h"
#include "Session.h"
#include "Util.h"
#include "WritableCredential.h"
@@ -45,10 +46,8 @@
using ::android::security::rkp::IRemoteProvisioning;
bool useRkpd() {
- std::string useRkpdFlagValue = server_configurable_flags::GetServerConfigurableFlag(
- "remote_key_provisioning_native", "enable_rkpd",
- /*default_value=*/"false");
- return useRkpdFlagValue == "true";
+ return android::base::GetBoolProperty("remote_provisioning.enable_rkpd",
+ /*default_value=*/true);
}
} // namespace
@@ -70,31 +69,14 @@
LOG(ERROR) << "Error getting remotely provisioned component: " << status;
return false;
}
- useRkpd_ = useRkpd();
-
- if (useRkpd_) {
- uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
- auto rpcKeyFuture = getRpcKeyFuture(rpc_, callingUid);
- if (!rpcKeyFuture) {
- LOG(ERROR) << "Error in getRpcKeyFuture()";
- return false;
- }
- rpcKeyFuture_ = std::move(*rpcKeyFuture);
- } else {
- keyPool_ = android::waitForService<IRemotelyProvisionedKeyPool>(
- IRemotelyProvisionedKeyPool::descriptor);
- if (!keyPool_) {
- LOG(ERROR) << "Error getting IRemotelyProvisionedKeyPool HAL with service name '"
- << IRemotelyProvisionedKeyPool::descriptor << "'";
- return false;
- }
- }
}
LOG(INFO) << "Connected to Identity Credential HAL with API version " << halApiVersion_
<< " and name '" << hwInfo_.credentialStoreName << "' authored by '"
<< hwInfo_.credentialStoreAuthorName << "' with chunk size " << hwInfo_.dataChunkSize
- << " and directoAccess set to " << (hwInfo_.isDirectAccess ? "true" : "false");
+ << " directoAccess set to " << (hwInfo_.isDirectAccess ? "true" : "false")
+ << " and remote key provisioning support "
+ << (hwInfo_.isRemoteKeyProvisioningSupported ? "enabled" : "disabled");
return true;
}
@@ -140,7 +122,9 @@
if (hwInfo_.isRemoteKeyProvisioningSupported) {
status = setRemotelyProvisionedAttestationKey(halWritableCredential.get());
if (!status.isOk()) {
- return halStatusToGenericError(status);
+ LOG(WARNING) << status.toString8()
+ << "\nUnable to fetch remotely provisioned attestation key, falling back "
+ << "to the factory-provisioned attestation key.";
}
}
@@ -205,13 +189,21 @@
std::vector<uint8_t> encodedCertChain;
Status status;
- if (useRkpd_) {
- if (rpcKeyFuture_.wait_for(std::chrono::seconds(10)) != std::future_status::ready) {
+ if (useRkpd()) {
+ LOG(INFO) << "Fetching attestation key from RKPD";
+
+ uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
+ auto rpcKeyFuture = getRpcKeyFuture(rpc_, callingUid);
+ if (!rpcKeyFuture) {
+ return Status::fromServiceSpecificError(ERROR_GENERIC, "Error in getRpcKeyFuture()");
+ }
+
+ if (rpcKeyFuture->wait_for(std::chrono::seconds(10)) != std::future_status::ready) {
return Status::fromServiceSpecificError(
ERROR_GENERIC, "Waiting for remotely provisioned attestation key timed out");
}
- std::optional<::android::security::rkp::RemotelyProvisionedKey> key = rpcKeyFuture_.get();
+ std::optional<::android::security::rkp::RemotelyProvisionedKey> key = rpcKeyFuture->get();
if (!key) {
return Status::fromServiceSpecificError(
ERROR_GENERIC, "Failed to get remotely provisioned attestation key");
@@ -225,6 +217,16 @@
keyBlob = std::move(key->keyBlob);
encodedCertChain = std::move(key->encodedCertChain);
} else {
+ LOG(INFO) << "Fetching attestation key from remotely provisioned key pool.";
+
+ sp<IRemotelyProvisionedKeyPool> keyPool =
+ android::waitForService<IRemotelyProvisionedKeyPool>(
+ IRemotelyProvisionedKeyPool::descriptor);
+ if (!keyPool) {
+ return Status::fromServiceSpecificError(
+ ERROR_GENERIC, "Error getting IRemotelyProvisionedKeyPool HAL");
+ }
+
std::optional<std::string> rpcId = getRpcId(rpc_);
if (!rpcId) {
return Status::fromServiceSpecificError(
@@ -233,11 +235,9 @@
uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
::android::security::remoteprovisioning::RemotelyProvisionedKey key;
- Status status = keyPool_->getAttestationKey(callingUid, *rpcId, &key);
+ Status status = keyPool->getAttestationKey(callingUid, *rpcId, &key);
if (!status.isOk()) {
- LOG(WARNING) << "Unable to fetch remotely provisioned attestation key, falling back "
- << "to the factory-provisioned attestation key.";
- return Status::ok();
+ return status;
}
keyBlob = std::move(key.keyBlob);
diff --git a/identity/CredentialStore.h b/identity/CredentialStore.h
index 495841b..57c94e0 100644
--- a/identity/CredentialStore.h
+++ b/identity/CredentialStore.h
@@ -17,7 +17,6 @@
#ifndef SYSTEM_SECURITY_CREDENTIAL_STORE_H_
#define SYSTEM_SECURITY_CREDENTIAL_STORE_H_
-#include <future>
#include <string>
#include <vector>
@@ -26,8 +25,6 @@
#include <android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.h>
#include <android/security/rkp/IRemoteProvisioning.h>
-#include "RemotelyProvisionedKey.h"
-
namespace android {
namespace security {
namespace identity {
@@ -80,10 +77,7 @@
HardwareInformation hwInfo_;
- bool useRkpd_;
sp<IRemotelyProvisionedComponent> rpc_;
- sp<IRemotelyProvisionedKeyPool> keyPool_;
- std::future<std::optional<RemotelyProvisionedKey>> rpcKeyFuture_;
};
} // namespace identity
diff --git a/identity/RemotelyProvisionedKey.cpp b/identity/RemotelyProvisionedKey.cpp
index 46a42f4..784a680 100644
--- a/identity/RemotelyProvisionedKey.cpp
+++ b/identity/RemotelyProvisionedKey.cpp
@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include <android/security/rkp/BnGetKeyCallback.h>
#include <android/security/rkp/BnGetRegistrationCallback.h>
+#include <android/security/rkp/IGetKeyCallback.h>
#include <android/security/rkp/IRemoteProvisioning.h>
#include <binder/IServiceManager.h>
#include <binder/Status.h>
@@ -38,10 +39,13 @@
using ::android::hardware::security::keymint::RpcHardwareInfo;
using ::android::security::rkp::BnGetKeyCallback;
using ::android::security::rkp::BnGetRegistrationCallback;
+using ::android::security::rkp::IGetKeyCallback;
using ::android::security::rkp::IRegistration;
using ::android::security::rkp::IRemoteProvisioning;
using ::android::security::rkp::RemotelyProvisionedKey;
+constexpr const char* kRemoteProvisioningServiceName = "remote_provisioning";
+
std::optional<String16> findRpcNameById(std::string_view targetRpcId) {
auto deviceManifest = vintf::VintfObject::GetDeviceHalManifest();
auto instances = deviceManifest->getAidlInstances("android.hardware.security.keymint",
@@ -94,11 +98,11 @@
keyPromise_.set_value(std::nullopt);
return Status::ok();
}
- Status onError(const String16& error) override {
+ Status onError(IGetKeyCallback::ErrorCode error, const String16& description) override {
if (called_.test_and_set()) {
return Status::ok();
}
- LOG(ERROR) << "GetKeyCallback failed: " << error;
+ LOG(ERROR) << "GetKeyCallback failed: " << static_cast<int>(error) << ", " << description;
keyPromise_.set_value(std::nullopt);
return Status::ok();
}
@@ -122,7 +126,8 @@
auto cb = sp<GetKeyCallback>::make(std::move(keyPromise_));
auto status = registration->getKey(keyId_, cb);
if (!status.isOk()) {
- cb->onError(String16("Failed to register GetKeyCallback"));
+ cb->onError(IGetKeyCallback::ErrorCode::ERROR_UNKNOWN,
+ String16("Failed to register GetKeyCallback"));
}
return Status::ok();
}
@@ -182,7 +187,7 @@
}
sp<IRemoteProvisioning> remoteProvisioning =
- android::waitForService<IRemoteProvisioning>(IRemoteProvisioning::descriptor);
+ android::waitForService<IRemoteProvisioning>(String16(kRemoteProvisioningServiceName));
if (!remoteProvisioning) {
LOG(ERROR) << "Failed to get IRemoteProvisioning HAL";
return std::nullopt;
diff --git a/identity/main.cpp b/identity/main.cpp
index 2559789..b3a41ec 100644
--- a/identity/main.cpp
+++ b/identity/main.cpp
@@ -23,6 +23,7 @@
#include <android-base/logging.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
#include "CredentialStoreFactory.h"
@@ -32,6 +33,7 @@
using ::android::IPCThreadState;
using ::android::IServiceManager;
+using ::android::ProcessState;
using ::android::sp;
using ::android::String16;
using ::android::base::InitLogging;
@@ -53,8 +55,10 @@
CHECK(ret == ::android::OK) << "Couldn't register binder service";
LOG(INFO) << "Registered binder service";
- // Credstore is a single-threaded process. So devote the main thread
- // to handling binder messages.
+ // Credstore needs one thread to handle binder messages and one to handle
+ // asynchronous responses from RKPD.
+ ProcessState::self()->setThreadPoolMaxThreadCount(2);
+ ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return 0;
diff --git a/keystore-engine/keystore2_engine.cpp b/keystore-engine/keystore2_engine.cpp
index 69caf51..dc90be3 100644
--- a/keystore-engine/keystore2_engine.cpp
+++ b/keystore-engine/keystore2_engine.cpp
@@ -146,11 +146,13 @@
return nullptr;
}
- rsa->n = BN_dup(public_rsa->n);
- rsa->e = BN_dup(public_rsa->e);
- if (rsa->n == nullptr || rsa->e == nullptr) {
+ bssl::UniquePtr<BIGNUM> n(BN_dup(RSA_get0_n(public_rsa)));
+ bssl::UniquePtr<BIGNUM> e(BN_dup(RSA_get0_e(public_rsa)));
+ if (n == nullptr || e == nullptr || !RSA_set0_key(rsa.get(), n.get(), e.get(), nullptr)) {
return nullptr;
}
+ OWNERSHIP_TRANSFERRED(n);
+ OWNERSHIP_TRANSFERRED(e);
bssl::UniquePtr<EVP_PKEY> result(EVP_PKEY_new());
if (result.get() == nullptr || !EVP_PKEY_assign_RSA(result.get(), rsa.get())) {
@@ -420,19 +422,19 @@
Keystore2KeyBackend{response.metadata.key, response.iSecurityLevel});
bssl::UniquePtr<EVP_PKEY> result;
- switch (EVP_PKEY_type(pkey->type)) {
+ switch (EVP_PKEY_id(pkey.get())) {
case EVP_PKEY_RSA: {
- bssl::UniquePtr<RSA> public_rsa(EVP_PKEY_get1_RSA(pkey.get()));
- result = wrap_rsa(key_backend, public_rsa.get());
+ RSA* public_rsa = EVP_PKEY_get0_RSA(pkey.get());
+ result = wrap_rsa(key_backend, public_rsa);
break;
}
case EVP_PKEY_EC: {
- bssl::UniquePtr<EC_KEY> public_ecdsa(EVP_PKEY_get1_EC_KEY(pkey.get()));
- result = wrap_ecdsa(key_backend, public_ecdsa.get());
+ EC_KEY* public_ecdsa = EVP_PKEY_get0_EC_KEY(pkey.get());
+ result = wrap_ecdsa(key_backend, public_ecdsa);
break;
}
default:
- LOG(ERROR) << AT << "Unsupported key type " << EVP_PKEY_type(pkey->type);
+ LOG(ERROR) << AT << "Unsupported key type " << EVP_PKEY_id(pkey.get());
return nullptr;
}
diff --git a/keystore/keystore_cli_v2.cpp b/keystore/keystore_cli_v2.cpp
index d01c67d..ab3e22c 100644
--- a/keystore/keystore_cli_v2.cpp
+++ b/keystore/keystore_cli_v2.cpp
@@ -59,6 +59,16 @@
constexpr const char keystore2_service_name[] = "android.system.keystore2.IKeystoreService/default";
+std::string string_replace_all(std::string str, const std::string& from,
+ const std::string& to) {
+ size_t start = 0;
+ while ((start = str.find(from, start)) != std::string::npos) {
+ str.replace(start, from.length(), to);
+ start += to.length();
+ }
+ return str;
+}
+
int unwrapError(const ndk::ScopedAStatus& status) {
if (status.isOk()) return 0;
if (status.getExceptionCode() == EX_SERVICE_SPECIFIC) {
@@ -1028,7 +1038,8 @@
auto listener = ndk::SharedRefBase::make<ConfirmationListener>();
auto future = listener->get_future();
- auto rc = apcService->presentPrompt(listener, promptText, extraData, locale, uiOptionsAsFlags);
+ auto rc = apcService->presentPrompt(listener, string_replace_all(promptText, "\\n", "\n"),
+ extraData, locale, uiOptionsAsFlags);
if (!rc.isOk()) {
std::cerr << "Presenting confirmation prompt failed: " << rc.getDescription() << std::endl;
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index fa80563..d8c0081 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -44,7 +44,6 @@
"android.security.rkp_aidl-rust",
"libanyhow",
"libbinder_rs",
- "libfutures",
"libkeystore2_aaid-rust",
"libkeystore2_apc_compat-rust",
"libkeystore2_crypto_rust",
@@ -60,6 +59,7 @@
"libserde",
"libserde_cbor",
"libthiserror",
+ "libtokio",
],
shared_libs: [
"libcutils",
@@ -158,6 +158,7 @@
"watchdog",
"keystore2_blob_test_utils",
],
+ require_root: true,
}
rust_defaults {
diff --git a/keystore2/aaid/lib.rs b/keystore2/aaid/lib.rs
index 3187198..8f6a09e 100644
--- a/keystore2/aaid/lib.rs
+++ b/keystore2/aaid/lib.rs
@@ -29,7 +29,7 @@
// in the second pointer argument.
let status = unsafe { aaid_keystore_attestation_id(uid, buffer.as_mut_ptr(), &mut size) };
match status {
- 0 => Ok(buffer[0..size as usize].to_vec()),
+ 0 => Ok(buffer[0..size].to_vec()),
status => Err(status),
}
}
diff --git a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
index 3f33431..e3b7d11 100644
--- a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
+++ b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
@@ -41,8 +41,26 @@
/**
* Unlocks the keystore for the given user id.
+ *
* Callers require 'Unlock' permission.
- * If a password was set, a password must be given on unlock or the operation fails.
+ *
+ * Super-Encryption Key:
+ * When the device is unlocked (and password is non-null), Keystore stores in memory
+ * a super-encryption key derived from the password that protects UNLOCKED_DEVICE_REQUIRED
+ * keys; this key is wiped from memory when the device is locked.
+ *
+ * If unlockingSids is non-empty on lock, then before the super-encryption key is wiped from
+ * memory, a copy of it is stored in memory encrypted with a fresh AES key. This key is then
+ * imported into KM, tagged such that it can be used given a valid, recent auth token for any
+ * of the unlockingSids.
+ *
+ * Options for unlock:
+ * - If the password is non-null, the super-encryption key is re-derived as above.
+ * - If the password is null, then if a suitable auth token to access the encrypted
+ * Super-encryption key stored in KM has been sent to keystore (via addAuthToken), the
+ * encrypted super-encryption key is recovered so that UNLOCKED_DEVICE_REQUIRED keys can
+ * be used once again.
+ * - If neither of these are met, then the operation fails.
*
* ## Error conditions:
* `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'Unlock' permission.
@@ -50,33 +68,10 @@
* `ResponseCode::VALUE_CORRUPTED` - if the super key can not be decrypted.
* `ResponseCode::KEY_NOT_FOUND` - if the super key is not found.
*
- * @lockScreenEvent - Indicates what happened.
- * * LockScreenEvent.UNLOCK if the screen was unlocked.
- * * LockScreenEvent.LOCK if the screen was locked.
- *
- * @param userId - Android user id
- *
- * @param password - synthetic password derived by the user denoted by the user id
- *
- * @param unlockingSids - list of biometric SIDs for this user. This will be null when
- * lockScreenEvent is UNLOCK, but may be non-null when
- * lockScreenEvent is LOCK.
- *
- * When the device is unlocked, Keystore stores in memory
- * a super-encryption key that protects UNLOCKED_DEVICE_REQUIRED
- * keys; this key is wiped from memory when the device is locked.
- *
- * If unlockingSids is non-empty on lock, then before the
- * super-encryption key is wiped from memory, a copy of it
- * is stored in memory encrypted with a fresh AES key.
- * This key is then imported into KM, tagged such that it can be
- * used given a valid, recent auth token for any of the
- * unlockingSids.
- *
- * Then, when the device is unlocked again, if a suitable auth token
- * has been sent to keystore, it is used to recover the
- * super-encryption key, so that UNLOCKED_DEVICE_REQUIRED keys can
- * be used once again.
+ * @param lockScreenEvent whether the lock screen locked or unlocked
+ * @param userId android user id
+ * @param password synthetic password derived from the user's LSKF, must be null on lock
+ * @param unlockingSids list of biometric SIDs for this user, ignored on unlock
*/
void onLockScreenEvent(in LockScreenEvent lockScreenEvent, in int userId,
in @nullable byte[] password, in @nullable long[] unlockingSids);
diff --git a/keystore2/apc_compat/apc_compat.rs b/keystore2/apc_compat/apc_compat.rs
index 9f44927..480f14d 100644
--- a/keystore2/apc_compat/apc_compat.rs
+++ b/keystore2/apc_compat/apc_compat.rs
@@ -94,7 +94,7 @@
// If the pointer and size is not nullptr and not 0 respectively, the C/C++
// implementation must pass a valid pointer to an allocation of at least size bytes,
// and the pointer must be valid until this function returns.
- unsafe { slice::from_raw_parts(tbs_message, s as usize) },
+ unsafe { slice::from_raw_parts(tbs_message, s) },
),
};
let confirmation_token = match (confirmation_token.is_null(), confirmation_token_size) {
@@ -104,7 +104,7 @@
// If the pointer and size is not nullptr and not 0 respectively, the C/C++
// implementation must pass a valid pointer to an allocation of at least size bytes,
// and the pointer must be valid until this function returns.
- unsafe { slice::from_raw_parts(confirmation_token, s as usize) },
+ unsafe { slice::from_raw_parts(confirmation_token, s) },
),
};
hal_cb(rc, tbs_message, confirmation_token)
@@ -178,7 +178,7 @@
cb,
prompt_text.as_ptr(),
extra_data.as_ptr(),
- extra_data.len() as usize,
+ extra_data.len(),
locale.as_ptr(),
ui_opts,
)
diff --git a/keystore2/legacykeystore/lib.rs b/keystore2/legacykeystore/lib.rs
index ed5bd4f..464f0a2 100644
--- a/keystore2/legacykeystore/lib.rs
+++ b/keystore2/legacykeystore/lib.rs
@@ -502,10 +502,8 @@
) -> Result<bool> {
let blob = legacy_loader
.read_legacy_keystore_entry(uid, alias, |ciphertext, iv, tag, _salt, _key_size| {
- if let Some(key) = SUPER_KEY
- .read()
- .unwrap()
- .get_per_boot_key_by_user_id(uid_to_android_user(uid as u32))
+ if let Some(key) =
+ SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(uid))
{
key.decrypt(ciphertext, iv, tag)
} else {
diff --git a/keystore2/src/async_task.rs b/keystore2/src/async_task.rs
index 0515c8f..6548445 100644
--- a/keystore2/src/async_task.rs
+++ b/keystore2/src/async_task.rs
@@ -67,7 +67,7 @@
pub fn get_mut<T: Any + Send + Default>(&mut self) -> &mut T {
self.0
.entry(TypeId::of::<T>())
- .or_insert_with(|| Box::new(T::default()) as Box<dyn Any + Send>)
+ .or_insert_with(|| Box::<T>::default() as Box<dyn Any + Send>)
.downcast_mut::<T>()
.unwrap()
}
diff --git a/keystore2/src/attestation_key_utils.rs b/keystore2/src/attestation_key_utils.rs
index d01cf86..8c4cdea 100644
--- a/keystore2/src/attestation_key_utils.rs
+++ b/keystore2/src/attestation_key_utils.rs
@@ -30,6 +30,7 @@
};
use anyhow::{Context, Result};
use keystore2_crypto::parse_subject_from_certificate;
+use rustutils::system_properties;
/// KeyMint takes two different kinds of attestation keys. Remote provisioned keys
/// and those that have been generated by the user. Unfortunately, they need to be
@@ -53,9 +54,9 @@
}
fn use_rkpd() -> bool {
- let property_name = "persist.device_config.remote_key_provisioning_native.enable_rkpd";
- let default_value = false;
- rustutils::system_properties::read_bool(property_name, 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/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index 7ba47c8..08b7589 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -360,8 +360,7 @@
// Safety: the key is valid.
// This will not write past the specified length of the buffer; if the
// len above is too short, it returns 0.
- let written_len =
- unsafe { ECKEYMarshalPrivateKey(key.0, buf.as_mut_ptr(), buf.len()) } as usize;
+ let written_len = unsafe { ECKEYMarshalPrivateKey(key.0, buf.as_mut_ptr(), buf.len()) };
if written_len == len {
Ok(buf)
} else {
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 62fd579..c9c28f6 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -830,7 +830,7 @@
pub fn satisfies(&self, user_secure_ids: &[i64], auth_type: HardwareAuthenticatorType) -> bool {
user_secure_ids.iter().any(|&sid| {
(sid == self.auth_token.userId || sid == self.auth_token.authenticatorId)
- && (((auth_type.0 as i32) & (self.auth_token.authenticatorType.0 as i32)) != 0)
+ && ((auth_type.0 & self.auth_token.authenticatorType.0) != 0)
})
}
@@ -1859,7 +1859,8 @@
let (_, hw_info) = get_keymint_dev_by_uuid(km_uuid)
.context("Error in retrieving keymint device by UUID.")?;
log_rkp_error_stats(MetricsRkpError::OUT_OF_KEYS, &hw_info.securityLevel);
- return Err(KsError::Rc(ResponseCode::OUT_OF_KEYS)).context("Out of keys.");
+ return Err(KsError::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
+ .context("Out of keys.");
} else if result > 1 {
return Err(KsError::sys())
.context(format!("Expected to update 1 entry, instead updated {}", result));
@@ -4562,7 +4563,7 @@
DESTINATION_UID,
|k, av| {
assert_eq!(Domain::SELINUX, k.domain);
- assert_eq!(DESTINATION_NAMESPACE as i64, k.nspace);
+ assert_eq!(DESTINATION_NAMESPACE, k.nspace);
assert!(av.is_none());
Ok(())
},
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index b60b64f..3ca3942 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -71,11 +71,6 @@
pub fn perm() -> Self {
Error::Rc(ResponseCode::PERMISSION_DENIED)
}
-
- /// Short hand for `Error::Rc(ResponseCode::OUT_OF_KEYS)`
- pub fn out_of_keys() -> Self {
- Error::Rc(ResponseCode::OUT_OF_KEYS)
- }
}
/// Helper function to map the binder status we get from calls into KeyMint
diff --git a/keystore2/src/fuzzers/Android.bp b/keystore2/src/fuzzers/Android.bp
index 3adb922..9a2d98d 100644
--- a/keystore2/src/fuzzers/Android.bp
+++ b/keystore2/src/fuzzers/Android.bp
@@ -37,3 +37,30 @@
componentid: 155276,
},
}
+
+
+rust_fuzz {
+ name: "authorization_service_fuzzer",
+ srcs: ["aidl-fuzzers/authorization_service_fuzzer.rs"],
+ rustlibs: [
+ "libkeystore2",
+ "libkeystore2_crypto_rust",
+ "libkeystore2_vintf_rust",
+ "libkeystore2_aaid-rust",
+ "libkeystore2_apc_compat-rust",
+ "libkeystore2_selinux",
+ "libbinder_rs",
+ "libbinder_random_parcel_rs",
+ ],
+ fuzz_config: {
+ fuzz_on_haiku_device: true,
+ fuzz_on_haiku_host: false,
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ "smoreland@google.com",
+ "waghpawan@google.com"
+ ],
+ // Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer
+ hotlists: ["4637097"],
+ },
+}
diff --git a/keystore2/src/fuzzers/aidl-fuzzers/authorization_service_fuzzer.rs b/keystore2/src/fuzzers/aidl-fuzzers/authorization_service_fuzzer.rs
new file mode 100644
index 0000000..c1b2098
--- /dev/null
+++ b/keystore2/src/fuzzers/aidl-fuzzers/authorization_service_fuzzer.rs
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#![allow(missing_docs)]
+#![no_main]
+#[macro_use]
+extern crate libfuzzer_sys;
+
+use binder_random_parcel_rs::fuzz_service;
+use keystore2::authorization::AuthorizationManager;
+
+fuzz_target!(|data: &[u8]| {
+ let authorization_service = AuthorizationManager::new_native_binder().unwrap_or_else(|e| {
+ panic!("Failed to create android.security.authorization service because of {:?}", e);
+ });
+ fuzz_service(&mut authorization_service.as_binder(), data);
+});
diff --git a/keystore2/src/fuzzers/keystore2_unsafe_fuzzer.rs b/keystore2/src/fuzzers/keystore2_unsafe_fuzzer.rs
index 4c2419a..1a385e7 100644
--- a/keystore2/src/fuzzers/keystore2_unsafe_fuzzer.rs
+++ b/keystore2/src/fuzzers/keystore2_unsafe_fuzzer.rs
@@ -16,8 +16,6 @@
#![feature(slice_internals)]
#![no_main]
-#[macro_use]
-extern crate libfuzzer_sys;
use core::slice::memchr;
use keystore2::{legacy_blob::LegacyBlobLoader, utils::ui_opts_2_compat};
@@ -31,7 +29,7 @@
};
use keystore2_selinux::{check_access, getpidcon, setcon, Backend, Context, KeystoreKeyBackend};
use keystore2_vintf::{get_aidl_instances, get_hidl_instances};
-use libfuzzer_sys::arbitrary::Arbitrary;
+use libfuzzer_sys::{arbitrary::Arbitrary, fuzz_target};
use std::{ffi::CString, sync::Arc};
// Avoid allocating too much memory and crashing the fuzzer.
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index 7cf1819..2ffcc71 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -321,7 +321,7 @@
acc.push(c as char);
}
c => {
- acc.push((b'+' + (c as u8 >> 6)) as char);
+ acc.push((b'+' + (c >> 6)) as char);
acc.push((b'0' + (c & 0x3F)) as char);
}
};
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index cb2962a..1a83339 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -138,8 +138,8 @@
/// (2) if remote provisioning is present and enabled on the system. If these conditions are
/// met, it makes an attempt to fetch the attestation key assigned to the `caller_uid`.
///
- /// It returns the ResponseCode `OUT_OF_KEYS` if there is not one key currently assigned to the
- /// `caller_uid` and there are none available to assign.
+ /// It returns the ResponseCode `OUT_OF_KEYS_TRANSIENT_ERROR` if there is not one key currently
+ /// assigned to the `caller_uid` and there are none available to assign.
pub fn get_remotely_provisioned_attestation_key_and_certs(
&self,
key: &KeyDescriptor,
@@ -490,7 +490,7 @@
/// Fetches a remote provisioning attestation key and certificate chain inside of the
/// returned `CertificateChain` struct if one exists for the given caller_uid. If one has not
/// been assigned, this function will assign it. If there are no signed attestation keys
-/// available to be assigned, it will return the ResponseCode `OUT_OF_KEYS`
+/// available to be assigned, it will return the ResponseCode `OUT_OF_KEYS_TRANSIENT_ERROR`
fn get_rem_prov_attest_key(
domain: Domain,
caller_uid: u32,
@@ -645,7 +645,7 @@
/// Fetches a remotely provisioned certificate chain and key for the given client uid that
/// was provisioned using the IRemotelyProvisionedComponent with the given id. The same key
/// will be returned for a given caller_uid on every request. If there are no attestation keys
- /// available, `OUT_OF_KEYS` is returned.
+ /// available, `OUT_OF_KEYS_TRANSIENT_ERROR` is returned.
fn get_attestation_key(
&self,
db: &mut KeystoreDB,
@@ -671,7 +671,7 @@
}),
// It should be impossible to get `None`, but handle it just in case as a
// precaution against future behavioral changes in `get_rem_prov_attest_key`.
- None => Err(error::Error::Rc(ResponseCode::OUT_OF_KEYS))
+ None => Err(error::Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
.context(ks_err!("No available attestation keys")),
}
}
@@ -958,7 +958,7 @@
.unwrap_err()
.downcast::<error::Error>()
.unwrap(),
- error::Error::Rc(ResponseCode::OUT_OF_KEYS)
+ error::Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
);
}
@@ -1023,7 +1023,7 @@
.unwrap_err()
.downcast::<error::Error>()
.unwrap(),
- error::Error::Rc(ResponseCode::OUT_OF_KEYS)
+ error::Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
);
}
diff --git a/keystore2/src/rkpd_client.rs b/keystore2/src/rkpd_client.rs
index b426440..0ea2d39 100644
--- a/keystore2/src/rkpd_client.rs
+++ b/keystore2/src/rkpd_client.rs
@@ -14,14 +14,14 @@
//! Helper wrapper around RKPD interface.
-use crate::error::{map_binder_status_code, Error, ErrorCode};
+use crate::error::{map_binder_status_code, Error, ResponseCode};
use crate::globals::get_remotely_provisioned_component_name;
use crate::ks_err;
use crate::utils::watchdog as wd;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
use android_security_rkp_aidl::aidl::android::security::rkp::{
- IGetKeyCallback::BnGetKeyCallback, IGetKeyCallback::IGetKeyCallback,
- IGetRegistrationCallback::BnGetRegistrationCallback,
+ IGetKeyCallback::BnGetKeyCallback, IGetKeyCallback::ErrorCode::ErrorCode as GetKeyErrorCode,
+ IGetKeyCallback::IGetKeyCallback, IGetRegistrationCallback::BnGetRegistrationCallback,
IGetRegistrationCallback::IGetRegistrationCallback, IRegistration::IRegistration,
IRemoteProvisioning::IRemoteProvisioning,
IStoreUpgradedKeyCallback::BnStoreUpgradedKeyCallback,
@@ -30,9 +30,19 @@
};
use android_security_rkp_aidl::binder::{BinderFeatures, Interface, Strong};
use anyhow::{Context, Result};
-use futures::channel::oneshot;
-use futures::executor::block_on;
use std::sync::Mutex;
+use std::time::Duration;
+use tokio::sync::oneshot;
+use tokio::time::timeout;
+
+// Normally, we block indefinitely when making calls outside of keystore and rely on watchdog to
+// report deadlocks. However, RKPD is mainline updatable. Also, calls to RKPD may wait on network
+// for certificates. So, we err on the side of caution and timeout instead.
+static RKPD_TIMEOUT: Duration = Duration::from_secs(10);
+
+fn tokio_rt() -> tokio::runtime::Runtime {
+ tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap()
+}
/// Thread-safe channel for sending a value once and only once. If a value has
/// already been send, subsequent calls to send will noop.
@@ -47,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");
+ }
}
}
}
@@ -79,17 +92,17 @@
let _wp = wd::watch_millis("IGetRegistrationCallback::onCancel", 500);
log::warn!("IGetRegistrationCallback cancelled");
self.registration_tx.send(
- Err(Error::Km(ErrorCode::OPERATION_CANCELLED))
+ Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
.context(ks_err!("GetRegistrationCallback cancelled.")),
);
Ok(())
}
- fn onError(&self, error: &str) -> binder::Result<()> {
+ fn onError(&self, description: &str) -> binder::Result<()> {
let _wp = wd::watch_millis("IGetRegistrationCallback::onError", 500);
- log::error!("IGetRegistrationCallback failed: '{error}'");
+ log::error!("IGetRegistrationCallback failed: '{description}'");
self.registration_tx.send(
- Err(Error::Km(ErrorCode::UNKNOWN_ERROR))
- .context(ks_err!("GetRegistrationCallback failed: {:?}", error)),
+ Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
+ .context(ks_err!("GetRegistrationCallback failed: {:?}", description)),
);
Ok(())
}
@@ -113,7 +126,12 @@
.getRegistration(&rpc_name, &cb)
.context(ks_err!("Trying to get registration."))?;
- rx.await.unwrap()
+ match timeout(RKPD_TIMEOUT, rx).await {
+ Err(e) => {
+ Err(Error::Rc(ResponseCode::SYSTEM_ERROR)).context(ks_err!("Waiting for RKPD: {:?}", e))
+ }
+ Ok(v) => v.unwrap(),
+ }
}
struct GetKeyCallback {
@@ -144,22 +162,61 @@
let _wp = wd::watch_millis("IGetKeyCallback::onCancel", 500);
log::warn!("IGetKeyCallback cancelled");
self.key_tx.send(
- Err(Error::Km(ErrorCode::OPERATION_CANCELLED))
+ Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
.context(ks_err!("GetKeyCallback cancelled.")),
);
Ok(())
}
- fn onError(&self, error: &str) -> binder::Result<()> {
+ fn onError(&self, error: GetKeyErrorCode, description: &str) -> binder::Result<()> {
let _wp = wd::watch_millis("IGetKeyCallback::onError", 500);
- log::error!("IGetKeyCallback failed: {error}");
- self.key_tx.send(
- Err(Error::Km(ErrorCode::UNKNOWN_ERROR))
- .context(ks_err!("GetKeyCallback failed: {:?}", error)),
- );
+ log::error!("IGetKeyCallback failed: {description}");
+ let rc = match error {
+ GetKeyErrorCode::ERROR_UNKNOWN => ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
+ GetKeyErrorCode::ERROR_PERMANENT => ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
+ GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY => {
+ ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY
+ }
+ GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH => {
+ ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE
+ }
+ _ => {
+ log::error!("Unexpected error from rkpd: {error:?}");
+ ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR
+ }
+ };
+ self.key_tx.send(Err(Error::Rc(rc)).context(ks_err!(
+ "GetKeyCallback failed: {:?} {:?}",
+ error,
+ description
+ )));
Ok(())
}
}
+async fn get_rkpd_attestation_key_from_registration_async(
+ registration: &Strong<dyn IRegistration>,
+ caller_uid: u32,
+) -> Result<RemotelyProvisionedKey> {
+ let (tx, rx) = oneshot::channel();
+ let cb = GetKeyCallback::new_native_binder(tx);
+
+ registration
+ .getKey(caller_uid.try_into().unwrap(), &cb)
+ .context(ks_err!("Trying to get key."))?;
+
+ match timeout(RKPD_TIMEOUT, rx).await {
+ 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(),
+ }
+}
+
async fn get_rkpd_attestation_key_async(
security_level: &SecurityLevel,
caller_uid: u32,
@@ -167,15 +224,7 @@
let registration = get_rkpd_registration(security_level)
.await
.context(ks_err!("Trying to get to IRegistration service."))?;
-
- let (tx, rx) = oneshot::channel();
- let cb = GetKeyCallback::new_native_binder(tx);
-
- registration
- .getKey(caller_uid.try_into().unwrap(), &cb)
- .context(ks_err!("Trying to get key."))?;
-
- rx.await.unwrap()
+ get_rkpd_attestation_key_from_registration_async(®istration, caller_uid).await
}
struct StoreUpgradedKeyCallback {
@@ -204,13 +253,32 @@
let _wp = wd::watch_millis("IGetRegistrationCallback::onError", 500);
log::error!("IGetRegistrationCallback failed: {error}");
self.completer.send(
- Err(Error::Km(ErrorCode::UNKNOWN_ERROR))
+ Err(Error::Rc(ResponseCode::SYSTEM_ERROR))
.context(ks_err!("Failed to store upgraded key: {:?}", error)),
);
Ok(())
}
}
+async fn store_rkpd_attestation_key_with_registration_async(
+ registration: &Strong<dyn IRegistration>,
+ key_blob: &[u8],
+ upgraded_blob: &[u8],
+) -> Result<()> {
+ let (tx, rx) = oneshot::channel();
+ let cb = StoreUpgradedKeyCallback::new_native_binder(tx);
+
+ registration
+ .storeUpgradedKeyAsync(key_blob, upgraded_blob, &cb)
+ .context(ks_err!("Failed to store upgraded blob with RKPD."))?;
+
+ match timeout(RKPD_TIMEOUT, rx).await {
+ Err(e) => Err(Error::Rc(ResponseCode::SYSTEM_ERROR))
+ .context(ks_err!("Waiting for RKPD to complete storing key: {:?}", e)),
+ Ok(v) => v.unwrap(),
+ }
+}
+
async fn store_rkpd_attestation_key_async(
security_level: &SecurityLevel,
key_blob: &[u8],
@@ -219,15 +287,7 @@
let registration = get_rkpd_registration(security_level)
.await
.context(ks_err!("Trying to get to IRegistration service."))?;
-
- let (tx, rx) = oneshot::channel();
- let cb = StoreUpgradedKeyCallback::new_native_binder(tx);
-
- registration
- .storeUpgradedKeyAsync(key_blob, upgraded_blob, &cb)
- .context(ks_err!("Failed to store upgraded blob with RKPD."))?;
-
- rx.await.unwrap()
+ store_rkpd_attestation_key_with_registration_async(®istration, key_blob, upgraded_blob).await
}
/// Get attestation key from RKPD.
@@ -236,7 +296,7 @@
caller_uid: u32,
) -> Result<RemotelyProvisionedKey> {
let _wp = wd::watch_millis("Calling get_rkpd_attestation_key()", 500);
- block_on(get_rkpd_attestation_key_async(security_level, caller_uid))
+ tokio_rt().block_on(get_rkpd_attestation_key_async(security_level, caller_uid))
}
/// Store attestation key in RKPD.
@@ -246,63 +306,132 @@
upgraded_blob: &[u8],
) -> Result<()> {
let _wp = wd::watch_millis("Calling store_rkpd_attestation_key()", 500);
- block_on(store_rkpd_attestation_key_async(security_level, key_blob, upgraded_blob))
+ tokio_rt().block_on(store_rkpd_attestation_key_async(security_level, key_blob, upgraded_blob))
}
#[cfg(test)]
mod tests {
use super::*;
+ use crate::error::map_km_error;
+ use crate::globals::get_keymint_device;
+ use crate::utils::upgrade_keyblob_if_required_with;
+ use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, AttestationKey::AttestationKey, KeyParameter::KeyParameter,
+ KeyParameterValue::KeyParameterValue, Tag::Tag,
+ };
use android_security_rkp_aidl::aidl::android::security::rkp::IRegistration::BnRegistration;
- use std::sync::Arc;
+ 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 MockRegistrationValues {
- _key: RemotelyProvisionedKey,
+ key: RemotelyProvisionedKey,
+ latency: Option<Duration>,
+ thread_join_handles: Vec<Option<std::thread::JoinHandle<()>>>,
}
- #[derive(Default)]
struct MockRegistration(Arc<Mutex<MockRegistrationValues>>);
impl MockRegistration {
- pub fn new_native_binder() -> Strong<dyn IRegistration> {
- let result: Self = Default::default();
+ pub fn new_native_binder(
+ key: &RemotelyProvisionedKey,
+ latency: Option<Duration>,
+ ) -> Strong<dyn IRegistration> {
+ 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, _: &Strong<dyn IGetKeyCallback>) -> binder::Result<()> {
- todo!()
+ fn getKey(&self, _: i32, cb: &Strong<dyn IGetKeyCallback>) -> binder::Result<()> {
+ let mut values = self.0.lock().unwrap();
+ let key = RemotelyProvisionedKey {
+ keyBlob: values.key.keyBlob.clone(),
+ encodedCertChain: values.key.encodedCertChain.clone(),
+ };
+ let latency = values.latency;
+ let get_key_cb = cb.clone();
+
+ // Need a separate thread to trigger timeout in the caller.
+ 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(
&self,
_: &[u8],
_: &[u8],
- _: &Strong<dyn IStoreUpgradedKeyCallback>,
+ cb: &Strong<dyn IStoreUpgradedKeyCallback>,
) -> binder::Result<()> {
- todo!()
+ // 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 = values.latency;
+
+ std::thread::spawn(move || {
+ if let Some(duration) = latency {
+ std::thread::sleep(duration);
+ }
+ store_cb.onSuccess().unwrap();
+ });
+ Ok(())
}
}
- fn get_mock_registration() -> Result<binder::Strong<dyn IRegistration>> {
+ fn get_mock_registration(
+ key: &RemotelyProvisionedKey,
+ latency: Option<Duration>,
+ ) -> Result<binder::Strong<dyn IRegistration>> {
let (tx, rx) = oneshot::channel();
let cb = GetRegistrationCallback::new_native_binder(tx);
- let mock_registration = MockRegistration::new_native_binder();
+ let mock_registration = MockRegistration::new_native_binder(key, latency);
assert!(cb.onSuccess(&mock_registration).is_ok());
- block_on(rx).unwrap()
+ tokio_rt().block_on(rx).unwrap()
+ }
+
+ // Using the same key ID makes test cases race with each other. So, we use separate key IDs for
+ // different test cases.
+ fn get_next_key_id() -> u32 {
+ static ID: AtomicU32 = AtomicU32::new(0);
+ ID.fetch_add(1, Ordering::Relaxed)
}
#[test]
fn test_get_registration_cb_success() {
- let registration = get_mock_registration();
+ let key: RemotelyProvisionedKey = Default::default();
+ let registration = get_mock_registration(&key, /*latency=*/ None);
assert!(registration.is_ok());
}
@@ -312,10 +441,10 @@
let cb = GetRegistrationCallback::new_native_binder(tx);
assert!(cb.onCancel().is_ok());
- let result = block_on(rx).unwrap();
+ let result = tokio_rt().block_on(rx).unwrap();
assert_eq!(
result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Km(ErrorCode::OPERATION_CANCELLED)
+ Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
);
}
@@ -325,10 +454,10 @@
let cb = GetRegistrationCallback::new_native_binder(tx);
assert!(cb.onError("error").is_ok());
- let result = block_on(rx).unwrap();
+ let result = tokio_rt().block_on(rx).unwrap();
assert_eq!(
result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Km(ErrorCode::UNKNOWN_ERROR)
+ Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
);
}
@@ -340,7 +469,7 @@
let cb = GetKeyCallback::new_native_binder(tx);
assert!(cb.onSuccess(&mock_key).is_ok());
- let key = block_on(rx).unwrap().unwrap();
+ let key = tokio_rt().block_on(rx).unwrap().unwrap();
assert_eq!(key, mock_key);
}
@@ -350,24 +479,41 @@
let cb = GetKeyCallback::new_native_binder(tx);
assert!(cb.onCancel().is_ok());
- let result = block_on(rx).unwrap();
+ let result = tokio_rt().block_on(rx).unwrap();
assert_eq!(
result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Km(ErrorCode::OPERATION_CANCELLED)
+ Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
);
}
#[test]
fn test_get_key_cb_error() {
- let (tx, rx) = oneshot::channel();
- let cb = GetKeyCallback::new_native_binder(tx);
- assert!(cb.onError("error").is_ok());
+ let error_mapping = HashMap::from([
+ (GetKeyErrorCode::ERROR_UNKNOWN, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+ (GetKeyErrorCode::ERROR_PERMANENT, ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR),
+ (
+ GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY,
+ ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
+ ),
+ (
+ GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH,
+ ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
+ ),
+ ]);
- let result = block_on(rx).unwrap();
- assert_eq!(
- result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Km(ErrorCode::UNKNOWN_ERROR)
- );
+ // Loop over the generated list of enum values to better ensure this test stays in
+ // sync with the AIDL.
+ for get_key_error in GetKeyErrorCode::enum_values() {
+ let (tx, rx) = oneshot::channel();
+ let cb = GetKeyCallback::new_native_binder(tx);
+ assert!(cb.onError(get_key_error, "error").is_ok());
+
+ let result = tokio_rt().block_on(rx).unwrap();
+ assert_eq!(
+ result.unwrap_err().downcast::<Error>().unwrap(),
+ Error::Rc(error_mapping[&get_key_error]),
+ );
+ }
}
#[test]
@@ -376,7 +522,7 @@
let cb = StoreUpgradedKeyCallback::new_native_binder(tx);
assert!(cb.onSuccess().is_ok());
- block_on(rx).unwrap().unwrap();
+ tokio_rt().block_on(rx).unwrap().unwrap();
}
#[test]
@@ -385,17 +531,73 @@
let cb = StoreUpgradedKeyCallback::new_native_binder(tx);
assert!(cb.onError("oh no! it failed").is_ok());
- let result = block_on(rx).unwrap();
+ let result = tokio_rt().block_on(rx).unwrap();
assert_eq!(
result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Km(ErrorCode::UNKNOWN_ERROR)
+ Error::Rc(ResponseCode::SYSTEM_ERROR)
+ );
+ }
+
+ #[test]
+ fn test_get_mock_key_success() {
+ let mock_key =
+ RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
+ let registration = get_mock_registration(&mock_key, /*latency=*/ None).unwrap();
+
+ let key = tokio_rt()
+ .block_on(get_rkpd_attestation_key_from_registration_async(®istration, 0))
+ .unwrap();
+ assert_eq!(key, mock_key);
+ }
+
+ #[test]
+ 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(1);
+ let registration = get_mock_registration(&mock_key, Some(latency)).unwrap();
+
+ let result =
+ tokio_rt().block_on(get_rkpd_attestation_key_from_registration_async(®istration, 0));
+ assert_eq!(
+ result.unwrap_err().downcast::<Error>().unwrap(),
+ Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
+ );
+ }
+
+ #[test]
+ fn test_store_mock_key_success() {
+ let mock_key =
+ RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
+ let registration = get_mock_registration(&mock_key, /*latency=*/ None).unwrap();
+ tokio_rt()
+ .block_on(store_rkpd_attestation_key_with_registration_async(®istration, &[], &[]))
+ .unwrap();
+ }
+
+ #[test]
+ 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(1);
+ let registration = get_mock_registration(&mock_key, Some(latency)).unwrap();
+
+ let result = tokio_rt().block_on(store_rkpd_attestation_key_with_registration_async(
+ ®istration,
+ &[],
+ &[],
+ ));
+ assert_eq!(
+ result.unwrap_err().downcast::<Error>().unwrap(),
+ Error::Rc(ResponseCode::SYSTEM_ERROR)
);
}
#[test]
fn test_get_rkpd_attestation_key() {
binder::ProcessState::start_thread_pool();
- let key = get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, 0).unwrap();
+ let key_id = get_next_key_id();
+ let key = get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id).unwrap();
assert!(!key.keyBlob.is_empty());
assert!(!key.encodedCertChain.is_empty());
}
@@ -404,11 +606,11 @@
fn test_get_rkpd_attestation_key_same_caller() {
binder::ProcessState::start_thread_pool();
let sec_level = SecurityLevel::TRUSTED_ENVIRONMENT;
- let caller_uid = 0;
+ let key_id = get_next_key_id();
// Multiple calls should return the same key.
- let first_key = get_rkpd_attestation_key(&sec_level, caller_uid).unwrap();
- let second_key = get_rkpd_attestation_key(&sec_level, caller_uid).unwrap();
+ let first_key = get_rkpd_attestation_key(&sec_level, key_id).unwrap();
+ let second_key = get_rkpd_attestation_key(&sec_level, key_id).unwrap();
assert_eq!(first_key.keyBlob, second_key.keyBlob);
assert_eq!(first_key.encodedCertChain, second_key.encodedCertChain);
@@ -418,21 +620,120 @@
fn test_get_rkpd_attestation_key_different_caller() {
binder::ProcessState::start_thread_pool();
let sec_level = SecurityLevel::TRUSTED_ENVIRONMENT;
+ let first_key_id = get_next_key_id();
+ let second_key_id = get_next_key_id();
// Different callers should be getting different keys.
- let first_key = get_rkpd_attestation_key(&sec_level, 1).unwrap();
- let second_key = get_rkpd_attestation_key(&sec_level, 2).unwrap();
+ let first_key = get_rkpd_attestation_key(&sec_level, first_key_id).unwrap();
+ let second_key = get_rkpd_attestation_key(&sec_level, second_key_id).unwrap();
assert_ne!(first_key.keyBlob, second_key.keyBlob);
assert_ne!(first_key.encodedCertChain, second_key.encodedCertChain);
}
#[test]
+ // Couple of things to note:
+ // 1. This test must never run with UID of keystore. Otherwise, it can mess up keys stored by
+ // keystore.
+ // 2. Storing and reading the stored key is prone to race condition. So, we only do this in one
+ // test case.
fn test_store_rkpd_attestation_key() {
binder::ProcessState::start_thread_pool();
let sec_level = SecurityLevel::TRUSTED_ENVIRONMENT;
- let key = get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, 0).unwrap();
+ let key_id = get_next_key_id();
+ let key = get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id).unwrap();
+ let new_blob: [u8; 8] = rand::random();
- assert!(store_rkpd_attestation_key(&sec_level, &key.keyBlob, &key.keyBlob).is_ok());
+ assert!(store_rkpd_attestation_key(&sec_level, &key.keyBlob, &new_blob).is_ok());
+
+ let new_key =
+ get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id).unwrap();
+
+ // Restore original key so that we don't leave RKPD with invalid blobs.
+ assert!(store_rkpd_attestation_key(&sec_level, &new_blob, &key.keyBlob).is_ok());
+ assert_eq!(new_key.keyBlob, new_blob);
+ }
+
+ #[test]
+ // This is a helper for a manual test. We want to check that after a system upgrade RKPD
+ // attestation keys can also be upgraded and stored again with RKPD. The steps are:
+ // 1. Run this test and check in stdout that no key upgrade happened.
+ // 2. Perform a system upgrade.
+ // 3. Run this test and check in stdout that key upgrade did happen.
+ //
+ // Note that this test must be run with that same UID every time. Running as root, i.e. UID 0,
+ // should do the trick. Also, use "--nocapture" flag to get stdout.
+ fn test_rkpd_attestation_key_upgrade() {
+ binder::ProcessState::start_thread_pool();
+ let security_level = SecurityLevel::TRUSTED_ENVIRONMENT;
+ let (keymint, _, _) = get_keymint_device(&security_level).unwrap();
+ let key_id = get_next_key_id();
+ let mut key_upgraded = false;
+
+ let key = get_rkpd_attestation_key(&security_level, key_id).unwrap();
+ assert!(!key.keyBlob.is_empty());
+ assert!(!key.encodedCertChain.is_empty());
+
+ upgrade_keyblob_if_required_with(
+ &*keymint,
+ &key.keyBlob,
+ /*upgrade_params=*/ &[],
+ /*km_op=*/
+ |blob| {
+ let params = vec![
+ KeyParameter {
+ tag: Tag::ALGORITHM,
+ value: KeyParameterValue::Algorithm(Algorithm::AES),
+ },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(128) },
+ ];
+ let attestation_key = AttestationKey {
+ keyBlob: blob.to_vec(),
+ attestKeyParams: vec![],
+ issuerSubjectName: parse_subject_from_certificate(&key.encodedCertChain)
+ .unwrap(),
+ };
+
+ map_km_error(keymint.generateKey(¶ms, Some(&attestation_key)))
+ },
+ /*new_blob_handler=*/
+ |new_blob| {
+ // This handler is only executed if a key upgrade was performed.
+ key_upgraded = true;
+ store_rkpd_attestation_key(&security_level, &key.keyBlob, new_blob).unwrap();
+ Ok(())
+ },
+ )
+ .unwrap();
+
+ if key_upgraded {
+ println!("RKPD key was upgraded and stored with RKPD.");
+ } else {
+ println!("RKPD key was NOT upgraded.");
+ }
+ }
+
+ #[test]
+ fn test_stress_get_rkpd_attestation_key() {
+ binder::ProcessState::start_thread_pool();
+ let key_id = get_next_key_id();
+ let mut threads = vec![];
+ const NTHREADS: u32 = 10;
+ const NCALLS: u32 = 1000;
+
+ for _ in 0..NTHREADS {
+ threads.push(std::thread::spawn(move || {
+ for _ in 0..NCALLS {
+ let key = get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id)
+ .unwrap();
+ assert!(!key.keyBlob.is_empty());
+ assert!(!key.encodedCertChain.is_empty());
+ }
+ }));
+ }
+
+ for t in threads {
+ assert!(t.join().is_ok());
+ }
}
}
diff --git a/keystore2/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/src/utils.rs b/keystore2/src/utils.rs
index 75d98e2..7bc548e 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -209,6 +209,7 @@
parameters.into_iter().map(|p| p.into_authorization()).collect()
}
+#[allow(clippy::unnecessary_cast)]
/// This returns the current time (in milliseconds) as an instance of a monotonic clock,
/// by invoking the system call since Rust does not support getting monotonic time instance
/// as an integer.
@@ -276,7 +277,40 @@
);
result.sort_unstable();
result.dedup();
- Ok(result)
+
+ let mut items_to_return = 0;
+ let mut returned_bytes: usize = 0;
+ const RESPONSE_SIZE_LIMIT: usize = 358400;
+ // Estimate the transaction size to avoid returning more items than what
+ // could fit in a binder transaction.
+ for kd in result.iter() {
+ // 4 bytes for the Domain enum
+ // 8 bytes for the Namespace long.
+ returned_bytes += 4 + 8;
+ // Size of the alias string. Includes 4 bytes for length encoding.
+ if let Some(alias) = &kd.alias {
+ returned_bytes += 4 + alias.len();
+ }
+ // Size of the blob. Includes 4 bytes for length encoding.
+ if let Some(blob) = &kd.blob {
+ returned_bytes += 4 + blob.len();
+ }
+ // The binder transaction size limit is 1M. Empirical measurements show
+ // that the binder overhead is 60% (to be confirmed). So break after
+ // 350KB and return a partial list.
+ if returned_bytes > RESPONSE_SIZE_LIMIT {
+ log::warn!(
+ "Key descriptors list ({} items) may exceed binder \
+ size, returning {} items est {} bytes.",
+ result.len(),
+ items_to_return,
+ returned_bytes
+ );
+ break;
+ }
+ items_to_return += 1;
+ }
+ Ok(result[..items_to_return].to_vec())
}
/// This module provides helpers for simplified use of the watchdog module.
diff --git a/keystore2/src/watchdog.rs b/keystore2/src/watchdog.rs
index a26b632..01043c5 100644
--- a/keystore2/src/watchdog.rs
+++ b/keystore2/src/watchdog.rs
@@ -141,7 +141,7 @@
},
);
// Put the groups back into a vector.
- let mut groups: Vec<Vec<(&Index, &Record)>> = groups.into_iter().map(|(_, v)| v).collect();
+ let mut groups: Vec<Vec<(&Index, &Record)>> = groups.into_values().collect();
// Sort the groups by start time of the most recent (.last()) of each group.
// It is panic safe to use unwrap() here because we never add empty vectors to
// the map.
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/ffi_test_utils.cpp b/keystore2/tests/ffi_test_utils.cpp
index 45ce02c..de20d83 100644
--- a/keystore2/tests/ffi_test_utils.cpp
+++ b/keystore2/tests/ffi_test_utils.cpp
@@ -237,7 +237,7 @@
}
// Perform ASN.1 DER encoding of KeyDescription.
- size_t asn1_data_len = i2d_TEST_KEY_DESCRIPTION(key_description.get(), nullptr);
+ int asn1_data_len = i2d_TEST_KEY_DESCRIPTION(key_description.get(), nullptr);
if (asn1_data_len < 0) {
cxx_result.error = keymaster::TranslateLastOpenSslError();
return cxx_result;
@@ -341,7 +341,7 @@
}
// ASN.1 DER-encoding of secure key wrapper.
- size_t asn1_data_len = i2d_TEST_SECURE_KEY_WRAPPER(sec_key_wrapper.get(), nullptr);
+ int asn1_data_len = i2d_TEST_SECURE_KEY_WRAPPER(sec_key_wrapper.get(), nullptr);
if (asn1_data_len < 0) {
cxx_result.error = keymaster::TranslateLastOpenSslError();
return cxx_result;
diff --git a/keystore2/tests/keystore2_client_attest_key_tests.rs b/keystore2/tests/keystore2_client_attest_key_tests.rs
index 2d44753..4febd9b 100644
--- a/keystore2/tests/keystore2_client_attest_key_tests.rs
+++ b/keystore2/tests/keystore2_client_attest_key_tests.rs
@@ -149,9 +149,7 @@
let mut cert_chain: Vec<u8> = Vec::new();
cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
cert_chain.extend(attestation_key_metadata.certificateChain.as_ref().unwrap());
- // The server seems to be issuing test certs with invalid subject names.
- // Re-enable when b/263254184 is fixed
- // validate_certchain(&cert_chain).expect("Error while validating cert chain.");
+ validate_certchain(&cert_chain).expect("Error while validating cert chain.");
// Create EC key and use attestation key to sign it.
let ec_key_alias = format!("ks_ec_attested_test_key_{}", getuid());
@@ -168,9 +166,7 @@
cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
cert_chain.extend(attestation_key_metadata.certificateChain.as_ref().unwrap());
- // The server seems to be issuing test certs with invalid subject names.
- // Re-enable when b/263254184 is fixed
- // validate_certchain(&cert_chain).expect("Error while validating cert chain.");
+ validate_certchain(&cert_chain).expect("Error while validating cert chain.");
}
}
diff --git a/keystore2/tests/keystore2_client_delete_key_tests.rs b/keystore2/tests/keystore2_client_delete_key_tests.rs
new file mode 100644
index 0000000..2a06edb
--- /dev/null
+++ b/keystore2/tests/keystore2_client_delete_key_tests.rs
@@ -0,0 +1,150 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use nix::unistd::getuid;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ ErrorCode::ErrorCode, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error};
+
+/// Generate a key and delete it using keystore2 service `deleteKey` API. Test should successfully
+/// delete the generated key.
+#[test]
+fn keystore2_delete_key_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "delete_key_success_key";
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ None,
+ )
+ .unwrap();
+
+ keystore2.deleteKey(&key_metadata.key).expect("Failed to delete a key.");
+
+ // Check wehther deleted key is removed from keystore.
+ let result = key_generations::map_ks_error(keystore2.getKeyEntry(&key_metadata.key));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Try to delete non-existing key with domain other than BLOB using keystore2 service `deleteKey`
+/// API. Test should fail with an error code `KEY_NOT_FOUND`.
+#[test]
+fn keystore2_delete_key_fail() {
+ let test_alias = "delete_key_failure_key";
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(keystore2.deleteKey(&KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+ alias: Some(test_alias.to_string()),
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Generate a key with `Domain::BLOB`. Try to delete a key with `Domain::BLOB` using keystore2
+/// service `deleteKey` API. Test should fail to delete a key with domain BLOB with an error code
+/// `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_delete_key_with_blob_domain_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "delete_key_blob_fail_key";
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::BLOB,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias.to_string()),
+ None,
+ )
+ .unwrap();
+
+ let result = key_generations::map_ks_error(keystore2.deleteKey(&key_metadata.key));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
+}
+
+/// Generate a key with `Domain::BLOB`. Delete generated key with `Domain::BLOB` using underlying
+/// security level `deleteKey` API. Test should delete the key successfully.
+#[test]
+fn keystore2_delete_key_blob_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "delete_key_blob_success_key";
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::BLOB,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias.to_string()),
+ None,
+ )
+ .unwrap();
+
+ let result = sec_level.deleteKey(&key_metadata.key);
+ assert!(result.is_ok());
+}
+
+/// Try to delete a key with `Domain::BLOB` without providing key-blob. Test should fail to delete a
+/// key with error code `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_delete_key_fails_with_missing_key_blob() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let result = key_generations::map_ks_error(sec_level.deleteKey(&KeyDescriptor {
+ domain: Domain::BLOB,
+ nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+ alias: None,
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INVALID_ARGUMENT), result.unwrap_err());
+}
+
+/// Try to delete a key with domain other than `Domain::BLOB` using underlying security-level
+/// `deleteKey` API. Test should fail to delete a key-blob from underlying security-level backend
+/// with error code `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_delete_key_blob_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_delete_keyblob_test_key_{}", getuid());
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias),
+ None,
+ )
+ .unwrap();
+
+ let result = key_generations::map_ks_error(sec_level.deleteKey(&key_metadata.key));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INVALID_ARGUMENT), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_grant_key_tests.rs b/keystore2/tests/keystore2_client_grant_key_tests.rs
index 7c75734..bde872d 100644
--- a/keystore2/tests/keystore2_client_grant_key_tests.rs
+++ b/keystore2/tests/keystore2_client_grant_key_tests.rs
@@ -19,7 +19,8 @@
Digest::Digest, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
};
use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+ Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
+ IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
ResponseCode::ResponseCode,
};
@@ -27,7 +28,9 @@
authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
};
-use crate::keystore2_client_test_utils::perform_sample_sign_operation;
+use crate::keystore2_client_test_utils::{
+ generate_ec_key_and_grant_to_users, perform_sample_sign_operation,
+};
/// Generate an EC signing key and grant it to the user with given access vector.
fn generate_ec_key_and_grant_to_user(
@@ -50,6 +53,36 @@
keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
}
+fn load_grant_key_and_perform_sign_operation(
+ keystore2: &binder::Strong<dyn IKeystoreService>,
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ grant_key_nspace: i64,
+) -> Result<(), binder::Status> {
+ let key_entry_response = keystore2.getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ })?;
+
+ // Perform sample crypto operation using granted key.
+ let op_response = sec_level.createOperation(
+ &key_entry_response.metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+ false,
+ )?;
+
+ assert!(op_response.iOperation.is_some());
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(perform_sample_sign_operation(
+ &op_response.iOperation.unwrap()
+ ))
+ );
+
+ Ok(())
+}
+
/// Try to grant a key with permission that does not map to any of the `KeyPermission` values.
/// An error is expected with values that does not map to set of permissions listed in
/// `KeyPermission`.
@@ -203,3 +236,520 @@
)
};
}
+
+/// Grant a key to the user with DELETE access. In grantee context load the key and delete it.
+/// Verify that grantee should succeed in deleting the granted key and in grantor context test
+/// should fail to find the key with error response `KEY_NOT_FOUND`.
+#[test]
+fn keystore2_grant_delete_key_success() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+ static ALIAS: &str = "ks_grant_key_delete_success";
+
+ // Generate a key and grant it to a user with DELETE permission.
+ let grant_key_nspace = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let access_vector = KeyPermission::DELETE.0;
+ let mut grant_keys = generate_ec_key_and_grant_to_users(
+ &keystore2,
+ &sec_level,
+ Some(ALIAS.to_string()),
+ vec![GRANTEE_UID.try_into().unwrap()],
+ access_vector,
+ )
+ .unwrap();
+
+ grant_keys.remove(0)
+ })
+ };
+
+ // Grantee context, delete the key.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+ keystore2
+ .deleteKey(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ })
+ .unwrap();
+ },
+ )
+ };
+
+ // Verify whether key got deleted in grantor's context.
+ unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), move || {
+ let keystore2_inst = get_keystore_service();
+ let result =
+ key_generations::map_ks_error(keystore2_inst.getKeyEntry(&KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS.to_string()),
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+ })
+ };
+}
+
+/// Grant a key to the user. In grantee context load the granted key and try to grant it to second
+/// user. Test should fail with a response code `PERMISSION_DENIED` to grant a key to second user
+/// from grantee context. Test should make sure second grantee should not have a access to granted
+/// key.
+#[test]
+fn keystore2_grant_key_fails_with_permission_denied() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+
+ const SEC_USER_ID: u32 = 98;
+ const SEC_APPLICATION_ID: u32 = 10001;
+ static SEC_GRANTEE_UID: u32 = SEC_USER_ID * AID_USER_OFFSET + SEC_APPLICATION_ID;
+ static SEC_GRANTEE_GID: u32 = SEC_GRANTEE_UID;
+
+ // Generate a key and grant it to a user with GET_INFO permission.
+ let grant_key_nspace = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let access_vector = KeyPermission::GET_INFO.0;
+ let alias = format!("ks_grant_perm_denied_key_{}", getuid());
+ let mut grant_keys = generate_ec_key_and_grant_to_users(
+ &keystore2,
+ &sec_level,
+ Some(alias),
+ vec![GRANTEE_UID.try_into().unwrap()],
+ access_vector,
+ )
+ .unwrap();
+
+ grant_keys.remove(0)
+ })
+ };
+
+ // Grantee context, load the granted key and try to grant it to `SEC_GRANTEE_UID` grantee.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+ let access_vector = KeyPermission::GET_INFO.0;
+
+ let key_entry_response = keystore2
+ .getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ })
+ .unwrap();
+
+ let result = key_generations::map_ks_error(keystore2.grant(
+ &key_entry_response.metadata.key,
+ SEC_GRANTEE_UID.try_into().unwrap(),
+ access_vector,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+ },
+ )
+ };
+
+ // Make sure second grantee shouldn't have access to the above granted key.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(SEC_GRANTEE_UID),
+ Gid::from_raw(SEC_GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ }));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+ },
+ )
+ };
+}
+
+/// Try to grant a key with `GRANT` access. Keystore2 system shouldn't allow to grant a key with
+/// `GRANT` access. Test should fail to grant a key with `PERMISSION_DENIED` error response code.
+#[test]
+fn keystore2_grant_key_fails_with_grant_perm_expect_perm_denied() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let access_vector = KeyPermission::GRANT.0;
+ let alias = format!("ks_grant_access_vec_key_{}", getuid());
+ let user_id = 98;
+ let application_id = 10001;
+ let grantee_uid = user_id * AID_USER_OFFSET + application_id;
+
+ let result = key_generations::map_ks_error(generate_ec_key_and_grant_to_users(
+ &keystore2,
+ &sec_level,
+ Some(alias),
+ vec![grantee_uid.try_into().unwrap()],
+ access_vector,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+}
+
+/// Try to grant a non-existing key to the user. Test should fail with `KEY_NOT_FOUND` error
+/// response.
+#[test]
+fn keystore2_grant_fails_with_non_existing_key_expect_key_not_found_err() {
+ let keystore2 = get_keystore_service();
+ let alias = format!("ks_grant_test_non_existing_key_5_{}", getuid());
+ let user_id = 98;
+ let application_id = 10001;
+ let grantee_uid = user_id * AID_USER_OFFSET + application_id;
+ let access_vector = KeyPermission::GET_INFO.0;
+
+ let result = key_generations::map_ks_error(keystore2.grant(
+ &KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+ alias: Some(alias),
+ blob: None,
+ },
+ grantee_uid.try_into().unwrap(),
+ access_vector,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Grant a key to the user and immediately ungrant the granted key. In grantee context try to load
+/// the key. Grantee should fail to load the ungranted key with `KEY_NOT_FOUND` error response.
+#[test]
+fn keystore2_ungrant_key_success() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+
+ // Generate a key and grant it to a user with GET_INFO permission.
+ let grant_key_nspace = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_ungrant_test_key_1{}", getuid());
+ let access_vector = KeyPermission::GET_INFO.0;
+ let mut grant_keys = generate_ec_key_and_grant_to_users(
+ &keystore2,
+ &sec_level,
+ Some(alias.to_string()),
+ vec![GRANTEE_UID.try_into().unwrap()],
+ access_vector,
+ )
+ .unwrap();
+
+ let grant_key_nspace = grant_keys.remove(0);
+
+ //Ungrant above granted key.
+ keystore2
+ .ungrant(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias),
+ blob: None,
+ },
+ GRANTEE_UID.try_into().unwrap(),
+ )
+ .unwrap();
+
+ grant_key_nspace
+ })
+ };
+
+ // Grantee context, try to load the ungranted key.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+ let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+ },
+ )
+ };
+}
+
+/// Generate a key, grant it to the user and then delete the granted key. Try to ungrant
+/// a deleted key. Test should fail to ungrant a non-existing key with `KEY_NOT_FOUND` error
+/// response. Generate a new key with the same alias and try to access the previously granted
+/// key in grantee context. Test should fail to load the granted key in grantee context as the
+/// associated key is deleted from grantor context.
+#[test]
+fn keystore2_ungrant_fails_with_non_existing_key_expect_key_not_found_error() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const APPLICATION_ID: u32 = 10001;
+ const USER_ID: u32 = 99;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+
+ let grant_key_nspace = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("{}{}", "ks_grant_delete_ungrant_test_key_1", getuid());
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias.to_string()),
+ None,
+ )
+ .unwrap();
+
+ let access_vector = KeyPermission::GET_INFO.0;
+ let grant_key = keystore2
+ .grant(&key_metadata.key, GRANTEE_UID.try_into().unwrap(), access_vector)
+ .unwrap();
+ assert_eq!(grant_key.domain, Domain::GRANT);
+
+ // Delete above granted key.
+ keystore2.deleteKey(&key_metadata.key).unwrap();
+
+ // Try to ungrant above granted key.
+ let result = key_generations::map_ks_error(
+ keystore2.ungrant(&key_metadata.key, GRANTEE_UID.try_into().unwrap()),
+ );
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+
+ // Generate a new key with the same alias and try to access the earlier granted key
+ // in grantee context.
+ let result = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias),
+ None,
+ );
+ assert!(result.is_ok());
+
+ grant_key.nspace
+ })
+ };
+
+ // Make sure grant did not persist, try to access the earlier granted key in grantee context.
+ // Grantee context should fail to load the granted key as its associated key is deleted in
+ // grantor context.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+ },
+ )
+ };
+}
+
+/// Grant a key to multiple users. Verify that all grantees should succeed in loading the key and
+/// use it for performing an operation successfully.
+#[test]
+fn keystore2_grant_key_to_multi_users_success() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const APPLICATION_ID: u32 = 10001;
+ const USER_ID_1: u32 = 99;
+ static GRANTEE_1_UID: u32 = USER_ID_1 * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_1_GID: u32 = GRANTEE_1_UID;
+
+ const USER_ID_2: u32 = 98;
+ static GRANTEE_2_UID: u32 = USER_ID_2 * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_2_GID: u32 = GRANTEE_2_UID;
+
+ // Generate a key and grant it to multiple users with GET_INFO|USE permissions.
+ let mut grant_keys = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_grant_test_key_2{}", getuid());
+ let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::USE.0;
+
+ generate_ec_key_and_grant_to_users(
+ &keystore2,
+ &sec_level,
+ Some(alias),
+ vec![GRANTEE_1_UID.try_into().unwrap(), GRANTEE_2_UID.try_into().unwrap()],
+ access_vector,
+ )
+ .unwrap()
+ })
+ };
+
+ for (grantee_uid, grantee_gid) in
+ &[(GRANTEE_1_UID, GRANTEE_1_GID), (GRANTEE_2_UID, GRANTEE_2_GID)]
+ {
+ let grant_key_nspace = grant_keys.remove(0);
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(*grantee_uid),
+ Gid::from_raw(*grantee_gid),
+ move || {
+ let keystore2 = get_keystore_service();
+ let sec_level =
+ keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(load_grant_key_and_perform_sign_operation(
+ &keystore2,
+ &sec_level,
+ grant_key_nspace
+ ))
+ );
+ },
+ )
+ };
+ }
+}
+
+/// Grant a key to multiple users with GET_INFO|DELETE permissions. In one of the grantee context
+/// use the key and delete it. Try to load the granted key in another grantee context. Test should
+/// fail to load the granted key with `KEY_NOT_FOUND` error response.
+#[test]
+fn keystore2_grant_key_to_multi_users_delete_fails_with_key_not_found_error() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const USER_ID_1: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_1_UID: u32 = USER_ID_1 * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_1_GID: u32 = GRANTEE_1_UID;
+
+ const USER_ID_2: u32 = 98;
+ static GRANTEE_2_UID: u32 = USER_ID_2 * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_2_GID: u32 = GRANTEE_2_UID;
+
+ // Generate a key and grant it to multiple users with GET_INFO permission.
+ let mut grant_keys = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_grant_test_key_2{}", getuid());
+ let access_vector =
+ KeyPermission::GET_INFO.0 | KeyPermission::USE.0 | KeyPermission::DELETE.0;
+
+ generate_ec_key_and_grant_to_users(
+ &keystore2,
+ &sec_level,
+ Some(alias),
+ vec![GRANTEE_1_UID.try_into().unwrap(), GRANTEE_2_UID.try_into().unwrap()],
+ access_vector,
+ )
+ .unwrap()
+ })
+ };
+
+ // Grantee #1 context
+ let grant_key1_nspace = grant_keys.remove(0);
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_1_UID),
+ Gid::from_raw(GRANTEE_1_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+ let sec_level =
+ keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(load_grant_key_and_perform_sign_operation(
+ &keystore2,
+ &sec_level,
+ grant_key1_nspace
+ ))
+ );
+
+ // Delete the granted key.
+ keystore2
+ .deleteKey(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key1_nspace,
+ alias: None,
+ blob: None,
+ })
+ .unwrap();
+ },
+ )
+ };
+
+ // Grantee #2 context
+ let grant_key2_nspace = grant_keys.remove(0);
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_2_UID),
+ Gid::from_raw(GRANTEE_2_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key2_nspace,
+ alias: None,
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+ },
+ )
+ };
+}
diff --git a/keystore2/tests/keystore2_client_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_list_entries_tests.rs b/keystore2/tests/keystore2_client_list_entries_tests.rs
index 62e3dd0..3b656c3 100644
--- a/keystore2/tests/keystore2_client_list_entries_tests.rs
+++ b/keystore2/tests/keystore2_client_list_entries_tests.rs
@@ -14,6 +14,8 @@
use nix::unistd::{getuid, Gid, Uid};
use rustutils::users::AID_USER_OFFSET;
+use std::collections::HashSet;
+use std::fmt::Write;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
use android_system_keystore2::aidl::android::system::keystore2::{
@@ -21,6 +23,7 @@
KeyPermission::KeyPermission, ResponseCode::ResponseCode,
};
+use crate::keystore2_client_test_utils::delete_app_key;
use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error, run_as};
/// Try to find a key with given key parameters using `listEntries` API.
@@ -183,3 +186,68 @@
assert!(result.is_err());
assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
}
+
+/// Import large number of Keystore entries with long aliases and try to list aliases
+/// of all the entries in the keystore.
+#[test]
+fn keystore2_list_entries_with_long_aliases_success() {
+ static CLIENT_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const USER_ID: u32 = 92;
+ const APPLICATION_ID: u32 = 10002;
+ static CLIENT_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static CLIENT_GID: u32 = CLIENT_UID;
+
+ unsafe {
+ run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ // Make sure there are no keystore entries exist before adding new entries.
+ let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
+ if !key_descriptors.is_empty() {
+ key_descriptors.into_iter().map(|key| key.alias.unwrap()).for_each(|alias| {
+ delete_app_key(&keystore2, &alias).unwrap();
+ });
+ }
+
+ let mut imported_key_aliases = HashSet::new();
+
+ // Import 100 keys with aliases of length 6000.
+ for count in 1..101 {
+ let mut alias = String::new();
+ write!(alias, "{}_{}", "X".repeat(6000), count).unwrap();
+ imported_key_aliases.insert(alias.clone());
+
+ let result =
+ key_generations::import_aes_key(&sec_level, Domain::APP, -1, Some(alias));
+ assert!(result.is_ok());
+ }
+
+ // b/222287335 Limiting Keystore `listEntries` API to return subset of the Keystore
+ // entries to avoid running out of binder buffer space.
+ // To verify that all the imported key aliases are present in Keystore,
+ // - get the list of entries from Keystore
+ // - check whether the retrieved key entries list is a subset of imported key aliases
+ // - delete this subset of keystore entries from Keystore as well as from imported
+ // list of key aliases
+ // - continue above steps till it cleanup all the imported keystore entries.
+ while !imported_key_aliases.is_empty() {
+ let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
+
+ // Check retrieved key entries list is a subset of imported keys list.
+ assert!(key_descriptors
+ .iter()
+ .all(|key| imported_key_aliases.contains(key.alias.as_ref().unwrap())));
+
+ // Delete the listed key entries from Keystore as well as from imported keys list.
+ key_descriptors.into_iter().map(|key| key.alias.unwrap()).for_each(|alias| {
+ delete_app_key(&keystore2, &alias).unwrap();
+ assert!(imported_key_aliases.remove(&alias));
+ });
+ }
+
+ assert!(imported_key_aliases.is_empty());
+ })
+ };
+}
diff --git a/keystore2/tests/keystore2_client_test_utils.rs b/keystore2/tests/keystore2_client_test_utils.rs
index 56995e4..58e6b7d 100644
--- a/keystore2/tests/keystore2_client_test_utils.rs
+++ b/keystore2/tests/keystore2_client_test_utils.rs
@@ -90,6 +90,29 @@
.expect("Could not check for declared keymint interface")
}
+/// Generate EC key and grant it to the list of users with given access vector.
+/// Returns the list of granted keys `nspace` values in the order of given grantee uids.
+pub fn generate_ec_key_and_grant_to_users(
+ keystore2: &binder::Strong<dyn IKeystoreService>,
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ alias: Option<String>,
+ grantee_uids: Vec<i32>,
+ access_vector: i32,
+) -> Result<Vec<i64>, binder::Status> {
+ let key_metadata =
+ key_generations::generate_ec_p256_signing_key(sec_level, Domain::APP, -1, alias, None)?;
+
+ let mut granted_keys = Vec::new();
+
+ for uid in grantee_uids {
+ let granted_key = keystore2.grant(&key_metadata.key, uid, access_vector)?;
+ assert_eq!(granted_key.domain, Domain::GRANT);
+ granted_keys.push(granted_key.nspace);
+ }
+
+ Ok(granted_keys)
+}
+
/// Generate a EC_P256 key using given domain, namespace and alias.
/// Create an operation using the generated key and perform sample signing operation.
pub fn create_signing_operation(
diff --git a/keystore2/tests/keystore2_client_tests.rs b/keystore2/tests/keystore2_client_tests.rs
index d705aa4..07a298a 100644
--- a/keystore2/tests/keystore2_client_tests.rs
+++ b/keystore2/tests/keystore2_client_tests.rs
@@ -16,12 +16,15 @@
pub mod keystore2_client_3des_key_tests;
pub mod keystore2_client_aes_key_tests;
pub mod keystore2_client_attest_key_tests;
+pub mod keystore2_client_delete_key_tests;
pub mod keystore2_client_ec_key_tests;
pub mod keystore2_client_grant_key_tests;
pub mod keystore2_client_hmac_key_tests;
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;
pub mod keystore2_client_rsa_key_tests;
pub mod keystore2_client_test_utils;
+pub mod keystore2_client_update_subcomponent_tests;
diff --git a/keystore2/tests/keystore2_client_update_subcomponent_tests.rs b/keystore2/tests/keystore2_client_update_subcomponent_tests.rs
new file mode 100644
index 0000000..c987f22
--- /dev/null
+++ b/keystore2/tests/keystore2_client_update_subcomponent_tests.rs
@@ -0,0 +1,230 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ ErrorCode::ErrorCode, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+ ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error, run_as};
+
+/// Generate a key and update its public certificate and certificate chain. Test should be able to
+/// load the key and able to verify whether its certificate and cert-chain are updated successfully.
+#[test]
+fn keystore2_update_subcomponent_success() {
+ let alias = "update_subcomponent_success_key";
+
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias.to_string()),
+ None,
+ )
+ .unwrap();
+
+ let other_cert: [u8; 32] = [123; 32];
+ let other_cert_chain: [u8; 32] = [12; 32];
+
+ keystore2
+ .updateSubcomponent(&key_metadata.key, Some(&other_cert), Some(&other_cert_chain))
+ .expect("updateSubcomponent should have succeeded.");
+
+ let key_entry_response = keystore2.getKeyEntry(&key_metadata.key).unwrap();
+ assert_eq!(Some(other_cert.to_vec()), key_entry_response.metadata.certificate);
+ assert_eq!(Some(other_cert_chain.to_vec()), key_entry_response.metadata.certificateChain);
+}
+
+/// Try to update non-existing asymmetric key public cert and certificate chain. Test should fail
+/// to update with error response code `KEY_NOT_FOUND`.
+#[test]
+fn keystore2_update_subcomponent_fail() {
+ let alias = "update_component_failure_key";
+
+ let keystore2 = get_keystore_service();
+
+ let other_cert: [u8; 32] = [123; 32];
+ let other_cert_chain: [u8; 32] = [12; 32];
+
+ let result = key_generations::map_ks_error(keystore2.updateSubcomponent(
+ &KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+ alias: Some(alias.to_string()),
+ blob: None,
+ },
+ Some(&other_cert),
+ Some(&other_cert_chain),
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Generate a key and grant it to two users. For one user grant it with only `GET_INFO` access
+/// permission and for another user grant it with GET_INFO and UPDATE access permissions. In a
+/// grantee context where key is granted with only GET_INFO access permission, try to update
+/// key's public certificate and certificate chain. Test should fail to update with error response
+/// code `PERMISSION_DENIED` because grantee does not possess UPDATE access permission for the
+/// specified key. In a grantee context where key is granted with UPDATE and GET_INFO access
+/// permissions, test should be able to update public certificate and cert-chain successfully.
+#[test]
+fn keystore2_update_subcomponent_fails_permission_denied() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const USER_ID_1: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_1_UID: u32 = USER_ID_1 * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_1_GID: u32 = GRANTEE_1_UID;
+
+ const USER_ID_2: u32 = 98;
+ static GRANTEE_2_UID: u32 = USER_ID_2 * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_2_GID: u32 = GRANTEE_2_UID;
+
+ // Generate a key and grant it to multiple users with different access permissions.
+ let mut granted_keys = unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_update_subcompo_test_1_{}", getuid());
+ let mut granted_keys = Vec::new();
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias),
+ None,
+ )
+ .unwrap();
+
+ // Grant a key without update permission.
+ let access_vector = KeyPermission::GET_INFO.0;
+ let granted_key = keystore2
+ .grant(&key_metadata.key, GRANTEE_1_UID.try_into().unwrap(), access_vector)
+ .unwrap();
+ assert_eq!(granted_key.domain, Domain::GRANT);
+ granted_keys.push(granted_key.nspace);
+
+ // Grant a key with update permission.
+ let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::UPDATE.0;
+ let granted_key = keystore2
+ .grant(&key_metadata.key, GRANTEE_2_UID.try_into().unwrap(), access_vector)
+ .unwrap();
+ assert_eq!(granted_key.domain, Domain::GRANT);
+ granted_keys.push(granted_key.nspace);
+
+ granted_keys
+ })
+ };
+
+ // Grantee context, try to update the key public certs, permission denied error is expected.
+ let granted_key1_nspace = granted_keys.remove(0);
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_1_UID),
+ Gid::from_raw(GRANTEE_1_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+
+ let other_cert: [u8; 32] = [123; 32];
+ let other_cert_chain: [u8; 32] = [12; 32];
+
+ let result = key_generations::map_ks_error(keystore2.updateSubcomponent(
+ &KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: granted_key1_nspace,
+ alias: None,
+ blob: None,
+ },
+ Some(&other_cert),
+ Some(&other_cert_chain),
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+ },
+ )
+ };
+
+ // Grantee context, update granted key public certs. Update should happen successfully.
+ let granted_key2_nspace = granted_keys.remove(0);
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_2_UID),
+ Gid::from_raw(GRANTEE_2_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+
+ let other_cert: [u8; 32] = [124; 32];
+ let other_cert_chain: [u8; 32] = [13; 32];
+
+ keystore2
+ .updateSubcomponent(
+ &KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: granted_key2_nspace,
+ alias: None,
+ blob: None,
+ },
+ Some(&other_cert),
+ Some(&other_cert_chain),
+ )
+ .expect("updateSubcomponent should have succeeded.");
+
+ let key_entry_response = keystore2
+ .getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: granted_key2_nspace,
+ alias: None,
+ blob: None,
+ })
+ .unwrap();
+ assert_eq!(Some(other_cert.to_vec()), key_entry_response.metadata.certificate);
+ assert_eq!(
+ Some(other_cert_chain.to_vec()),
+ key_entry_response.metadata.certificateChain
+ );
+ },
+ )
+ };
+}
+
+#[test]
+fn keystore2_get_security_level_success() {
+ let keystore2 = get_keystore_service();
+ assert!(
+ keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).is_ok(),
+ "getSecurityLevel with SecurityLevel::TRUSTED_ENVIRONMENT should have succeeded."
+ );
+}
+
+#[test]
+fn keystore2_get_security_level_failure() {
+ let keystore2 = get_keystore_service();
+ let result = key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::SOFTWARE));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+}
diff --git a/prng_seeder/Android.bp b/prng_seeder/Android.bp
index 292535a..763aaa0 100644
--- a/prng_seeder/Android.bp
+++ b/prng_seeder/Android.bp
@@ -63,3 +63,20 @@
installable: false,
prefer_rlib: true,
}
+
+rust_test {
+ name: "prng_seeder.test",
+ edition: "2021",
+ srcs: ["src/main.rs"],
+ rustlibs: [
+ "libanyhow",
+ "libbssl_ffi",
+ "libclap",
+ "libcutils_socket_bindgen",
+ "liblogger",
+ "liblog_rust",
+ "libnix",
+ "libtokio",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/prng_seeder/src/main.rs b/prng_seeder/src/main.rs
index 3f698f6..924481a 100644
--- a/prng_seeder/src/main.rs
+++ b/prng_seeder/src/main.rs
@@ -37,7 +37,7 @@
use crate::conditioner::ConditionerBuilder;
-#[derive(Debug, clap::Parser)]
+#[derive(Debug, Parser)]
struct Cli {
#[clap(long, default_value = "/dev/hw_random")]
source: PathBuf,
@@ -135,3 +135,14 @@
println!("prng_seeder: launch terminated: {:?}", e);
std::process::exit(-1);
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use clap::CommandFactory;
+
+ #[test]
+ fn verify_cli() {
+ Cli::command().debug_assert();
+ }
+}