summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-06-15 21:51:24 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-06-15 21:51:24 +0000
commit9c82adfa8ddf2ba7feee165b115e1ea2393aee29 (patch)
tree4092577b8206991d7a58795c34f03909f3a2d8c0
parentef2e840501d24a0785124f3bf568fe051e38309a (diff)
parentcd5ee701bcf1d5d23769c22c0bbad98a3c4fa431 (diff)
downloadsecurity-android12-mainline-tzdata3-release.tar.gz
Change-Id: I08e25e166197e06dd7718380e3c67ed2a5ba4bc6
-rw-r--r--OWNERS8
-rw-r--r--diced/Android.bp228
-rw-r--r--diced/aidl/Android.bp50
-rw-r--r--diced/aidl/android/security/dice/IDiceMaintenance.aidl39
-rw-r--r--diced/aidl/android/security/dice/IDiceNode.aidl96
-rw-r--r--diced/aidl/android/security/dice/ResponseCode.aidl37
-rw-r--r--diced/diced.microdroid.rc13
-rw-r--r--diced/diced.rc13
-rw-r--r--diced/open_dice_cbor/Android.bp55
-rw-r--r--diced/open_dice_cbor/lib.rs1037
-rw-r--r--diced/src/diced_client_test.rs188
-rw-r--r--diced/src/diced_main.rs76
-rw-r--r--diced/src/error.rs123
-rw-r--r--diced/src/error_vendor.rs119
-rw-r--r--diced/src/hal_node.rs725
-rw-r--r--diced/src/lib.rs203
-rw-r--r--diced/src/lib_vendor.rs20
-rw-r--r--diced/src/permission.rs46
-rw-r--r--diced/src/proxy_node_hal.rs119
-rw-r--r--diced/src/resident_node.rs191
-rw-r--r--diced/src/sample_inputs.rs255
-rw-r--r--diced/src/utils.rs381
-rw-r--r--fsverity/Android.bp66
-rw-r--r--fsverity/AndroidManifest.xml3
-rw-r--r--fsverity/OWNERS5
-rw-r--r--fsverity_init/Android.bp21
-rw-r--r--fsverity_init/OWNERS5
-rw-r--r--fsverity_init/fsverity_init.cpp48
-rw-r--r--fsverity_init/include/fsverity_init.h21
-rw-r--r--fsverity_init/main.cpp68
-rw-r--r--identity/Android.bp17
-rw-r--r--identity/Credential.cpp237
-rw-r--r--identity/Credential.h17
-rw-r--r--identity/CredentialData.cpp7
-rw-r--r--identity/CredentialData.h3
-rw-r--r--identity/CredentialStore.cpp117
-rw-r--r--identity/CredentialStore.h18
-rw-r--r--identity/Session.cpp101
-rw-r--r--identity/Session.h77
-rw-r--r--identity/TEST_MAPPING3
-rw-r--r--identity/binder/android/security/identity/ICredential.aidl6
-rw-r--r--identity/binder/android/security/identity/ICredentialStore.aidl4
-rw-r--r--identity/binder/android/security/identity/ISession.aidl34
-rw-r--r--identity/util/Android.bp42
-rw-r--r--identity/util/AndroidManifest.xml31
-rw-r--r--identity/util/AndroidTest.xml24
-rw-r--r--identity/util/src/java/com/android/security/identity/internal/Iso18013.java296
-rw-r--r--identity/util/src/java/com/android/security/identity/internal/Util.java1316
-rw-r--r--identity/util/test/java/com/android/security/identity/internal/HkdfTest.java201
-rw-r--r--identity/util/test/java/com/android/security/identity/internal/UtilUnitTests.java441
-rw-r--r--keystore-engine/Android.bp4
-rw-r--r--keystore-engine/keystore2_engine.cpp37
-rw-r--r--keystore/Android.bp12
-rw-r--r--keystore/keystore_cli_v2.cpp18
-rw-r--r--keystore/tests/Android.bp2
-rw-r--r--keystore/tests/confirmationui_invocation_test.cpp2
-rw-r--r--keystore/tests/fuzzer/Android.bp108
-rw-r--r--keystore/tests/fuzzer/README.md103
-rw-r--r--keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp67
-rw-r--r--keystore/tests/fuzzer/keystoreAttestationId_fuzzer.cpp29
-rw-r--r--keystore/tests/fuzzer/keystoreCommon.h77
-rw-r--r--keystore/tests/fuzzer/keystoreGetWifiHidl_fuzzer.cpp63
-rw-r--r--keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp46
-rw-r--r--keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp50
-rw-r--r--keystore2/Android.bp102
-rw-r--r--keystore2/TEST_MAPPING19
-rw-r--r--keystore2/aaid/Android.bp10
-rw-r--r--keystore2/aidl/Android.bp36
-rw-r--r--keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl13
-rw-r--r--keystore2/aidl/android/security/metrics/EcCurve.aidl1
-rw-r--r--keystore2/aidl/android/security/metrics/RkpErrorStats.aidl2
-rw-r--r--keystore2/aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl49
-rw-r--r--keystore2/aidl/android/security/remoteprovisioning/RemotelyProvisionedKey.aidl42
-rw-r--r--keystore2/android.system.keystore2-service.xml1
-rw-r--r--keystore2/apc_compat/Android.bp10
-rw-r--r--keystore2/keystore2.rc2
-rw-r--r--keystore2/legacykeystore/Android.bp18
-rw-r--r--keystore2/legacykeystore/lib.rs90
-rw-r--r--keystore2/selinux/Android.bp21
-rw-r--r--keystore2/selinux/src/concurrency_test.rs190
-rw-r--r--keystore2/selinux/src/lib.rs319
-rw-r--r--keystore2/src/apc.rs6
-rw-r--r--keystore2/src/async_task.rs6
-rw-r--r--keystore2/src/attestation_key_utils.rs19
-rw-r--r--keystore2/src/authorization.rs40
-rw-r--r--keystore2/src/boot_level_keys.rs40
-rw-r--r--keystore2/src/crypto/Android.bp14
-rw-r--r--keystore2/src/crypto/certificate_utils.cpp29
-rw-r--r--keystore2/src/crypto/crypto.cpp9
-rw-r--r--keystore2/src/crypto/crypto.hpp2
-rw-r--r--keystore2/src/crypto/error.rs9
-rw-r--r--keystore2/src/crypto/include/certificate_utils.h1
-rw-r--r--keystore2/src/crypto/lib.rs37
-rw-r--r--keystore2/src/crypto/tests/certificate_utils_test.cpp2
-rw-r--r--keystore2/src/crypto/zvec.rs20
-rw-r--r--keystore2/src/database.rs543
-rw-r--r--keystore2/src/database/utils.rs2
-rw-r--r--keystore2/src/enforcements.rs38
-rw-r--r--keystore2/src/error.rs27
-rw-r--r--keystore2/src/fuzzers/Android.bp29
-rw-r--r--keystore2/src/fuzzers/legacy_blob_fuzzer.rs26
-rw-r--r--keystore2/src/gc.rs10
-rw-r--r--keystore2/src/globals.rs214
-rw-r--r--keystore2/src/id_rotation.rs4
-rw-r--r--keystore2/src/key_parameter.rs92
-rw-r--r--keystore2/src/keystore2_main.rs43
-rw-r--r--keystore2/src/km_compat.rs588
-rw-r--r--keystore2/src/km_compat/Android.bp43
-rw-r--r--keystore2/src/km_compat/km_compat.cpp174
-rw-r--r--keystore2/src/km_compat/km_compat.h67
-rw-r--r--keystore2/src/km_compat/km_compat_type_conversion.h13
-rw-r--r--keystore2/src/km_compat/lib.rs10
-rw-r--r--keystore2/src/km_compat/slot_test.cpp70
-rw-r--r--keystore2/src/legacy_blob.rs1147
-rw-r--r--keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs (renamed from keystore2/src/legacy_blob/test_utils/legacy_blob_test_vectors.rs)207
-rw-r--r--keystore2/src/legacy_migrator.rs (renamed from keystore2/src/legacy_importer.rs)504
-rw-r--r--keystore2/src/lib.rs4
-rw-r--r--keystore2/src/maintenance.rs144
-rw-r--r--keystore2/src/metrics.rs2
-rw-r--r--keystore2/src/metrics_store.rs34
-rw-r--r--keystore2/src/operation.rs52
-rw-r--r--keystore2/src/permission.rs789
-rw-r--r--keystore2/src/raw_device.rs21
-rw-r--r--keystore2/src/remote_provisioning.rs881
-rw-r--r--keystore2/src/security_level.rs331
-rw-r--r--keystore2/src/service.rs117
-rw-r--r--keystore2/src/shared_secret_negotiation.rs113
-rw-r--r--keystore2/src/super_key.rs460
-rw-r--r--keystore2/src/try_insert.rs100
-rw-r--r--keystore2/src/utils.rs186
-rw-r--r--keystore2/src/vintf/Android.bp37
-rw-r--r--keystore2/src/vintf/lib.rs123
-rw-r--r--keystore2/src/vintf/vintf.cpp54
-rw-r--r--keystore2/src/vintf/vintf.hpp23
-rw-r--r--keystore2/src/watchdog.rs42
-rw-r--r--keystore2/system_property/Android.bp (renamed from keystore2/tests/legacy_blobs/Android.bp)46
-rw-r--r--keystore2/system_property/lib.rs217
-rw-r--r--keystore2/system_property/system_property_bindgen.hpp (renamed from fsverity/fsverity_digests.proto)13
-rw-r--r--keystore2/test_utils/authorizations.rs88
-rw-r--r--keystore2/test_utils/key_generations.rs68
-rw-r--r--keystore2/test_utils/lib.rs13
-rw-r--r--keystore2/test_utils/run_as.rs474
-rw-r--r--keystore2/tests/legacy_blobs/AndroidTest.xml34
-rw-r--r--keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs579
-rw-r--r--ondevice-signing/Android.bp40
-rw-r--r--ondevice-signing/CertUtils.cpp398
-rw-r--r--ondevice-signing/CertUtils.h (renamed from ondevice-signing/include/CertUtils.h)39
-rw-r--r--ondevice-signing/KeystoreHmacKey.cpp36
-rw-r--r--ondevice-signing/KeystoreHmacKey.h5
-rw-r--r--ondevice-signing/KeystoreKey.cpp55
-rw-r--r--ondevice-signing/KeystoreKey.h11
-rw-r--r--ondevice-signing/SigningKey.h (renamed from ondevice-signing/include/SigningKey.h)0
-rw-r--r--ondevice-signing/StatsReporter.cpp67
-rw-r--r--ondevice-signing/StatsReporter.h44
-rw-r--r--ondevice-signing/TEST_MAPPING2
-rw-r--r--ondevice-signing/VerityUtils.cpp300
-rw-r--r--ondevice-signing/VerityUtils.h (renamed from ondevice-signing/include/VerityUtils.h)20
-rw-r--r--ondevice-signing/odsign_main.cpp327
-rw-r--r--ondevice-signing/proto/Android.bp17
-rw-r--r--ondevice-signing/tests/Android.bp44
-rw-r--r--ondevice-signing/tests/SigningUtils.cert.derbin1551 -> 0 bytes
-rw-r--r--ondevice-signing/tests/SigningUtils.pem51
-rw-r--r--ondevice-signing/tests/SigningUtilsTest.cpp48
-rw-r--r--ondevice-signing/tests/test_filebin4096 -> 0 bytes
-rw-r--r--ondevice-signing/tests/test_file.sigbin512 -> 0 bytes
-rw-r--r--provisioner/Android.bp4
-rw-r--r--provisioner/rkp_factory_extraction_tool.cpp64
167 files changed, 3057 insertions, 16439 deletions
diff --git a/OWNERS b/OWNERS
index 563a78ce..bb510055 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,10 +1,6 @@
-alanstokes@google.com
+swillden@google.com
cbrubaker@google.com
-hasinitg@google.com
-jbires@google.com
jdanis@google.com
-jeffv@google.com
+hasinitg@google.com
kroot@google.com
-sethmo@google.com
-swillden@google.com
zeuthen@google.com
diff --git a/diced/Android.bp b/diced/Android.bp
deleted file mode 100644
index e13d863d..00000000
--- a/diced/Android.bp
+++ /dev/null
@@ -1,228 +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"],
-}
-
-rust_library {
- name: "libdiced_utils",
- crate_name: "diced_utils",
- srcs: ["src/utils.rs"],
- vendor_available: true,
-
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "libanyhow",
- "libdiced_open_dice_cbor",
- "libkeystore2_crypto_rust",
- ],
-}
-
-rust_test {
- name: "diced_utils_test",
- crate_name: "diced_utils_test",
- srcs: ["src/utils.rs"],
- test_suites: ["general-tests"],
- auto_gen_config: true,
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "libanyhow",
- "libdiced_open_dice_cbor",
- "libkeystore2_crypto_rust",
- ],
-}
-
-rust_library {
- name: "libdiced_sample_inputs",
- crate_name: "diced_sample_inputs",
- srcs: ["src/sample_inputs.rs"],
- vendor_available: true,
-
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "libanyhow",
- "libdiced_open_dice_cbor",
- "libdiced_utils",
- "libkeystore2_crypto_rust",
- ],
-}
-
-rust_test {
- name: "diced_sample_inputs_test",
- crate_name: "diced_sample_inputs_test",
- srcs: ["src/sample_inputs.rs"],
- test_suites: ["general-tests"],
- auto_gen_config: true,
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "libanyhow",
- "libdiced_open_dice_cbor",
- "libdiced_utils",
- "libkeystore2_crypto_rust",
- ],
-}
-
-rust_library {
- name: "libdiced",
- crate_name: "diced",
- srcs: ["src/lib.rs"],
-
- rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "android.security.dice-rust",
- "libdiced_open_dice_cbor",
- "libanyhow",
- "libbinder_rs",
- "libdiced_utils",
- "libkeystore2_crypto_rust",
- "libkeystore2_selinux",
- "liblibc",
- "liblog_rust",
- "libthiserror",
- ],
-}
-
-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/aidl/Android.bp b/diced/aidl/Android.bp
deleted file mode 100644
index 75c18564..00000000
--- a/diced/aidl/Android.bp
+++ /dev/null
@@ -1,50 +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",
- ],
- },
- 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 c81fdea1..00000000
--- 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 2b3ef764..00000000
--- 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 7c660580..00000000
--- 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 2226f473..00000000
--- 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 8c43fa5e..00000000
--- 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_cbor/Android.bp b/diced/open_dice_cbor/Android.bp
deleted file mode 100644
index 3e67045a..00000000
--- a/diced/open_dice_cbor/Android.bp
+++ /dev/null
@@ -1,55 +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 {
- 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,
-}
-
-rust_test {
- name: "diced_open_dice_cbor_test",
- crate_name: "diced_open_dice_cbor_test",
- srcs: ["lib.rs"],
- test_suites: ["general-tests"],
- auto_gen_config: true,
- rustlibs: [
- "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
deleted file mode 100644
index 7122ca51..00000000
--- a/diced/open_dice_cbor/lib.rs
+++ /dev/null
@@ -1,1037 +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.
-
-//! 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)]
-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 _,
- )
- })
- })
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
- 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,
- 0xb6, 0x9e, 0x3d, 0x04, 0xaf, 0x00, 0x15, 0xbc, 0xdd, 0x1f, 0xbc, 0x59, 0xe4, 0xc3, 0x87,
- 0x95, 0x5e,
- ];
-
- static SAMPLE_CDI_SEAL_TEST_VECTOR: &[u8] = &[
- 0x36, 0x1b, 0xd2, 0xb3, 0xc4, 0xda, 0x77, 0xb2, 0x9c, 0xba, 0x39, 0x53, 0x82, 0x93, 0xd9,
- 0xb8, 0x9f, 0x73, 0x2d, 0x27, 0x06, 0x15, 0xa8, 0xcb, 0x6d, 0x1d, 0xf2, 0xb1, 0x54, 0xbb,
- 0x62, 0xf1,
- ];
-
- static SAMPLE_BCC_TEST_VECTOR: &[u8] = &[
- 0x84, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 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,
- ];
-
- // This test invokes make_sample_bcc_and_cdis and compares the result bitwise to the target
- // vectors. The function uses main_flow, bcc_main_flow, format_config_descriptor,
- // derive_cdi_private_key_seed, and keypair_from_seed. This test is sensitive to errors
- // 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);
- }
-}
diff --git a/diced/src/diced_client_test.rs b/diced/src/diced_client_test.rs
deleted file mode 100644
index 39155088..00000000
--- 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 c2cf02c8..00000000
--- 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 3e230e4f..00000000
--- 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 e8657e0a..00000000
--- 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 01a75777..00000000
--- 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, 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 50e0e969..00000000
--- 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 01c804b7..00000000
--- 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 116df1b9..00000000
--- 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)]
- 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 8d883d27..00000000
--- 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 99a6dc9d..00000000
--- 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
deleted file mode 100644
index 93897a6f..00000000
--- a/diced/src/sample_inputs.rs
+++ /dev/null
@@ -1,255 +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 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::{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::io::Write;
-
-/// Sample UDS used to perform the root dice flow by `make_sample_bcc_and_cdis`.
-pub static UDS: &[u8; dice::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,
-];
-
-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.")?;
- cbor::encode_number(1, stream)
- .context("In encode_pub_key_ed25519: Trying to encode Key type tag.")?;
- cbor::encode_number(1, stream)
- .context("In encode_pub_key_ed25519: Trying to encode Key type.")?;
- cbor::encode_number(3, stream)
- .context("In encode_pub_key_ed25519: Trying to encode algorithm tag.")?;
- // Encoding a -8 for AlgorithmEdDSA. The encoded number is -1 - <header argument>,
- // the an argument of 7 below.
- cbor::encode_header(1 /* CBOR NEGATIVE INT */, 7 /* -1 -7 = -8*/, stream)
- .context("In encode_pub_key_ed25519: Trying to encode algorithm.")?;
- cbor::encode_number(4, stream)
- .context("In encode_pub_key_ed25519: Trying to encode ops tag.")?;
- // Ops 2 for verify.
- cbor::encode_number(2, stream).context("In encode_pub_key_ed25519: Trying to encode ops.")?;
- cbor::encode_header(1 /* CBOR NEGATIVE INT */, 0 /* -1 -0 = -1*/, stream)
- .context("In encode_pub_key_ed25519: Trying to encode curve tag.")?;
- // Curve 6 for Ed25519
- cbor::encode_number(6, stream).context("In encode_pub_key_ed25519: Trying to encode curve.")?;
- cbor::encode_header(1 /* CBOR NEGATIVE INT */, 1 /* -1 -1 = -2*/, stream)
- .context("In encode_pub_key_ed25519: Trying to encode X coordinate tag.")?;
- cbor::encode_bstr(pub_key, stream)
- .context("In encode_pub_key_ed25519: Trying to encode X coordinate.")?;
- 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)
- .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.")?;
-
- 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.")?;
-
- 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.")
-}
-
-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(),
- ]
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- // This simple test checks if the invocation succeeds, essentially it tests
- // if the initial bcc is accepted by `DiceContext::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
deleted file mode 100644
index 03e8969b..00000000
--- a/diced/src/utils.rs
+++ /dev/null
@@ -1,381 +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.
-
-//! 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 {
- use anyhow::{anyhow, Context, Result};
- use std::convert::TryInto;
- use std::io::Write;
-
- /// CBOR encodes a positive number.
- pub fn encode_number(n: u64, buffer: &mut dyn Write) -> Result<()> {
- encode_header(0, n, buffer)
- }
-
- /// CBOR encodes a binary string.
- pub fn encode_bstr(bstr: &[u8], buffer: &mut dyn Write) -> Result<()> {
- encode_header(
- 2,
- bstr.len().try_into().context("In encode_bstr: Failed to convert usize to u64.")?,
- buffer,
- )
- .context("In encode_bstr: While writing header.")?;
- let written = buffer.write(bstr).context("In encode_bstr: While writing payload.")?;
- if written != bstr.len() {
- return Err(anyhow!("In encode_bstr: Buffer too small. ({}, {})", written, bstr.len()));
- }
- Ok(())
- }
-
- /// Formats a CBOR header. `t` is the type, and n is the header argument.
- 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)
- })?;
- 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(
- || 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(&u8::to_be_bytes(n as u8)).with_context(|| {
- format!("In encode_header: Failed to write size ({}, {})", t, n)
- })?;
- if written != 1 {
- return Err(anyhow!(
- "In encode_header while writing size: Buffer to small. ({}, {})",
- t,
- n
- ));
- }
- }
- n if n <= 0xFFFF => {
- let written =
- buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (25u8 & 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(&u16::to_be_bytes(n as u16)).with_context(|| {
- format!("In encode_header: Failed to write size ({}, {})", t, n)
- })?;
- if written != 2 {
- return Err(anyhow!(
- "In encode_header while writing size: Buffer to small. ({}, {})",
- t,
- n
- ));
- }
- }
- n if n <= 0xFFFFFFFF => {
- let written =
- buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (26u8 & 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(&u32::to_be_bytes(n as u32)).with_context(|| {
- format!("In encode_header: Failed to write size ({}, {})", t, n)
- })?;
- if written != 4 {
- return Err(anyhow!(
- "In encode_header while writing size: Buffer to small. ({}, {})",
- t,
- n
- ));
- }
- }
- n => {
- let written =
- buffer.write(&u8::to_be_bytes(((t as u8) << 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(|| {
- format!("In encode_header: Failed to write size ({}, {})", t, n)
- })?;
- if written != 8 {
- return Err(anyhow!(
- "In encode_header while writing size: Buffer to small. ({}, {})",
- t,
- n
- ));
- }
- }
- }
- Ok(())
- }
-
- #[cfg(test)]
- mod test {
- use super::*;
-
- fn encode_header_helper(t: u8, n: u64) -> Vec<u8> {
- let mut b: Vec<u8> = vec![];
- encode_header(t, n, &mut b).unwrap();
- b
- }
-
- #[test]
- fn encode_header_test() {
- assert_eq!(&encode_header_helper(0, 0), &[0b000_00000]);
- assert_eq!(&encode_header_helper(0, 23), &[0b000_10111]);
- assert_eq!(&encode_header_helper(0, 24), &[0b000_11000, 24]);
- assert_eq!(&encode_header_helper(0, 0xff), &[0b000_11000, 0xff]);
- assert_eq!(&encode_header_helper(0, 0x100), &[0b000_11001, 0x01, 0x00]);
- assert_eq!(&encode_header_helper(0, 0xffff), &[0b000_11001, 0xff, 0xff]);
- assert_eq!(&encode_header_helper(0, 0x10000), &[0b000_11010, 0x00, 0x01, 0x00, 0x00]);
- assert_eq!(
- &encode_header_helper(0, 0xffffffff),
- &[0b000_11010, 0xff, 0xff, 0xff, 0xff]
- );
- assert_eq!(
- &encode_header_helper(0, 0x100000000),
- &[0b000_11011, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]
- );
- assert_eq!(
- &encode_header_helper(0, 0xffffffffffffffff),
- &[0b000_11011, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
- );
- }
- }
-}
diff --git a/fsverity/Android.bp b/fsverity/Android.bp
deleted file mode 100644
index 2fc3c01c..00000000
--- a/fsverity/Android.bp
+++ /dev/null
@@ -1,66 +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 {
- // 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"],
-}
-
-python_library_host {
- name: "fsverity_digests_proto_python",
- srcs: [
- "fsverity_digests.proto",
- ],
- required: [
- "fsverity",
- ],
- proto: {
- canonical_path_from_root: false,
- },
- version: {
- py2: {
- enabled: true,
- },
- py3: {
- enabled: true,
- },
- },
-}
-
-rust_protobuf {
- name: "libfsverity_digests_proto_rust",
- crate_name: "fsverity_digests_proto",
- source_stem: "fsverity_digests_proto",
- protos: [
- "fsverity_digests.proto",
- ],
- apex_available: [
- "com.android.compos",
- ],
-}
-
-cc_library_static {
- name: "libfsverity_digests_proto_cc",
- proto: {
- type: "lite",
- static: true,
- canonical_path_from_root: false,
- export_proto_headers: true,
- },
- srcs: ["fsverity_digests.proto"],
-}
diff --git a/fsverity/AndroidManifest.xml b/fsverity/AndroidManifest.xml
deleted file mode 100644
index 434955c4..00000000
--- a/fsverity/AndroidManifest.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.security.fsverity_metadata" />
diff --git a/fsverity/OWNERS b/fsverity/OWNERS
deleted file mode 100644
index f9e7b25e..00000000
--- a/fsverity/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-alanstokes@google.com
-ebiggers@google.com
-jeffv@google.com
-jiyong@google.com
-victorhsieh@google.com
diff --git a/fsverity_init/Android.bp b/fsverity_init/Android.bp
index 83c59457..39d4e6b6 100644
--- a/fsverity_init/Android.bp
+++ b/fsverity_init/Android.bp
@@ -10,34 +10,17 @@ package {
cc_binary {
name: "fsverity_init",
srcs: [
- "main.cpp",
+ "fsverity_init.cpp",
],
static_libs: [
"libc++fs",
- "libfsverity_init",
"libmini_keyctl_static",
],
shared_libs: [
"libbase",
"libkeyutils",
"liblog",
+ "liblogwrap",
],
cflags: ["-Werror", "-Wall", "-Wextra"],
}
-
-cc_library {
- name: "libfsverity_init",
- srcs: ["fsverity_init.cpp"],
- static_libs: [
- "libc++fs",
- "libmini_keyctl_static",
- ],
- shared_libs: [
- "libbase",
- "libkeyutils",
- "liblog",
- ],
- cflags: ["-Werror", "-Wall", "-Wextra"],
- export_include_dirs: ["include"],
- recovery_available: true,
-}
diff --git a/fsverity_init/OWNERS b/fsverity_init/OWNERS
deleted file mode 100644
index f9e7b25e..00000000
--- a/fsverity_init/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-alanstokes@google.com
-ebiggers@google.com
-jeffv@google.com
-jiyong@google.com
-victorhsieh@google.com
diff --git a/fsverity_init/fsverity_init.cpp b/fsverity_init/fsverity_init.cpp
index 61f84ddf..7ab4097e 100644
--- a/fsverity_init/fsverity_init.cpp
+++ b/fsverity_init/fsverity_init.cpp
@@ -37,17 +37,15 @@ bool LoadKeyToKeyring(key_serial_t keyring_id, const char* desc, const char* dat
return true;
}
-bool LoadKeyFromStdin(key_serial_t keyring_id, const char* keyname) {
+void LoadKeyFromStdin(key_serial_t keyring_id, const char* keyname) {
std::string content;
if (!android::base::ReadFdToString(STDIN_FILENO, &content)) {
LOG(ERROR) << "Failed to read key from stdin";
- return false;
+ return;
}
if (!LoadKeyToKeyring(keyring_id, keyname, content.c_str(), content.size())) {
LOG(ERROR) << "Failed to load key from stdin";
- return false;
}
- return true;
}
void LoadKeyFromFile(key_serial_t keyring_id, const char* keyname, const std::string& path) {
@@ -81,3 +79,45 @@ void LoadKeyFromVerifiedPartitions(key_serial_t keyring_id) {
LoadKeyFromDirectory(keyring_id, "fsv_system_", "/system/etc/security/fsverity");
LoadKeyFromDirectory(keyring_id, "fsv_product_", "/product/etc/security/fsverity");
}
+
+int main(int argc, const char** argv) {
+ if (argc < 2) {
+ LOG(ERROR) << "Not enough arguments";
+ return -1;
+ }
+
+ key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
+ if (keyring_id < 0) {
+ LOG(ERROR) << "Failed to find .fs-verity keyring id";
+ return -1;
+ }
+
+ const std::string_view command = argv[1];
+
+ if (command == "--load-verified-keys") {
+ LoadKeyFromVerifiedPartitions(keyring_id);
+ } else if (command == "--load-extra-key") {
+ if (argc != 3) {
+ LOG(ERROR) << "--load-extra-key requires <key_name> argument.";
+ return -1;
+ }
+ LoadKeyFromStdin(keyring_id, argv[2]);
+ } else if (command == "--lock") {
+ // Requires files backed by fs-verity to be verified with a key in .fs-verity
+ // keyring.
+ if (!android::base::WriteStringToFile("1", "/proc/sys/fs/verity/require_signatures")) {
+ PLOG(ERROR) << "Failed to enforce fs-verity signature";
+ }
+
+ if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+ if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+ PLOG(ERROR) << "Cannot restrict .fs-verity keyring";
+ }
+ }
+ } else {
+ LOG(ERROR) << "Unknown argument(s).";
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/fsverity_init/include/fsverity_init.h b/fsverity_init/include/fsverity_init.h
deleted file mode 100644
index c3bc93b4..00000000
--- a/fsverity_init/include/fsverity_init.h
+++ /dev/null
@@ -1,21 +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.
- */
-
-#include <mini_keyctl_utils.h>
-
-bool LoadKeyFromStdin(key_serial_t keyring_id, const char* keyname);
-void LoadKeyFromFile(key_serial_t keyring_id, const char* keyname, const std::string& path);
-void LoadKeyFromVerifiedPartitions(key_serial_t keyring_id);
diff --git a/fsverity_init/main.cpp b/fsverity_init/main.cpp
deleted file mode 100644
index 3f75dca9..00000000
--- a/fsverity_init/main.cpp
+++ /dev/null
@@ -1,68 +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.
- */
-
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <fsverity_init.h>
-#include <log/log.h>
-#include <mini_keyctl_utils.h>
-
-int main(int argc, const char** argv) {
- if (argc < 2) {
- LOG(ERROR) << "Not enough arguments";
- return -1;
- }
-
- key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
- if (keyring_id < 0) {
- LOG(ERROR) << "Failed to find .fs-verity keyring id";
- return -1;
- }
-
- const std::string_view command = argv[1];
-
- if (command == "--load-verified-keys") {
- LoadKeyFromVerifiedPartitions(keyring_id);
- } else if (command == "--load-extra-key") {
- if (argc != 3) {
- LOG(ERROR) << "--load-extra-key requires <key_name> argument.";
- return -1;
- }
- if (!LoadKeyFromStdin(keyring_id, argv[2])) {
- return -1;
- }
- } else if (command == "--lock") {
- // Requires files backed by fs-verity to be verified with a key in .fs-verity
- // keyring.
- if (!android::base::WriteStringToFile("1", "/proc/sys/fs/verity/require_signatures")) {
- PLOG(ERROR) << "Failed to enforce fs-verity signature";
- }
-
- if (!android::base::GetBoolProperty("ro.debuggable", false)) {
- if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
- PLOG(ERROR) << "Cannot restrict .fs-verity keyring";
- }
- }
- } else {
- LOG(ERROR) << "Unknown argument(s).";
- return -1;
- }
-
- return 0;
-}
diff --git a/identity/Android.bp b/identity/Android.bp
index b3b704fb..8267a6b1 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -24,11 +24,7 @@ cc_defaults {
cc_binary {
name: "credstore",
- defaults: [
- "identity_defaults",
- "keymint_use_latest_hal_aidl_ndk_shared",
- "keymint_use_latest_hal_aidl_cpp_static",
- ],
+ defaults: ["identity_defaults"],
srcs: [
"main.cpp",
@@ -37,7 +33,6 @@ cc_binary {
"WritableCredential.cpp",
"Credential.cpp",
"CredentialData.cpp",
- "Session.cpp",
"Util.cpp",
],
init_rc: ["credstore.rc"],
@@ -53,15 +48,14 @@ cc_binary {
"android.hardware.identity-support-lib",
"libkeymaster4support",
"libkeystore-attestation-application-id",
- "android.security.authorization-ndk",
- "android.security.remoteprovisioning-cpp",
- "libutilscallstack",
+ "android.hardware.security.keymint-V1-ndk_platform",
+ "android.security.authorization-ndk_platform",
],
static_libs: [
- "android.hardware.identity-V4-cpp",
+ "android.hardware.identity-V3-cpp",
"android.hardware.keymaster-V3-cpp",
"libcppbor_external",
- ],
+ ]
}
filegroup {
@@ -81,7 +75,6 @@ filegroup {
"binder/android/security/identity/AuthKeyParcel.aidl",
"binder/android/security/identity/SecurityHardwareInfoParcel.aidl",
"binder/android/security/identity/ICredentialStoreFactory.aidl",
- "binder/android/security/identity/ISession.aidl",
],
path: "binder",
}
diff --git a/identity/Credential.cpp b/identity/Credential.cpp
index c67fe4a3..7c75d8a2 100644
--- a/identity/Credential.cpp
+++ b/identity/Credential.cpp
@@ -70,10 +70,10 @@ using ::aidl::android::security::authorization::IKeystoreAuthorization;
Credential::Credential(CipherSuite cipherSuite, const std::string& dataPath,
const std::string& credentialName, uid_t callingUid,
HardwareInformation hwInfo, sp<IIdentityCredentialStore> halStoreBinder,
- sp<IPresentationSession> halSessionBinder, int halApiVersion)
+ int halApiVersion)
: cipherSuite_(cipherSuite), dataPath_(dataPath), credentialName_(credentialName),
callingUid_(callingUid), hwInfo_(std::move(hwInfo)), halStoreBinder_(halStoreBinder),
- halSessionBinder_(halSessionBinder), halApiVersion_(halApiVersion) {}
+ halApiVersion_(halApiVersion) {}
Credential::~Credential() {}
@@ -85,40 +85,25 @@ Status Credential::ensureOrReplaceHalBinder() {
"Error loading data for credential");
}
- // If we're in a session we explicitly don't get the binder to IIdentityCredential until
- // it's used in getEntries() which is the only method call allowed for sessions.
- //
- // Why? This is because we want to throw the IIdentityCredential object away as soon as it's
- // used because the HAL only guarantees a single IIdentityCredential object alive at a time
- // and in a session there may be multiple credentials in play and we want to do multiple
- // getEntries() calls on all of them.
- //
-
- if (!halSessionBinder_) {
- sp<IIdentityCredential> halBinder;
- Status status =
- halStoreBinder_->getCredential(cipherSuite_, data->getCredentialData(), &halBinder);
- if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
- int code = status.serviceSpecificErrorCode();
- if (code == IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED) {
- return halStatusToError(status, ICredentialStore::ERROR_CIPHER_SUITE_NOT_SUPPORTED);
- }
- }
- if (!status.isOk()) {
- LOG(ERROR) << "Error getting HAL binder";
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
+ sp<IIdentityCredential> halBinder;
+ Status status =
+ halStoreBinder_->getCredential(cipherSuite_, data->getCredentialData(), &halBinder);
+ if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
+ int code = status.serviceSpecificErrorCode();
+ if (code == IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED) {
+ return halStatusToError(status, ICredentialStore::ERROR_CIPHER_SUITE_NOT_SUPPORTED);
}
- halBinder_ = halBinder;
}
+ if (!status.isOk()) {
+ LOG(ERROR) << "Error getting HAL binder";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
+ }
+ halBinder_ = halBinder;
return Status::ok();
}
Status Credential::getCredentialKeyCertificateChain(std::vector<uint8_t>* _aidl_return) {
- if (halSessionBinder_) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Cannot be used with session");
- }
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -131,11 +116,7 @@ Status Credential::getCredentialKeyCertificateChain(std::vector<uint8_t>* _aidl_
// Returns operation handle
Status Credential::selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
- bool incrementUsageCount, int64_t* _aidl_return) {
- if (halSessionBinder_) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Cannot be used with session");
- }
+ int64_t* _aidl_return) {
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -146,7 +127,7 @@ Status Credential::selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingEx
// We just check if a key is available, we actually don't store it since we
// don't keep CredentialData around between binder calls.
const AuthKeyData* authKey =
- data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys, incrementUsageCount);
+ data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
if (authKey == nullptr) {
return Status::fromServiceSpecificError(
ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
@@ -167,19 +148,10 @@ bool Credential::ensureChallenge() {
}
int64_t challenge;
- // If we're in a session, the challenge is selected by the session
- if (halSessionBinder_) {
- Status status = halSessionBinder_->getAuthChallenge(&challenge);
- if (!status.isOk()) {
- LOG(ERROR) << "Error getting challenge from session: " << status.exceptionMessage();
- return false;
- }
- } else {
- Status status = halBinder_->createAuthChallenge(&challenge);
- if (!status.isOk()) {
- LOG(ERROR) << "Error getting challenge: " << status.exceptionMessage();
- return false;
- }
+ Status status = halBinder_->createAuthChallenge(&challenge);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Error getting challenge: " << status.exceptionMessage();
+ return false;
}
if (challenge == 0) {
LOG(ERROR) << "Returned challenge is 0 (bug in HAL or TA)";
@@ -246,8 +218,7 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
const vector<RequestNamespaceParcel>& requestNamespaces,
const vector<uint8_t>& sessionTranscript,
const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
- bool allowUsingExpiredKeys, bool incrementUsageCount,
- GetEntriesResultParcel* _aidl_return) {
+ bool allowUsingExpiredKeys, GetEntriesResultParcel* _aidl_return) {
GetEntriesResultParcel ret;
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
@@ -257,28 +228,6 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
"Error loading data for credential");
}
- // If used in a session, get the binder on demand...
- //
- sp<IIdentityCredential> halBinder = halBinder_;
- if (halSessionBinder_) {
- if (halBinder) {
- LOG(ERROR) << "Unexpected HAL binder for session";
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Unexpected HAL binder for session");
- }
- Status status = halSessionBinder_->getCredential(data->getCredentialData(), &halBinder);
- if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
- int code = status.serviceSpecificErrorCode();
- if (code == IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED) {
- return halStatusToError(status, ICredentialStore::ERROR_CIPHER_SUITE_NOT_SUPPORTED);
- }
- }
- if (!status.isOk()) {
- LOG(ERROR) << "Error getting HAL binder";
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
- }
- }
-
// Calculate requestCounts ahead of time and be careful not to include
// elements that don't exist.
//
@@ -405,40 +354,33 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
}
}
- // Reuse the same AuthKey over multiple getEntries() calls.
+ // Note that the selectAuthKey() method is only called if a CryptoObject is involved at
+ // the Java layer. So we could end up with no previously selected auth key and we may
+ // need one.
//
- bool updateUseCountOnDisk = false;
- if (!selectedAuthKey_) {
- // Note that the selectAuthKey() method is only called if a CryptoObject is involved at
- // the Java layer. So we could end up with no previously selected auth key and we may
- // need one.
+ const AuthKeyData* authKey =
+ data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
+ if (authKey == nullptr) {
+ // If no authKey is available, consider it an error only when a
+ // SessionTranscript was provided.
//
- const AuthKeyData* authKey = data->selectAuthKey(
- allowUsingExhaustedKeys, allowUsingExpiredKeys, incrementUsageCount);
- if (authKey == nullptr) {
- // If no authKey is available, consider it an error only when a
- // SessionTranscript was provided.
- //
- // We allow no SessionTranscript to be provided because it makes
- // the API simpler to deal with insofar it can be used without having
- // to generate any authentication keys.
- //
- // In this "no SessionTranscript is provided" mode we don't return
- // DeviceNameSpaces nor a MAC over DeviceAuthentication so we don't
- // need a device key.
- //
- if (sessionTranscript.size() > 0) {
- return Status::fromServiceSpecificError(
- ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
- "No suitable authentication key available and one is needed");
- }
- } else {
- // We did find an authKey. Store its contents for future getEntries() calls.
- updateUseCountOnDisk = true;
- selectedAuthKeySigningKeyBlob_ = authKey->keyBlob;
- selectedAuthKeyStaticAuthData_ = authKey->staticAuthenticationData;
+ // We allow no SessionTranscript to be provided because it makes
+ // the API simpler to deal with insofar it can be used without having
+ // to generate any authentication keys.
+ //
+ // In this "no SessionTranscript is provided" mode we don't return
+ // DeviceNameSpaces nor a MAC over DeviceAuthentication so we don't
+ // need a device key.
+ //
+ if (sessionTranscript.size() > 0) {
+ return Status::fromServiceSpecificError(
+ ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
+ "No suitable authentication key available and one is needed");
}
- selectedAuthKey_ = true;
+ }
+ vector<uint8_t> signingKeyBlob;
+ if (authKey != nullptr) {
+ signingKeyBlob = authKey->keyBlob;
}
// Pass the HAL enough information to allow calculating the size of
@@ -463,22 +405,22 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
}
// This is not catastrophic, we might be dealing with a version 1 implementation which
// doesn't have this method.
- Status status = halBinder->setRequestedNamespaces(halRequestNamespaces);
+ Status status = halBinder_->setRequestedNamespaces(halRequestNamespaces);
if (!status.isOk()) {
LOG(INFO) << "Failed setting expected requested namespaces, assuming V1 HAL "
<< "and continuing";
}
// Pass the verification token. Failure is OK, this method isn't in the V1 HAL.
- status = halBinder->setVerificationToken(aidlVerificationToken);
+ status = halBinder_->setVerificationToken(aidlVerificationToken);
if (!status.isOk()) {
LOG(INFO) << "Failed setting verification token, assuming V1 HAL "
<< "and continuing";
}
- status = halBinder->startRetrieval(selectedProfiles, aidlAuthToken, requestMessage,
- selectedAuthKeySigningKeyBlob_, sessionTranscript,
- readerSignature, requestCounts);
+ status =
+ halBinder_->startRetrieval(selectedProfiles, aidlAuthToken, requestMessage, signingKeyBlob,
+ sessionTranscript, readerSignature, requestCounts);
if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
int code = status.serviceSpecificErrorCode();
if (code == IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND) {
@@ -511,8 +453,8 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
}
status =
- halBinder->startRetrieveEntryValue(rns.namespaceName, rep.name, eData.value().size,
- eData.value().accessControlProfileIds);
+ halBinder_->startRetrieveEntryValue(rns.namespaceName, rep.name, eData.value().size,
+ eData.value().accessControlProfileIds);
if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
int code = status.serviceSpecificErrorCode();
if (code == IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED) {
@@ -540,7 +482,7 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
vector<uint8_t> value;
for (const auto& encryptedChunk : eData.value().encryptedChunks) {
vector<uint8_t> chunk;
- status = halBinder->retrieveEntryValue(encryptedChunk, &chunk);
+ status = halBinder_->retrieveEntryValue(encryptedChunk, &chunk);
if (!status.isOk()) {
return halStatusToGenericError(status);
}
@@ -554,14 +496,16 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
ret.resultNamespaces.push_back(resultNamespaceParcel);
}
- status = halBinder->finishRetrieval(&ret.mac, &ret.deviceNameSpaces);
+ status = halBinder_->finishRetrieval(&ret.mac, &ret.deviceNameSpaces);
if (!status.isOk()) {
return halStatusToGenericError(status);
}
- ret.staticAuthenticationData = selectedAuthKeyStaticAuthData_;
+ if (authKey != nullptr) {
+ ret.staticAuthenticationData = authKey->staticAuthenticationData;
+ }
// Ensure useCount is updated on disk.
- if (updateUseCountOnDisk) {
+ if (authKey != nullptr) {
if (!data->saveToDisk()) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
"Error saving data");
@@ -573,11 +517,6 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
}
Status Credential::deleteCredential(vector<uint8_t>* _aidl_return) {
- if (halSessionBinder_) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Cannot be used with session");
- }
-
vector<uint8_t> proofOfDeletionSignature;
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
@@ -605,12 +544,6 @@ Status Credential::deleteWithChallenge(const vector<uint8_t>& challenge,
return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
"Not implemented by HAL");
}
-
- if (halSessionBinder_) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Cannot be used with session");
- }
-
vector<uint8_t> proofOfDeletionSignature;
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
@@ -637,12 +570,6 @@ Status Credential::proveOwnership(const vector<uint8_t>& challenge, vector<uint8
return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
"Not implemented by HAL");
}
-
- if (halSessionBinder_) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Cannot be used with session");
- }
-
vector<uint8_t> proofOfOwnershipSignature;
Status status = halBinder_->proveOwnership(challenge, &proofOfOwnershipSignature);
if (!status.isOk()) {
@@ -653,26 +580,19 @@ Status Credential::proveOwnership(const vector<uint8_t>& challenge, vector<uint8
}
Status Credential::createEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
- if (halSessionBinder_) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Cannot be used with session");
- }
-
vector<uint8_t> keyPair;
Status status = halBinder_->createEphemeralKeyPair(&keyPair);
if (!status.isOk()) {
return halStatusToGenericError(status);
}
- time_t nowSeconds = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
- time_t validityNotBefore = nowSeconds;
- time_t validityNotAfter = nowSeconds + 24 * 60 * 60;
optional<vector<uint8_t>> pkcs12Bytes = ecKeyPairGetPkcs12(keyPair,
"ephemeralKey", // Alias for key
"0", // Serial, as a decimal number
"Credstore", // Issuer
"Ephemeral Key", // Subject
- validityNotBefore, validityNotAfter);
+ 0, // Validity Not Before
+ 24 * 60 * 60); // Validity Not After
if (!pkcs12Bytes) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
"Error creating PKCS#12 structure for key pair");
@@ -682,11 +602,6 @@ Status Credential::createEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
}
Status Credential::setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) {
- if (halSessionBinder_) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Cannot be used with session");
- }
-
Status status = halBinder_->setReaderEphemeralPublicKey(publicKey);
if (!status.isOk()) {
return halStatusToGenericError(status);
@@ -695,11 +610,6 @@ Status Credential::setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey)
}
Status Credential::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) {
- if (halSessionBinder_) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Cannot be used with session");
- }
-
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -715,11 +625,6 @@ Status Credential::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxU
}
Status Credential::getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) {
- if (halSessionBinder_) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Cannot be used with session");
- }
-
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -748,11 +653,6 @@ Status Credential::getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_
Status Credential::storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey,
const vector<uint8_t>& staticAuthData) {
- if (halSessionBinder_) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Cannot be used with session");
- }
-
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -781,12 +681,6 @@ Credential::storeStaticAuthenticationDataWithExpiration(const AuthKeyParcel& aut
return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
"Not implemented by HAL");
}
-
- if (halSessionBinder_) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Cannot be used with session");
- }
-
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -808,11 +702,6 @@ Credential::storeStaticAuthenticationDataWithExpiration(const AuthKeyParcel& aut
}
Status Credential::getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return) {
- if (halSessionBinder_) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Cannot be used with session");
- }
-
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -852,12 +741,6 @@ Status Credential::update(sp<IWritableCredential>* _aidl_return) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
"Not implemented by HAL");
}
-
- if (halSessionBinder_) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Cannot be used with session");
- }
-
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
diff --git a/identity/Credential.h b/identity/Credential.h
index 0906fea2..a76f3cc0 100644
--- a/identity/Credential.h
+++ b/identity/Credential.h
@@ -39,7 +39,6 @@ using ::android::hardware::identity::CipherSuite;
using ::android::hardware::identity::HardwareInformation;
using ::android::hardware::identity::IIdentityCredential;
using ::android::hardware::identity::IIdentityCredentialStore;
-using ::android::hardware::identity::IPresentationSession;
using ::android::hardware::identity::RequestDataItem;
using ::android::hardware::identity::RequestNamespace;
@@ -47,8 +46,7 @@ class Credential : public BnCredential {
public:
Credential(CipherSuite cipherSuite, const string& dataPath, const string& credentialName,
uid_t callingUid, HardwareInformation hwInfo,
- sp<IIdentityCredentialStore> halStoreBinder,
- sp<IPresentationSession> halSessionBinder, int halApiVersion);
+ sp<IIdentityCredentialStore> halStoreBinder, int halApiVersion);
~Credential();
Status ensureOrReplaceHalBinder();
@@ -69,14 +67,13 @@ class Credential : public BnCredential {
Status getCredentialKeyCertificateChain(vector<uint8_t>* _aidl_return) override;
Status selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
- bool incrementUsageCount, int64_t* _aidl_return) override;
+ int64_t* _aidl_return) override;
Status getEntries(const vector<uint8_t>& requestMessage,
const vector<RequestNamespaceParcel>& requestNamespaces,
const vector<uint8_t>& sessionTranscript,
const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
- bool allowUsingExpiredKeys, bool incrementUsageCount,
- GetEntriesResultParcel* _aidl_return) override;
+ bool allowUsingExpiredKeys, GetEntriesResultParcel* _aidl_return) override;
Status setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) override;
Status getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) override;
@@ -97,20 +94,12 @@ class Credential : public BnCredential {
uid_t callingUid_;
HardwareInformation hwInfo_;
sp<IIdentityCredentialStore> halStoreBinder_;
- sp<IPresentationSession> halSessionBinder_;
uint64_t selectedChallenge_ = 0;
sp<IIdentityCredential> halBinder_;
int halApiVersion_;
- // This is used to cache the selected AuthKey to ensure the same AuthKey is used across
- // multiple getEntries() calls.
- //
- bool selectedAuthKey_ = false;
- vector<uint8_t> selectedAuthKeySigningKeyBlob_;
- vector<uint8_t> selectedAuthKeyStaticAuthData_;
-
bool ensureChallenge();
ssize_t
diff --git a/identity/CredentialData.cpp b/identity/CredentialData.cpp
index 2189f909..74b995d8 100644
--- a/identity/CredentialData.cpp
+++ b/identity/CredentialData.cpp
@@ -538,8 +538,7 @@ AuthKeyData* CredentialData::findAuthKey_(bool allowUsingExhaustedKeys,
}
const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys,
- bool allowUsingExpiredKeys,
- bool incrementUsageCount) {
+ bool allowUsingExpiredKeys) {
AuthKeyData* candidate;
// First try to find a un-expired key..
@@ -557,9 +556,7 @@ const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys,
}
}
- if (incrementUsageCount) {
- candidate->useCount += 1;
- }
+ candidate->useCount += 1;
return candidate;
}
diff --git a/identity/CredentialData.h b/identity/CredentialData.h
index e240e473..24b55d3d 100644
--- a/identity/CredentialData.h
+++ b/identity/CredentialData.h
@@ -111,8 +111,7 @@ class CredentialData : public RefBase {
// Returns |nullptr| if a suitable key cannot be found. Otherwise returns
// the authentication and increases its use-count.
- const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
- bool incrementUsageCount);
+ const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys);
optional<vector<vector<uint8_t>>>
getAuthKeysNeedingCertification(const sp<IIdentityCredential>& halBinder);
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index c5c429b7..071cf247 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -17,66 +17,20 @@
#define LOG_TAG "credstore"
#include <algorithm>
-#include <optional>
#include <android-base/logging.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 "Credential.h"
#include "CredentialData.h"
#include "CredentialStore.h"
-#include "Session.h"
#include "Util.h"
#include "WritableCredential.h"
namespace android {
namespace security {
namespace identity {
-namespace {
-
-using ::android::hardware::security::keymint::IRemotelyProvisionedComponent;
-using ::android::hardware::security::keymint::RpcHardwareInfo;
-using ::android::security::remoteprovisioning::IRemotelyProvisionedKeyPool;
-using ::android::security::remoteprovisioning::RemotelyProvisionedKey;
-
-std::optional<std::string>
-getRemotelyProvisionedComponentId(const sp<IIdentityCredentialStore>& hal) {
- auto init = [](const sp<IIdentityCredentialStore>& hal) -> std::optional<std::string> {
- sp<IRemotelyProvisionedComponent> remotelyProvisionedComponent;
- Status status = hal->getRemotelyProvisionedComponent(&remotelyProvisionedComponent);
- if (!status.isOk()) {
- LOG(ERROR) << "Error getting remotely provisioned component: " << status;
- return std::nullopt;
- }
-
- RpcHardwareInfo rpcHwInfo;
- status = remotelyProvisionedComponent->getHardwareInfo(&rpcHwInfo);
- if (!status.isOk()) {
- LOG(ERROR) << "Error getting remotely provisioned component hardware info: " << status;
- return std::nullopt;
- }
-
- if (!rpcHwInfo.uniqueId) {
- LOG(ERROR) << "Remotely provisioned component is missing a unique id, which is "
- << "required for credential key remotely provisioned attestation keys. "
- << "This is a bug in the vendor implementation.";
- return std::nullopt;
- }
-
- // This id is required to later fetch remotely provisioned attestation keys.
- return *rpcHwInfo.uniqueId;
- };
-
- static std::optional<std::string> id = init(hal);
- return id;
-}
-
-} // namespace
CredentialStore::CredentialStore(const std::string& dataPath, sp<IIdentityCredentialStore> hal)
: dataPath_(dataPath), hal_(hal) {}
@@ -89,16 +43,6 @@ bool CredentialStore::init() {
}
halApiVersion_ = hal_->getInterfaceVersion();
- if (hwInfo_.isRemoteKeyProvisioningSupported) {
- keyPool_ = android::waitForService<IRemotelyProvisionedKeyPool>(
- IRemotelyProvisionedKeyPool::descriptor);
- if (keyPool_.get() == nullptr) {
- 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
@@ -145,21 +89,13 @@ Status CredentialStore::createCredential(const std::string& credentialName,
return halStatusToGenericError(status);
}
- if (hwInfo_.isRemoteKeyProvisioningSupported) {
- status = setRemotelyProvisionedAttestationKey(halWritableCredential.get());
- if (!status.isOk()) {
- return halStatusToGenericError(status);
- }
- }
-
sp<IWritableCredential> writableCredential = new WritableCredential(
dataPath_, credentialName, docType, false, hwInfo_, halWritableCredential);
*_aidl_return = writableCredential;
return Status::ok();
}
-Status CredentialStore::getCredentialCommon(const std::string& credentialName, int32_t cipherSuite,
- sp<IPresentationSession> halSessionBinder,
+Status CredentialStore::getCredentialByName(const std::string& credentialName, int32_t cipherSuite,
sp<ICredential>* _aidl_return) {
*_aidl_return = nullptr;
@@ -177,9 +113,8 @@ Status CredentialStore::getCredentialCommon(const std::string& credentialName, i
// Note: IdentityCredentialStore.java's CipherSuite enumeration and CipherSuite from the
// HAL is manually kept in sync. So this cast is safe.
- sp<Credential> credential =
- new Credential(CipherSuite(cipherSuite), dataPath_, credentialName, callingUid, hwInfo_,
- hal_, halSessionBinder, halApiVersion_);
+ sp<Credential> credential = new Credential(CipherSuite(cipherSuite), dataPath_, credentialName,
+ callingUid, hwInfo_, hal_, halApiVersion_);
Status loadStatus = credential->ensureOrReplaceHalBinder();
if (!loadStatus.isOk()) {
@@ -190,50 +125,6 @@ Status CredentialStore::getCredentialCommon(const std::string& credentialName, i
return loadStatus;
}
-Status CredentialStore::getCredentialByName(const std::string& credentialName, int32_t cipherSuite,
- sp<ICredential>* _aidl_return) {
- return getCredentialCommon(credentialName, cipherSuite, nullptr, _aidl_return);
-}
-
-Status CredentialStore::createPresentationSession(int32_t cipherSuite, sp<ISession>* _aidl_return) {
- sp<IPresentationSession> halPresentationSession;
- Status status =
- hal_->createPresentationSession(CipherSuite(cipherSuite), &halPresentationSession);
- if (!status.isOk()) {
- return halStatusToGenericError(status);
- }
-
- *_aidl_return = new Session(cipherSuite, halPresentationSession, this);
- return Status::ok();
-}
-
-Status CredentialStore::setRemotelyProvisionedAttestationKey(
- IWritableIdentityCredential* halWritableCredential) {
- std::optional<std::string> rpcId = getRemotelyProvisionedComponentId(hal_);
- if (!rpcId) {
- return Status::fromServiceSpecificError(ERROR_GENERIC,
- "Error getting remotely provisioned component id");
- }
-
- uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
- RemotelyProvisionedKey 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();
- }
-
- status = halWritableCredential->setRemotelyProvisionedAttestationKey(key.keyBlob,
- key.encodedCertChain);
- if (!status.isOk()) {
- LOG(ERROR) << "Error setting remotely provisioned attestation key on credential";
- return status;
- }
-
- return Status::ok();
-}
-
} // namespace identity
} // namespace security
} // namespace android
diff --git a/identity/CredentialStore.h b/identity/CredentialStore.h
index df7928e5..15da4eba 100644
--- a/identity/CredentialStore.h
+++ b/identity/CredentialStore.h
@@ -21,8 +21,8 @@
#include <vector>
#include <android/hardware/identity/IIdentityCredentialStore.h>
+
#include <android/security/identity/BnCredentialStore.h>
-#include <android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.h>
namespace android {
namespace security {
@@ -30,16 +30,12 @@ namespace identity {
using ::android::sp;
using ::android::binder::Status;
-using ::std::optional;
using ::std::string;
using ::std::unique_ptr;
using ::std::vector;
using ::android::hardware::identity::HardwareInformation;
using ::android::hardware::identity::IIdentityCredentialStore;
-using ::android::hardware::identity::IPresentationSession;
-using ::android::hardware::identity::IWritableIdentityCredential;
-using ::android::security::remoteprovisioning::IRemotelyProvisionedKeyPool;
class CredentialStore : public BnCredentialStore {
public:
@@ -48,12 +44,6 @@ class CredentialStore : public BnCredentialStore {
bool init();
- // Used by both getCredentialByName() and Session::getCredential()
- //
- Status getCredentialCommon(const string& credentialName, int32_t cipherSuite,
- sp<IPresentationSession> halSessionBinder,
- sp<ICredential>* _aidl_return);
-
// ICredentialStore overrides
Status getSecurityHardwareInfo(SecurityHardwareInfoParcel* _aidl_return) override;
@@ -63,18 +53,12 @@ class CredentialStore : public BnCredentialStore {
Status getCredentialByName(const string& credentialName, int32_t cipherSuite,
sp<ICredential>* _aidl_return) override;
- Status createPresentationSession(int32_t cipherSuite, sp<ISession>* _aidl_return) override;
-
private:
- Status setRemotelyProvisionedAttestationKey(IWritableIdentityCredential* halWritableCredential);
-
string dataPath_;
sp<IIdentityCredentialStore> hal_;
int halApiVersion_;
- sp<IRemotelyProvisionedKeyPool> keyPool_;
-
HardwareInformation hwInfo_;
};
diff --git a/identity/Session.cpp b/identity/Session.cpp
deleted file mode 100644
index 98ba3d3a..00000000
--- a/identity/Session.cpp
+++ /dev/null
@@ -1,101 +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.
- */
-
-#define LOG_TAG "credstore"
-
-#include <android-base/logging.h>
-#include <android/binder_manager.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <android/security/identity/ICredentialStore.h>
-#include <android/security/identity/ISession.h>
-
-#include "Session.h"
-#include "Util.h"
-
-namespace android {
-namespace security {
-namespace identity {
-
-using std::optional;
-
-using ::android::hardware::identity::IPresentationSession;
-using ::android::hardware::identity::IWritableIdentityCredential;
-
-using ::android::hardware::identity::support::ecKeyPairGetPkcs12;
-using ::android::hardware::identity::support::ecKeyPairGetPrivateKey;
-using ::android::hardware::identity::support::ecKeyPairGetPublicKey;
-using ::android::hardware::identity::support::hexdump;
-using ::android::hardware::identity::support::sha256;
-
-Status Session::getEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
- vector<uint8_t> keyPair;
- Status status = halBinder_->getEphemeralKeyPair(&keyPair);
- if (!status.isOk()) {
- return halStatusToGenericError(status);
- }
- time_t nowSeconds = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
- time_t validityNotBefore = nowSeconds;
- time_t validityNotAfter = nowSeconds + 24 * 60 * 60;
- optional<vector<uint8_t>> pkcs12Bytes = ecKeyPairGetPkcs12(keyPair,
- "ephemeralKey", // Alias for key
- "0", // Serial, as a decimal number
- "Credstore", // Issuer
- "Ephemeral Key", // Subject
- validityNotBefore, validityNotAfter);
- if (!pkcs12Bytes) {
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Error creating PKCS#12 structure for key pair");
- }
- *_aidl_return = pkcs12Bytes.value();
- return Status::ok();
-}
-
-Status Session::setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) {
- Status status = halBinder_->setReaderEphemeralPublicKey(publicKey);
- if (!status.isOk()) {
- return halStatusToGenericError(status);
- }
- return Status::ok();
-}
-
-Status Session::setSessionTranscript(const vector<uint8_t>& sessionTranscript) {
- Status status = halBinder_->setSessionTranscript(sessionTranscript);
- if (!status.isOk()) {
- return halStatusToGenericError(status);
- }
- return Status::ok();
-}
-
-Status Session::getCredentialForPresentation(const string& credentialName,
- sp<ICredential>* _aidl_return) {
- return store_->getCredentialCommon(credentialName, cipherSuite_, halBinder_, _aidl_return);
-}
-
-Status Session::getAuthChallenge(int64_t* _aidl_return) {
- *_aidl_return = 0;
- int64_t authChallenge;
- Status status = halBinder_->getAuthChallenge(&authChallenge);
- if (!status.isOk()) {
- return halStatusToGenericError(status);
- }
- *_aidl_return = authChallenge;
- return Status::ok();
-}
-
-} // namespace identity
-} // namespace security
-} // namespace android
diff --git a/identity/Session.h b/identity/Session.h
deleted file mode 100644
index 116c2fd1..00000000
--- a/identity/Session.h
+++ /dev/null
@@ -1,77 +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.
- */
-
-#ifndef SYSTEM_SECURITY_PRESENTATION_H_
-#define SYSTEM_SECURITY_PRESENTATION_H_
-
-#include <string>
-#include <vector>
-
-#include <android/security/identity/BnSession.h>
-
-#include <android/hardware/identity/IPresentationSession.h>
-
-#include <android/hardware/identity/IIdentityCredentialStore.h>
-
-#include "CredentialStore.h"
-
-namespace android {
-namespace security {
-namespace identity {
-
-using ::android::sp;
-using ::android::binder::Status;
-using ::std::string;
-using ::std::vector;
-
-using ::android::hardware::identity::CipherSuite;
-using ::android::hardware::identity::HardwareInformation;
-using ::android::hardware::identity::IIdentityCredential;
-using ::android::hardware::identity::IIdentityCredentialStore;
-using ::android::hardware::identity::IPresentationSession;
-using ::android::hardware::identity::RequestDataItem;
-using ::android::hardware::identity::RequestNamespace;
-
-class Session : public BnSession {
- public:
- Session(int32_t cipherSuite, sp<IPresentationSession> halBinder, sp<CredentialStore> store)
- : cipherSuite_(cipherSuite), halBinder_(halBinder), store_(store) {}
-
- bool initialize();
-
- // ISession overrides
- Status getEphemeralKeyPair(vector<uint8_t>* _aidl_return) override;
-
- Status setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override;
-
- Status setSessionTranscript(const vector<uint8_t>& sessionTranscript) override;
-
- Status getAuthChallenge(int64_t* _aidl_return) override;
-
- Status getCredentialForPresentation(const string& credentialName,
- sp<ICredential>* _aidl_return) override;
-
- private:
- int32_t cipherSuite_;
- sp<IPresentationSession> halBinder_;
- sp<CredentialStore> store_;
-};
-
-} // namespace identity
-} // namespace security
-} // namespace android
-
-#endif // SYSTEM_SECURITY_SESSION_H_
diff --git a/identity/TEST_MAPPING b/identity/TEST_MAPPING
index 6444c56a..87707a84 100644
--- a/identity/TEST_MAPPING
+++ b/identity/TEST_MAPPING
@@ -2,9 +2,6 @@
"presubmit": [
{
"name": "CtsIdentityTestCases"
- },
- {
- "name": "identity-credential-util-tests"
}
]
}
diff --git a/identity/binder/android/security/identity/ICredential.aidl b/identity/binder/android/security/identity/ICredential.aidl
index e6a9fae0..2165810f 100644
--- a/identity/binder/android/security/identity/ICredential.aidl
+++ b/identity/binder/android/security/identity/ICredential.aidl
@@ -49,16 +49,14 @@ interface ICredential {
byte[] getCredentialKeyCertificateChain();
long selectAuthKey(in boolean allowUsingExhaustedKeys,
- in boolean allowUsingExpiredKeys,
- in boolean incrementUsageCount);
+ in boolean allowUsingExpiredKeys);
GetEntriesResultParcel getEntries(in byte[] requestMessage,
in RequestNamespaceParcel[] requestNamespaces,
in byte[] sessionTranscript,
in byte[] readerSignature,
in boolean allowUsingExhaustedKeys,
- in boolean allowUsingExpiredKeys,
- in boolean incrementUsageCount);
+ in boolean allowUsingExpiredKeys);
void setAvailableAuthenticationKeys(in int keyCount, in int maxUsesPerKey);
diff --git a/identity/binder/android/security/identity/ICredentialStore.aidl b/identity/binder/android/security/identity/ICredentialStore.aidl
index 39b5e5f6..8357f47b 100644
--- a/identity/binder/android/security/identity/ICredentialStore.aidl
+++ b/identity/binder/android/security/identity/ICredentialStore.aidl
@@ -19,7 +19,6 @@ package android.security.identity;
import android.security.identity.IWritableCredential;
import android.security.identity.ICredential;
import android.security.identity.SecurityHardwareInfoParcel;
-import android.security.identity.ISession;
/**
* @hide
@@ -46,9 +45,6 @@ interface ICredentialStore {
IWritableCredential createCredential(in @utf8InCpp String credentialName,
in @utf8InCpp String docType);
-
ICredential getCredentialByName(in @utf8InCpp String credentialName,
in int cipherSuite);
-
- ISession createPresentationSession(in int cipherSuite);
}
diff --git a/identity/binder/android/security/identity/ISession.aidl b/identity/binder/android/security/identity/ISession.aidl
deleted file mode 100644
index 2139ec1c..00000000
--- a/identity/binder/android/security/identity/ISession.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (c) 2019, 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.identity;
-
-import android.security.identity.ICredential;
-
-/**
- * @hide
- */
-interface ISession {
- byte[] getEphemeralKeyPair();
-
- long getAuthChallenge();
-
- void setReaderEphemeralPublicKey(in byte[] publicKey);
-
- void setSessionTranscript(in byte[] sessionTranscript);
-
- ICredential getCredentialForPresentation(in @utf8InCpp String credentialName);
-}
diff --git a/identity/util/Android.bp b/identity/util/Android.bp
deleted file mode 100644
index 71d77187..00000000
--- a/identity/util/Android.bp
+++ /dev/null
@@ -1,42 +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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_library {
- name: "identity-credential-util",
- srcs: [
- "src/java/**/*.java",
- ],
- static_libs: [
- "androidx.annotation_annotation",
- "bouncycastle-unbundled",
- "cbor-java",
- ],
-}
-
-android_test {
- name: "identity-credential-util-tests",
- test_suites: ["general-tests"],
- srcs: [
- "test/java/**/*.java",
- ],
- static_libs: [
- "androidx.test.rules",
- "identity-credential-util",
- "junit",
- ],
-}
diff --git a/identity/util/AndroidManifest.xml b/identity/util/AndroidManifest.xml
deleted file mode 100644
index eece4dc0..00000000
--- a/identity/util/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.security.identity.internal">
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.security.identity.internal"
- android:label="Unit tests for com.android.security.identity.internal"/>
-
-</manifest>
-
diff --git a/identity/util/AndroidTest.xml b/identity/util/AndroidTest.xml
deleted file mode 100644
index 345460f0..00000000
--- a/identity/util/AndroidTest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<configuration description="Config for identity cred support library tests">
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="identity-credential-util-tests.apk" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.InstrumentationTest" >
- <option name="package" value="com.android.security.identity.internal" />
- </test>
-</configuration>
diff --git a/identity/util/src/java/com/android/security/identity/internal/Iso18013.java b/identity/util/src/java/com/android/security/identity/internal/Iso18013.java
deleted file mode 100644
index 6da90e52..00000000
--- a/identity/util/src/java/com/android/security/identity/internal/Iso18013.java
+++ /dev/null
@@ -1,296 +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 com.android.security.identity.internal;
-
-import static com.android.security.identity.internal.Util.CBOR_SEMANTIC_TAG_ENCODED_CBOR;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.InvalidKeyException;
-import java.security.InvalidParameterException;
-import java.security.KeyPair;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECPoint;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import javax.crypto.KeyAgreement;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-import co.nstant.in.cbor.CborBuilder;
-import co.nstant.in.cbor.CborDecoder;
-import co.nstant.in.cbor.CborEncoder;
-import co.nstant.in.cbor.CborException;
-import co.nstant.in.cbor.builder.MapBuilder;
-import co.nstant.in.cbor.model.ByteString;
-import co.nstant.in.cbor.model.DataItem;
-
-/**
- * Various utilities for working with the ISO mobile driving license (mDL)
- * application specification (ISO 18013-5).
- */
-public class Iso18013 {
- /**
- * Each version of the spec is namespaced, and all namespace-specific constants
- * are thus collected into a namespace-specific nested class.
- */
- public static class V1 {
- public static final String NAMESPACE = "org.iso.18013.5.1";
- public static final String DOC_TYPE = "org.iso.18013.5.1.mdl";
-
- public static final String FAMILY_NAME = "family_name";
- public static final String GIVEN_NAME = "given_name";
- public static final String BIRTH_DATE = "birth_date";
- public static final String ISSUE_DATE = "issue_date";
- public static final String EXPIRY = "expiry_date";
- public static final String ISSUING_COUNTRY = "issuing_country";
- public static final String ISSUING_AUTHORITY = "issuing_authority";
- public static final String DOCUMENT_NUMBER = "document_number";
- public static final String PORTRAIT = "portrait";
- public static final String DRIVING_PRIVILEGES = "driving_privileges";
- public static final String UN_DISTINGUISHING_SIGN = "un_distinguishing_sign";
- public static final String HEIGHT = "height";
- public static final String BIO_FACE = "biometric_template_face";
-
- public static String ageOver(int age) {
- if (age < 0 || age > 99) {
- throw new InvalidParameterException("age must be between 0 and 99, inclusive");
- }
- return String.format("age_over_%02d", age);
- }
- }
-
- public static byte[] buildDeviceAuthenticationCbor(String docType,
- byte[] encodedSessionTranscript,
- byte[] deviceNameSpacesBytes) {
- ByteArrayOutputStream daBaos = new ByteArrayOutputStream();
- try {
- ByteArrayInputStream bais = new ByteArrayInputStream(encodedSessionTranscript);
- List<DataItem> dataItems = null;
- dataItems = new CborDecoder(bais).decode();
- DataItem sessionTranscript = dataItems.get(0);
- ByteString deviceNameSpacesBytesItem = new ByteString(deviceNameSpacesBytes);
- deviceNameSpacesBytesItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
- new CborEncoder(daBaos).encode(new CborBuilder()
- .addArray()
- .add("DeviceAuthentication")
- .add(sessionTranscript)
- .add(docType)
- .add(deviceNameSpacesBytesItem)
- .end()
- .build());
- } catch (CborException e) {
- throw new RuntimeException("Error encoding DeviceAuthentication", e);
- }
- return daBaos.toByteArray();
- }
-
- public static byte[] buildReaderAuthenticationBytesCbor(
- byte[] encodedSessionTranscript,
- byte[] requestMessageBytes) {
-
- ByteArrayOutputStream daBaos = new ByteArrayOutputStream();
- try {
- ByteArrayInputStream bais = new ByteArrayInputStream(encodedSessionTranscript);
- List<DataItem> dataItems = null;
- dataItems = new CborDecoder(bais).decode();
- DataItem sessionTranscript = dataItems.get(0);
- ByteString requestMessageBytesItem = new ByteString(requestMessageBytes);
- requestMessageBytesItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
- new CborEncoder(daBaos).encode(new CborBuilder()
- .addArray()
- .add("ReaderAuthentication")
- .add(sessionTranscript)
- .add(requestMessageBytesItem)
- .end()
- .build());
- } catch (CborException e) {
- throw new RuntimeException("Error encoding ReaderAuthentication", e);
- }
- byte[] readerAuthentication = daBaos.toByteArray();
- return Util.prependSemanticTagForEncodedCbor(readerAuthentication);
- }
-
- // This returns a SessionTranscript which satisfy the requirement
- // that the uncompressed X and Y coordinates of the public key for the
- // mDL's ephemeral key-pair appear somewhere in the encoded
- // DeviceEngagement.
- public static byte[] buildSessionTranscript(KeyPair ephemeralKeyPair) {
- // Make the coordinates appear in an already encoded bstr - this
- // mimics how the mDL COSE_Key appear as encoded data inside the
- // encoded DeviceEngagement
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
- // X and Y are always positive so for interop we remove any leading zeroes
- // inserted by the BigInteger encoder.
- byte[] x = stripLeadingZeroes(w.getAffineX().toByteArray());
- byte[] y = stripLeadingZeroes(w.getAffineY().toByteArray());
- baos.write(new byte[]{41});
- baos.write(x);
- baos.write(y);
- baos.write(new byte[]{42, 44});
- } catch (IOException e) {
- e.printStackTrace();
- return null;
- }
- byte[] blobWithCoords = baos.toByteArray();
-
- baos = new ByteArrayOutputStream();
- try {
- new CborEncoder(baos).encode(new CborBuilder()
- .addArray()
- .add(blobWithCoords)
- .end()
- .build());
- } catch (CborException e) {
- e.printStackTrace();
- return null;
- }
- ByteString encodedDeviceEngagementItem = new ByteString(baos.toByteArray());
- ByteString encodedEReaderKeyItem = new ByteString(Util.cborEncodeString("doesn't matter"));
- encodedDeviceEngagementItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
- encodedEReaderKeyItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
-
- baos = new ByteArrayOutputStream();
- try {
- new CborEncoder(baos).encode(new CborBuilder()
- .addArray()
- .add(encodedDeviceEngagementItem)
- .add(encodedEReaderKeyItem)
- .end()
- .build());
- } catch (CborException e) {
- e.printStackTrace();
- return null;
- }
- return baos.toByteArray();
- }
-
- /*
- * Helper function to create a CBOR data for requesting data items. The IntentToRetain
- * value will be set to false for all elements.
- *
- * <p>The returned CBOR data conforms to the following CDDL schema:</p>
- *
- * <pre>
- * ItemsRequest = {
- * ? "docType" : DocType,
- * "nameSpaces" : NameSpaces,
- * ? "RequestInfo" : {* tstr => any} ; Additional info the reader wants to provide
- * }
- *
- * NameSpaces = {
- * + NameSpace => DataElements ; Requested data elements for each NameSpace
- * }
- *
- * DataElements = {
- * + DataElement => IntentToRetain
- * }
- *
- * DocType = tstr
- *
- * DataElement = tstr
- * IntentToRetain = bool
- * NameSpace = tstr
- * </pre>
- *
- * @param entriesToRequest The entries to request, organized as a map of namespace
- * names with each value being a collection of data elements
- * in the given namespace.
- * @param docType The document type or {@code null} if there is no document
- * type.
- * @return CBOR data conforming to the CDDL mentioned above.
- */
- public static @NonNull
- byte[] createItemsRequest(
- @NonNull Map<String, Collection<String>> entriesToRequest,
- @Nullable String docType) {
- CborBuilder builder = new CborBuilder();
- MapBuilder<CborBuilder> mapBuilder = builder.addMap();
- if (docType != null) {
- mapBuilder.put("docType", docType);
- }
-
- MapBuilder<MapBuilder<CborBuilder>> nsMapBuilder = mapBuilder.putMap("nameSpaces");
- for (String namespaceName : entriesToRequest.keySet()) {
- Collection<String> entryNames = entriesToRequest.get(namespaceName);
- MapBuilder<MapBuilder<MapBuilder<CborBuilder>>> entryNameMapBuilder =
- nsMapBuilder.putMap(namespaceName);
- for (String entryName : entryNames) {
- entryNameMapBuilder.put(entryName, false);
- }
- }
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- CborEncoder encoder = new CborEncoder(baos);
- try {
- encoder.encode(builder.build());
- } catch (CborException e) {
- throw new RuntimeException("Error encoding CBOR", e);
- }
- return baos.toByteArray();
- }
-
- public static SecretKey calcEMacKeyForReader(PublicKey authenticationPublicKey,
- PrivateKey ephemeralReaderPrivateKey,
- byte[] encodedSessionTranscript) {
- try {
- KeyAgreement ka = KeyAgreement.getInstance("ECDH");
- ka.init(ephemeralReaderPrivateKey);
- ka.doPhase(authenticationPublicKey, true);
- byte[] sharedSecret = ka.generateSecret();
-
- byte[] sessionTranscriptBytes =
- Util.cborEncode(Util.buildCborTaggedByteString(encodedSessionTranscript));
-
- byte[] salt = MessageDigest.getInstance("SHA-256").digest(sessionTranscriptBytes);
- byte[] info = new byte[]{'E', 'M', 'a', 'c', 'K', 'e', 'y'};
- byte[] derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
-
- SecretKey secretKey = new SecretKeySpec(derivedKey, "");
- return secretKey;
- } catch (InvalidKeyException
- | NoSuchAlgorithmException e) {
- throw new IllegalStateException("Error performing key agreement", e);
- }
- }
-
- private static byte[] stripLeadingZeroes(byte[] value) {
- int n = 0;
- while (n < value.length && value[n] == 0) {
- n++;
- }
- int newLen = value.length - n;
- byte[] ret = new byte[newLen];
- int m = 0;
- while (n < value.length) {
- ret[m++] = value[n++];
- }
- return ret;
- }
-}
diff --git a/identity/util/src/java/com/android/security/identity/internal/Util.java b/identity/util/src/java/com/android/security/identity/internal/Util.java
deleted file mode 100644
index 4ec54a72..00000000
--- a/identity/util/src/java/com/android/security/identity/internal/Util.java
+++ /dev/null
@@ -1,1316 +0,0 @@
-/*
- * Copyright 2019 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 com.android.security.identity.internal;
-
-import android.security.identity.ResultData;
-import android.security.identity.IdentityCredentialStore;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.FeatureInfo;
-import android.os.SystemProperties;
-import android.security.keystore.KeyProperties;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.KeyStore;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.PrivateKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.spec.ECGenParameterSpec;
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.Formatter;
-import java.util.Map;
-
-import javax.crypto.KeyAgreement;
-import javax.crypto.Mac;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECPoint;
-
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1OctetString;
-
-import co.nstant.in.cbor.CborBuilder;
-import co.nstant.in.cbor.CborDecoder;
-import co.nstant.in.cbor.CborEncoder;
-import co.nstant.in.cbor.CborException;
-import co.nstant.in.cbor.builder.ArrayBuilder;
-import co.nstant.in.cbor.builder.MapBuilder;
-import co.nstant.in.cbor.model.AbstractFloat;
-import co.nstant.in.cbor.model.Array;
-import co.nstant.in.cbor.model.ByteString;
-import co.nstant.in.cbor.model.DataItem;
-import co.nstant.in.cbor.model.DoublePrecisionFloat;
-import co.nstant.in.cbor.model.MajorType;
-import co.nstant.in.cbor.model.NegativeInteger;
-import co.nstant.in.cbor.model.SimpleValue;
-import co.nstant.in.cbor.model.SimpleValueType;
-import co.nstant.in.cbor.model.SpecialType;
-import co.nstant.in.cbor.model.UnicodeString;
-import co.nstant.in.cbor.model.UnsignedInteger;
-
-public class Util {
- private static final String TAG = "Util";
-
- public static byte[] canonicalizeCbor(byte[] encodedCbor) throws CborException {
- ByteArrayInputStream bais = new ByteArrayInputStream(encodedCbor);
- List<DataItem> dataItems = new CborDecoder(bais).decode();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- for(DataItem dataItem : dataItems) {
- CborEncoder encoder = new CborEncoder(baos);
- encoder.encode(dataItem);
- }
- return baos.toByteArray();
- }
-
-
- public static String cborPrettyPrint(byte[] encodedBytes) throws CborException {
- StringBuilder sb = new StringBuilder();
-
- ByteArrayInputStream bais = new ByteArrayInputStream(encodedBytes);
- List<DataItem> dataItems = new CborDecoder(bais).decode();
- int count = 0;
- for (DataItem dataItem : dataItems) {
- if (count > 0) {
- sb.append(",\n");
- }
- cborPrettyPrintDataItem(sb, 0, dataItem);
- count++;
- }
-
- return sb.toString();
- }
-
- // Returns true iff all elements in |items| are not compound (e.g. an array or a map).
- static boolean cborAreAllDataItemsNonCompound(List<DataItem> items) {
- for (DataItem item : items) {
- switch (item.getMajorType()) {
- case ARRAY:
- case MAP:
- return false;
- default:
- // continue inspecting other data items
- }
- }
- return true;
- }
-
- public static void cborPrettyPrintDataItem(StringBuilder sb, int indent, DataItem dataItem) {
- StringBuilder indentBuilder = new StringBuilder();
- for (int n = 0; n < indent; n++) {
- indentBuilder.append(' ');
- }
- String indentString = indentBuilder.toString();
-
- if (dataItem.hasTag()) {
- sb.append(String.format("tag %d ", dataItem.getTag().getValue()));
- }
-
- switch (dataItem.getMajorType()) {
- case INVALID:
- // TODO: throw
- sb.append("<invalid>");
- break;
- case UNSIGNED_INTEGER: {
- // Major type 0: an unsigned integer.
- BigInteger value = ((UnsignedInteger) dataItem).getValue();
- sb.append(value);
- }
- break;
- case NEGATIVE_INTEGER: {
- // Major type 1: a negative integer.
- BigInteger value = ((NegativeInteger) dataItem).getValue();
- sb.append(value);
- }
- break;
- case BYTE_STRING: {
- // Major type 2: a byte string.
- byte[] value = ((ByteString) dataItem).getBytes();
- sb.append("[");
- int count = 0;
- for (byte b : value) {
- if (count > 0) {
- sb.append(", ");
- }
- sb.append(String.format("0x%02x", b));
- count++;
- }
- sb.append("]");
- }
- break;
- case UNICODE_STRING: {
- // Major type 3: string of Unicode characters that is encoded as UTF-8 [RFC3629].
- String value = ((UnicodeString) dataItem).getString();
- // TODO: escape ' in |value|
- sb.append("'" + value + "'");
- }
- break;
- case ARRAY: {
- // Major type 4: an array of data items.
- List<DataItem> items = ((co.nstant.in.cbor.model.Array) dataItem).getDataItems();
- if (items.size() == 0) {
- sb.append("[]");
- } else if (cborAreAllDataItemsNonCompound(items)) {
- // The case where everything fits on one line.
- sb.append("[");
- int count = 0;
- for (DataItem item : items) {
- cborPrettyPrintDataItem(sb, indent, item);
- if (++count < items.size()) {
- sb.append(", ");
- }
- }
- sb.append("]");
- } else {
- sb.append("[\n" + indentString);
- int count = 0;
- for (DataItem item : items) {
- sb.append(" ");
- cborPrettyPrintDataItem(sb, indent + 2, item);
- if (++count < items.size()) {
- sb.append(",");
- }
- sb.append("\n" + indentString);
- }
- sb.append("]");
- }
- }
- break;
- case MAP: {
- // Major type 5: a map of pairs of data items.
- Collection<DataItem> keys = ((co.nstant.in.cbor.model.Map) dataItem).getKeys();
- if (keys.size() == 0) {
- sb.append("{}");
- } else {
- sb.append("{\n" + indentString);
- int count = 0;
- for (DataItem key : keys) {
- sb.append(" ");
- DataItem value = ((co.nstant.in.cbor.model.Map) dataItem).get(key);
- cborPrettyPrintDataItem(sb, indent + 2, key);
- sb.append(" : ");
- cborPrettyPrintDataItem(sb, indent + 2, value);
- if (++count < keys.size()) {
- sb.append(",");
- }
- sb.append("\n" + indentString);
- }
- sb.append("}");
- }
- }
- break;
- case TAG:
- // Major type 6: optional semantic tagging of other major types
- //
- // We never encounter this one since it's automatically handled via the
- // DataItem that is tagged.
- throw new RuntimeException("Semantic tag data item not expected");
-
- case SPECIAL:
- // Major type 7: floating point numbers and simple data types that need no
- // content, as well as the "break" stop code.
- if (dataItem instanceof SimpleValue) {
- switch (((SimpleValue) dataItem).getSimpleValueType()) {
- case FALSE:
- sb.append("false");
- break;
- case TRUE:
- sb.append("true");
- break;
- case NULL:
- sb.append("null");
- break;
- case UNDEFINED:
- sb.append("undefined");
- break;
- case RESERVED:
- sb.append("reserved");
- break;
- case UNALLOCATED:
- sb.append("unallocated");
- break;
- }
- } else if (dataItem instanceof DoublePrecisionFloat) {
- DecimalFormat df = new DecimalFormat("0",
- DecimalFormatSymbols.getInstance(Locale.ENGLISH));
- df.setMaximumFractionDigits(340);
- sb.append(df.format(((DoublePrecisionFloat) dataItem).getValue()));
- } else if (dataItem instanceof AbstractFloat) {
- DecimalFormat df = new DecimalFormat("0",
- DecimalFormatSymbols.getInstance(Locale.ENGLISH));
- df.setMaximumFractionDigits(340);
- sb.append(df.format(((AbstractFloat) dataItem).getValue()));
- } else {
- sb.append("break");
- }
- break;
- }
- }
-
- public static byte[] encodeCbor(List<DataItem> dataItems) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- CborEncoder encoder = new CborEncoder(baos);
- try {
- encoder.encode(dataItems);
- } catch (CborException e) {
- throw new RuntimeException("Error encoding data", e);
- }
- return baos.toByteArray();
- }
-
- public static byte[] coseBuildToBeSigned(byte[] encodedProtectedHeaders,
- byte[] payload,
- byte[] detachedContent) {
- CborBuilder sigStructure = new CborBuilder();
- ArrayBuilder<CborBuilder> array = sigStructure.addArray();
-
- array.add("Signature1");
- array.add(encodedProtectedHeaders);
-
- // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
- // so external_aad is the empty bstr
- byte emptyExternalAad[] = new byte[0];
- array.add(emptyExternalAad);
-
- // Next field is the payload, independently of how it's transported (RFC
- // 8152 section 4.4). Since our API specifies only one of |data| and
- // |detachedContent| can be non-empty, it's simply just the non-empty one.
- if (payload != null && payload.length > 0) {
- array.add(payload);
- } else {
- array.add(detachedContent);
- }
- array.end();
- return encodeCbor(sigStructure.build());
- }
-
- private static final int COSE_LABEL_ALG = 1;
- private static final int COSE_LABEL_X5CHAIN = 33; // temporary identifier
-
- // From "COSE Algorithms" registry
- private static final int COSE_ALG_ECDSA_256 = -7;
- private static final int COSE_ALG_HMAC_256_256 = 5;
-
- private static byte[] signatureDerToCose(byte[] signature) {
- if (signature.length > 128) {
- throw new RuntimeException("Unexpected length " + signature.length
- + ", expected less than 128");
- }
- if (signature[0] != 0x30) {
- throw new RuntimeException("Unexpected first byte " + signature[0]
- + ", expected 0x30");
- }
- if ((signature[1] & 0x80) != 0x00) {
- throw new RuntimeException("Unexpected second byte " + signature[1]
- + ", bit 7 shouldn't be set");
- }
- int rOffset = 2;
- int rSize = signature[rOffset + 1];
- byte[] rBytes = stripLeadingZeroes(
- Arrays.copyOfRange(signature,rOffset + 2, rOffset + rSize + 2));
-
- int sOffset = rOffset + 2 + rSize;
- int sSize = signature[sOffset + 1];
- byte[] sBytes = stripLeadingZeroes(
- Arrays.copyOfRange(signature, sOffset + 2, sOffset + sSize + 2));
-
- if (rBytes.length > 32) {
- throw new RuntimeException("rBytes.length is " + rBytes.length + " which is > 32");
- }
- if (sBytes.length > 32) {
- throw new RuntimeException("sBytes.length is " + sBytes.length + " which is > 32");
- }
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- for (int n = 0; n < 32 - rBytes.length; n++) {
- baos.write(0x00);
- }
- baos.write(rBytes);
- for (int n = 0; n < 32 - sBytes.length; n++) {
- baos.write(0x00);
- }
- baos.write(sBytes);
- } catch (IOException e) {
- e.printStackTrace();
- return null;
- }
- return baos.toByteArray();
- }
-
- // Adds leading 0x00 if the first encoded byte MSB is set.
- private static byte[] encodePositiveBigInteger(BigInteger i) {
- byte[] bytes = i.toByteArray();
- if ((bytes[0] & 0x80) != 0) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- baos.write(0x00);
- baos.write(bytes);
- } catch (IOException e) {
- e.printStackTrace();
- throw new RuntimeException("Failed writing data", e);
- }
- bytes = baos.toByteArray();
- }
- return bytes;
- }
-
- private static byte[] signatureCoseToDer(byte[] signature) {
- if (signature.length != 64) {
- throw new RuntimeException("signature.length is " + signature.length + ", expected 64");
- }
- // r and s are always positive and may use all 256 bits so use the constructor which
- // parses them as unsigned.
- BigInteger r = new BigInteger(1, Arrays.copyOfRange(signature, 0, 32));
- BigInteger s = new BigInteger(1, Arrays.copyOfRange(signature, 32, 64));
- byte[] rBytes = encodePositiveBigInteger(r);
- byte[] sBytes = encodePositiveBigInteger(s);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- baos.write(0x30);
- baos.write(2 + rBytes.length + 2 + sBytes.length);
- baos.write(0x02);
- baos.write(rBytes.length);
- baos.write(rBytes);
- baos.write(0x02);
- baos.write(sBytes.length);
- baos.write(sBytes);
- } catch (IOException e) {
- e.printStackTrace();
- return null;
- }
- return baos.toByteArray();
- }
-
- public static byte[] coseSign1Sign(PrivateKey key,
- @Nullable byte[] data,
- byte[] detachedContent,
- @Nullable Collection<X509Certificate> certificateChain)
- throws NoSuchAlgorithmException, InvalidKeyException, CertificateEncodingException {
-
- int dataLen = (data != null ? data.length : 0);
- int detachedContentLen = (detachedContent != null ? detachedContent.length : 0);
- if (dataLen > 0 && detachedContentLen > 0) {
- throw new RuntimeException("data and detachedContent cannot both be non-empty");
- }
-
- CborBuilder protectedHeaders = new CborBuilder();
- MapBuilder<CborBuilder> protectedHeadersMap = protectedHeaders.addMap();
- protectedHeadersMap.put(COSE_LABEL_ALG, COSE_ALG_ECDSA_256);
- byte[] protectedHeadersBytes = encodeCbor(protectedHeaders.build());
-
- byte[] toBeSigned = coseBuildToBeSigned(protectedHeadersBytes, data, detachedContent);
-
- byte[] coseSignature = null;
- try {
- Signature s = Signature.getInstance("SHA256withECDSA");
- s.initSign(key);
- s.update(toBeSigned);
- byte[] derSignature = s.sign();
- coseSignature = signatureDerToCose(derSignature);
- } catch (SignatureException e) {
- throw new RuntimeException("Error signing data");
- }
-
- CborBuilder builder = new CborBuilder();
- ArrayBuilder<CborBuilder> array = builder.addArray();
- array.add(protectedHeadersBytes);
- MapBuilder<ArrayBuilder<CborBuilder>> unprotectedHeaders = array.addMap();
- if (certificateChain != null && certificateChain.size() > 0) {
- if (certificateChain.size() == 1) {
- X509Certificate cert = certificateChain.iterator().next();
- unprotectedHeaders.put(COSE_LABEL_X5CHAIN, cert.getEncoded());
- } else {
- ArrayBuilder<MapBuilder<ArrayBuilder<CborBuilder>>> x5chainsArray =
- unprotectedHeaders.putArray(COSE_LABEL_X5CHAIN);
- for (X509Certificate cert : certificateChain) {
- x5chainsArray.add(cert.getEncoded());
- }
- }
- }
- if (data == null || data.length == 0) {
- array.add(new SimpleValue(SimpleValueType.NULL));
- } else {
- array.add(data);
- }
- array.add(coseSignature);
-
- return encodeCbor(builder.build());
- }
-
- public static boolean coseSign1CheckSignature(byte[] signatureCose1,
- byte[] detachedContent,
- PublicKey publicKey) throws NoSuchAlgorithmException, InvalidKeyException {
- ByteArrayInputStream bais = new ByteArrayInputStream(signatureCose1);
- List<DataItem> dataItems = null;
- try {
- dataItems = new CborDecoder(bais).decode();
- } catch (CborException e) {
- throw new RuntimeException("Given signature is not valid CBOR", e);
- }
- if (dataItems.size() != 1) {
- throw new RuntimeException("Expected just one data item");
- }
- DataItem dataItem = dataItems.get(0);
- if (dataItem.getMajorType() != MajorType.ARRAY) {
- throw new RuntimeException("Data item is not an array");
- }
- List<DataItem> items = ((co.nstant.in.cbor.model.Array) dataItem).getDataItems();
- if (items.size() < 4) {
- throw new RuntimeException("Expected at least four items in COSE_Sign1 array");
- }
- if (items.get(0).getMajorType() != MajorType.BYTE_STRING) {
- throw new RuntimeException("Item 0 (protected headers) is not a byte-string");
- }
- byte[] encodedProtectedHeaders =
- ((co.nstant.in.cbor.model.ByteString) items.get(0)).getBytes();
- byte[] payload = new byte[0];
- if (items.get(2).getMajorType() == MajorType.SPECIAL) {
- if (((co.nstant.in.cbor.model.Special) items.get(2)).getSpecialType()
- != SpecialType.SIMPLE_VALUE) {
- throw new RuntimeException("Item 2 (payload) is a special but not a simple value");
- }
- SimpleValue simple = (co.nstant.in.cbor.model.SimpleValue) items.get(2);
- if (simple.getSimpleValueType() != SimpleValueType.NULL) {
- throw new RuntimeException("Item 2 (payload) is a simple but not the value null");
- }
- } else if (items.get(2).getMajorType() == MajorType.BYTE_STRING) {
- payload = ((co.nstant.in.cbor.model.ByteString) items.get(2)).getBytes();
- } else {
- throw new RuntimeException("Item 2 (payload) is not nil or byte-string");
- }
- if (items.get(3).getMajorType() != MajorType.BYTE_STRING) {
- throw new RuntimeException("Item 3 (signature) is not a byte-string");
- }
- byte[] coseSignature = ((co.nstant.in.cbor.model.ByteString) items.get(3)).getBytes();
-
- byte[] derSignature = signatureCoseToDer(coseSignature);
-
- int dataLen = payload.length;
- int detachedContentLen = (detachedContent != null ? detachedContent.length : 0);
- if (dataLen > 0 && detachedContentLen > 0) {
- throw new RuntimeException("data and detachedContent cannot both be non-empty");
- }
-
- byte[] toBeSigned = Util.coseBuildToBeSigned(encodedProtectedHeaders,
- payload, detachedContent);
-
- try {
- Signature verifier = Signature.getInstance("SHA256withECDSA");
- verifier.initVerify(publicKey);
- verifier.update(toBeSigned);
- return verifier.verify(derSignature);
- } catch (SignatureException e) {
- throw new RuntimeException("Error verifying signature");
- }
- }
-
- // Returns the empty byte-array if no data is included in the structure.
- //
- // Throws RuntimeException if the given bytes aren't valid COSE_Sign1.
- //
- public static byte[] coseSign1GetData(byte[] signatureCose1) {
- ByteArrayInputStream bais = new ByteArrayInputStream(signatureCose1);
- List<DataItem> dataItems = null;
- try {
- dataItems = new CborDecoder(bais).decode();
- } catch (CborException e) {
- throw new RuntimeException("Given signature is not valid CBOR", e);
- }
- if (dataItems.size() != 1) {
- throw new RuntimeException("Expected just one data item");
- }
- DataItem dataItem = dataItems.get(0);
- if (dataItem.getMajorType() != MajorType.ARRAY) {
- throw new RuntimeException("Data item is not an array");
- }
- List<DataItem> items = ((co.nstant.in.cbor.model.Array) dataItem).getDataItems();
- if (items.size() < 4) {
- throw new RuntimeException("Expected at least four items in COSE_Sign1 array");
- }
- byte[] payload = new byte[0];
- if (items.get(2).getMajorType() == MajorType.SPECIAL) {
- if (((co.nstant.in.cbor.model.Special) items.get(2)).getSpecialType()
- != SpecialType.SIMPLE_VALUE) {
- throw new RuntimeException("Item 2 (payload) is a special but not a simple value");
- }
- SimpleValue simple = (co.nstant.in.cbor.model.SimpleValue) items.get(2);
- if (simple.getSimpleValueType() != SimpleValueType.NULL) {
- throw new RuntimeException("Item 2 (payload) is a simple but not the value null");
- }
- } else if (items.get(2).getMajorType() == MajorType.BYTE_STRING) {
- payload = ((co.nstant.in.cbor.model.ByteString) items.get(2)).getBytes();
- } else {
- throw new RuntimeException("Item 2 (payload) is not nil or byte-string");
- }
- return payload;
- }
-
- // Returns the empty collection if no x5chain is included in the structure.
- //
- // Throws RuntimeException if the given bytes aren't valid COSE_Sign1.
- //
- public static Collection<X509Certificate> coseSign1GetX5Chain(byte[] signatureCose1)
- throws CertificateException {
- ArrayList<X509Certificate> ret = new ArrayList<>();
- ByteArrayInputStream bais = new ByteArrayInputStream(signatureCose1);
- List<DataItem> dataItems = null;
- try {
- dataItems = new CborDecoder(bais).decode();
- } catch (CborException e) {
- throw new RuntimeException("Given signature is not valid CBOR", e);
- }
- if (dataItems.size() != 1) {
- throw new RuntimeException("Expected just one data item");
- }
- DataItem dataItem = dataItems.get(0);
- if (dataItem.getMajorType() != MajorType.ARRAY) {
- throw new RuntimeException("Data item is not an array");
- }
- List<DataItem> items = ((co.nstant.in.cbor.model.Array) dataItem).getDataItems();
- if (items.size() < 4) {
- throw new RuntimeException("Expected at least four items in COSE_Sign1 array");
- }
- if (items.get(1).getMajorType() != MajorType.MAP) {
- throw new RuntimeException("Item 1 (unprocted headers) is not a map");
- }
- co.nstant.in.cbor.model.Map map = (co.nstant.in.cbor.model.Map) items.get(1);
- DataItem x5chainItem = map.get(new UnsignedInteger(COSE_LABEL_X5CHAIN));
- if (x5chainItem != null) {
- CertificateFactory factory = CertificateFactory.getInstance("X.509");
- if (x5chainItem instanceof ByteString) {
- ByteArrayInputStream certBais =
- new ByteArrayInputStream(((ByteString) x5chainItem).getBytes());
- ret.add((X509Certificate) factory.generateCertificate(certBais));
- } else if (x5chainItem instanceof Array) {
- for (DataItem certItem : ((Array) x5chainItem).getDataItems()) {
- if (!(certItem instanceof ByteString)) {
- throw new RuntimeException(
- "Unexpected type for array item in x5chain value");
- }
- ByteArrayInputStream certBais =
- new ByteArrayInputStream(((ByteString) certItem).getBytes());
- ret.add((X509Certificate) factory.generateCertificate(certBais));
- }
- } else {
- throw new RuntimeException("Unexpected type for x5chain value");
- }
- }
- return ret;
- }
-
- public static byte[] coseBuildToBeMACed(byte[] encodedProtectedHeaders,
- byte[] payload,
- byte[] detachedContent) {
- CborBuilder macStructure = new CborBuilder();
- ArrayBuilder<CborBuilder> array = macStructure.addArray();
-
- array.add("MAC0");
- array.add(encodedProtectedHeaders);
-
- // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
- // so external_aad is the empty bstr
- byte emptyExternalAad[] = new byte[0];
- array.add(emptyExternalAad);
-
- // Next field is the payload, independently of how it's transported (RFC
- // 8152 section 4.4). Since our API specifies only one of |data| and
- // |detachedContent| can be non-empty, it's simply just the non-empty one.
- if (payload != null && payload.length > 0) {
- array.add(payload);
- } else {
- array.add(detachedContent);
- }
-
- return encodeCbor(macStructure.build());
- }
-
- public static byte[] coseMac0(SecretKey key,
- @Nullable byte[] data,
- byte[] detachedContent)
- throws NoSuchAlgorithmException, InvalidKeyException, CertificateEncodingException {
-
- int dataLen = (data != null ? data.length : 0);
- int detachedContentLen = (detachedContent != null ? detachedContent.length : 0);
- if (dataLen > 0 && detachedContentLen > 0) {
- throw new RuntimeException("data and detachedContent cannot both be non-empty");
- }
-
- CborBuilder protectedHeaders = new CborBuilder();
- MapBuilder<CborBuilder> protectedHeadersMap = protectedHeaders.addMap();
- protectedHeadersMap.put(COSE_LABEL_ALG, COSE_ALG_HMAC_256_256);
- byte[] protectedHeadersBytes = encodeCbor(protectedHeaders.build());
-
- byte[] toBeMACed = coseBuildToBeMACed(protectedHeadersBytes, data, detachedContent);
-
- byte[] mac = null;
- Mac m = Mac.getInstance("HmacSHA256");
- m.init(key);
- m.update(toBeMACed);
- mac = m.doFinal();
-
- CborBuilder builder = new CborBuilder();
- ArrayBuilder<CborBuilder> array = builder.addArray();
- array.add(protectedHeadersBytes);
- MapBuilder<ArrayBuilder<CborBuilder>> unprotectedHeaders = array.addMap();
- if (data == null || data.length == 0) {
- array.add(new SimpleValue(SimpleValueType.NULL));
- } else {
- array.add(data);
- }
- array.add(mac);
-
- return encodeCbor(builder.build());
- }
-
- public static String replaceLine(String text, int lineNumber, String replacementLine) {
- String[] lines = text.split("\n");
- int numLines = lines.length;
- if (lineNumber < 0) {
- lineNumber = numLines - (-lineNumber);
- }
- StringBuilder sb = new StringBuilder();
- for (int n = 0; n < numLines; n++) {
- if (n == lineNumber) {
- sb.append(replacementLine);
- } else {
- sb.append(lines[n]);
- }
- // Only add terminating newline if passed-in string ends in a newline.
- if (n == numLines - 1) {
- if (text.endsWith(("\n"))) {
- sb.append('\n');
- }
- } else {
- sb.append('\n');
- }
- }
- return sb.toString();
- }
-
- public static byte[] cborEncode(DataItem dataItem) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- new CborEncoder(baos).encode(dataItem);
- } catch (CborException e) {
- // This should never happen and we don't want cborEncode() to throw since that
- // would complicate all callers. Log it instead.
- e.printStackTrace();
- Log.e(TAG, "Error encoding DataItem");
- }
- return baos.toByteArray();
- }
-
- public static byte[] cborEncodeBoolean(boolean value) {
- return cborEncode(new CborBuilder().add(value).build().get(0));
- }
-
- public static byte[] cborEncodeString(@NonNull String value) {
- return cborEncode(new CborBuilder().add(value).build().get(0));
- }
-
- public static byte[] cborEncodeBytestring(@NonNull byte[] value) {
- return cborEncode(new CborBuilder().add(value).build().get(0));
- }
-
- public static byte[] cborEncodeInt(long value) {
- return cborEncode(new CborBuilder().add(value).build().get(0));
- }
-
- static final int CBOR_SEMANTIC_TAG_ENCODED_CBOR = 24;
-
- public static DataItem cborToDataItem(byte[] data) {
- ByteArrayInputStream bais = new ByteArrayInputStream(data);
- try {
- List<DataItem> dataItems = new CborDecoder(bais).decode();
- if (dataItems.size() != 1) {
- throw new RuntimeException("Expected 1 item, found " + dataItems.size());
- }
- return dataItems.get(0);
- } catch (CborException e) {
- throw new RuntimeException("Error decoding data", e);
- }
- }
-
- public static boolean cborDecodeBoolean(@NonNull byte[] data) {
- return cborToDataItem(data) == SimpleValue.TRUE;
- }
-
- public static String cborDecodeString(@NonNull byte[] data) {
- return ((co.nstant.in.cbor.model.UnicodeString) cborToDataItem(data)).getString();
- }
-
- public static long cborDecodeInt(@NonNull byte[] data) {
- return ((co.nstant.in.cbor.model.Number) cborToDataItem(data)).getValue().longValue();
- }
-
- public static byte[] cborDecodeBytestring(@NonNull byte[] data) {
- return ((co.nstant.in.cbor.model.ByteString) cborToDataItem(data)).getBytes();
- }
-
- public static String getStringEntry(ResultData data, String namespaceName, String name) {
- return Util.cborDecodeString(data.getEntry(namespaceName, name));
- }
-
- public static boolean getBooleanEntry(ResultData data, String namespaceName, String name) {
- return Util.cborDecodeBoolean(data.getEntry(namespaceName, name));
- }
-
- public static long getIntegerEntry(ResultData data, String namespaceName, String name) {
- return Util.cborDecodeInt(data.getEntry(namespaceName, name));
- }
-
- public static byte[] getBytestringEntry(ResultData data, String namespaceName, String name) {
- return Util.cborDecodeBytestring(data.getEntry(namespaceName, name));
- }
-
- /*
-Certificate:
- Data:
- Version: 3 (0x2)
- Serial Number: 1 (0x1)
- Signature Algorithm: ecdsa-with-SHA256
- Issuer: CN=fake
- Validity
- Not Before: Jan 1 00:00:00 1970 GMT
- Not After : Jan 1 00:00:00 2048 GMT
- Subject: CN=fake
- Subject Public Key Info:
- Public Key Algorithm: id-ecPublicKey
- Public-Key: (256 bit)
- 00000000 04 9b 60 70 8a 99 b6 bf e3 b8 17 02 9e 93 eb 48 |..`p...........H|
- 00000010 23 b9 39 89 d1 00 bf a0 0f d0 2f bd 6b 11 bc d1 |#.9......./.k...|
- 00000020 19 53 54 28 31 00 f5 49 db 31 fb 9f 7d 99 bf 23 |.ST(1..I.1..}..#|
- 00000030 fb 92 04 6b 23 63 55 98 ad 24 d2 68 c4 83 bf 99 |...k#cU..$.h....|
- 00000040 62 |b|
- Signature Algorithm: ecdsa-with-SHA256
- 30:45:02:20:67:ad:d1:34:ed:a5:68:3f:5b:33:ee:b3:18:a2:
- eb:03:61:74:0f:21:64:4a:a3:2e:82:b3:92:5c:21:0f:88:3f:
- 02:21:00:b7:38:5c:9b:f2:9c:b1:27:86:37:44:df:eb:4a:b2:
- 6c:11:9a:c1:ff:b2:80:95:ce:fc:5f:26:b4:20:6e:9b:0d
- */
-
-
- public static @NonNull X509Certificate signPublicKeyWithPrivateKey(String keyToSignAlias,
- String keyToSignWithAlias) {
-
- KeyStore ks = null;
- try {
- ks = KeyStore.getInstance("AndroidKeyStore");
- ks.load(null);
-
- /* First note that KeyStore.getCertificate() returns a self-signed X.509 certificate
- * for the key in question. As per RFC 5280, section 4.1 an X.509 certificate has the
- * following structure:
- *
- * Certificate ::= SEQUENCE {
- * tbsCertificate TBSCertificate,
- * signatureAlgorithm AlgorithmIdentifier,
- * signatureValue BIT STRING }
- *
- * Conveniently, the X509Certificate class has a getTBSCertificate() method which
- * returns the tbsCertificate blob. So all we need to do is just sign that and build
- * signatureAlgorithm and signatureValue and combine it with tbsCertificate. We don't
- * need a full-blown ASN.1/DER encoder to do this.
- */
- X509Certificate selfSignedCert = (X509Certificate) ks.getCertificate(keyToSignAlias);
- byte[] tbsCertificate = selfSignedCert.getTBSCertificate();
-
- KeyStore.Entry keyToSignWithEntry = ks.getEntry(keyToSignWithAlias, null);
- Signature s = Signature.getInstance("SHA256withECDSA");
- s.initSign(((KeyStore.PrivateKeyEntry) keyToSignWithEntry).getPrivateKey());
- s.update(tbsCertificate);
- byte[] signatureValue = s.sign();
-
- /* The DER encoding for a SEQUENCE of length 128-65536 - the length is updated below.
- *
- * We assume - and test for below - that the final length is always going to be in
- * this range. This is a sound assumption given we're using 256-bit EC keys.
- */
- byte[] sequence = new byte[]{
- 0x30, (byte) 0x82, 0x00, 0x00
- };
-
- /* The DER encoding for the ECDSA with SHA-256 signature algorithm:
- *
- * SEQUENCE (1 elem)
- * OBJECT IDENTIFIER 1.2.840.10045.4.3.2 ecdsaWithSHA256 (ANSI X9.62 ECDSA
- * algorithm with SHA256)
- */
- byte[] signatureAlgorithm = new byte[]{
- 0x30, 0x0a, 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, 0x3d, 0x04, 0x03,
- 0x02
- };
-
- /* The DER encoding for a BIT STRING with one element - the length is updated below.
- *
- * We assume the length of signatureValue is always going to be less than 128. This
- * assumption works since we know ecdsaWithSHA256 signatures are always 69, 70, or
- * 71 bytes long when DER encoded.
- */
- byte[] bitStringForSignature = new byte[]{0x03, 0x00, 0x00};
-
- // Calculate sequence length and set it in |sequence|.
- int sequenceLength = tbsCertificate.length
- + signatureAlgorithm.length
- + bitStringForSignature.length
- + signatureValue.length;
- if (sequenceLength < 128 || sequenceLength > 65535) {
- throw new Exception("Unexpected sequenceLength " + sequenceLength);
- }
- sequence[2] = (byte) (sequenceLength >> 8);
- sequence[3] = (byte) (sequenceLength & 0xff);
-
- // Calculate signatureValue length and set it in |bitStringForSignature|.
- int signatureValueLength = signatureValue.length + 1;
- if (signatureValueLength >= 128) {
- throw new Exception("Unexpected signatureValueLength " + signatureValueLength);
- }
- bitStringForSignature[1] = (byte) signatureValueLength;
-
- // Finally concatenate everything together.
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- baos.write(sequence);
- baos.write(tbsCertificate);
- baos.write(signatureAlgorithm);
- baos.write(bitStringForSignature);
- baos.write(signatureValue);
- byte[] resultingCertBytes = baos.toByteArray();
-
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- ByteArrayInputStream bais = new ByteArrayInputStream(resultingCertBytes);
- X509Certificate result = (X509Certificate) cf.generateCertificate(bais);
- return result;
- } catch (Exception e) {
- throw new RuntimeException("Error signing public key with private key", e);
- }
- }
-
- public static byte[] buildDeviceAuthenticationCbor(String docType,
- byte[] encodedSessionTranscript,
- byte[] deviceNameSpacesBytes) {
- ByteArrayOutputStream daBaos = new ByteArrayOutputStream();
- try {
- ByteArrayInputStream bais = new ByteArrayInputStream(encodedSessionTranscript);
- List<DataItem> dataItems = null;
- dataItems = new CborDecoder(bais).decode();
- DataItem sessionTranscript = dataItems.get(0);
- ByteString deviceNameSpacesBytesItem = new ByteString(deviceNameSpacesBytes);
- deviceNameSpacesBytesItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
- new CborEncoder(daBaos).encode(new CborBuilder()
- .addArray()
- .add("DeviceAuthentication")
- .add(sessionTranscript)
- .add(docType)
- .add(deviceNameSpacesBytesItem)
- .end()
- .build());
- } catch (CborException e) {
- throw new RuntimeException("Error encoding DeviceAuthentication", e);
- }
- return daBaos.toByteArray();
- }
-
- public static byte[] buildReaderAuthenticationBytesCbor(
- byte[] encodedSessionTranscript,
- byte[] requestMessageBytes) {
-
- ByteArrayOutputStream daBaos = new ByteArrayOutputStream();
- try {
- ByteArrayInputStream bais = new ByteArrayInputStream(encodedSessionTranscript);
- List<DataItem> dataItems = null;
- dataItems = new CborDecoder(bais).decode();
- DataItem sessionTranscript = dataItems.get(0);
- ByteString requestMessageBytesItem = new ByteString(requestMessageBytes);
- requestMessageBytesItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
- new CborEncoder(daBaos).encode(new CborBuilder()
- .addArray()
- .add("ReaderAuthentication")
- .add(sessionTranscript)
- .add(requestMessageBytesItem)
- .end()
- .build());
- } catch (CborException e) {
- throw new RuntimeException("Error encoding ReaderAuthentication", e);
- }
- byte[] readerAuthentication = daBaos.toByteArray();
- return Util.prependSemanticTagForEncodedCbor(readerAuthentication);
- }
-
- // Returns #6.24(bstr) of the given already encoded CBOR
- //
- public static @NonNull DataItem buildCborTaggedByteString(@NonNull byte[] encodedCbor) {
- DataItem item = new ByteString(encodedCbor);
- item.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
- return item;
- }
-
- public static byte[] prependSemanticTagForEncodedCbor(byte[] encodedCbor) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- new CborEncoder(baos).encode(buildCborTaggedByteString(encodedCbor));
- } catch (CborException e) {
- throw new RuntimeException("Error encoding with semantic tag for CBOR encoding", e);
- }
- return baos.toByteArray();
- }
-
- public static byte[] concatArrays(byte[] a, byte[] b) {
- byte[] ret = new byte[a.length + b.length];
- System.arraycopy(a, 0, ret, 0, a.length);
- System.arraycopy(b, 0, ret, a.length, b.length);
- return ret;
- }
-
- public static SecretKey calcEMacKeyForReader(PublicKey authenticationPublicKey,
- PrivateKey ephemeralReaderPrivateKey,
- byte[] encodedSessionTranscript) {
- try {
- KeyAgreement ka = KeyAgreement.getInstance("ECDH");
- ka.init(ephemeralReaderPrivateKey);
- ka.doPhase(authenticationPublicKey, true);
- byte[] sharedSecret = ka.generateSecret();
-
- byte[] sessionTranscriptBytes =
- Util.prependSemanticTagForEncodedCbor(encodedSessionTranscript);
-
- byte[] salt = MessageDigest.getInstance("SHA-256").digest(sessionTranscriptBytes);
- byte[] info = new byte[] {'E', 'M', 'a', 'c', 'K', 'e', 'y'};
- byte[] derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
- SecretKey secretKey = new SecretKeySpec(derivedKey, "");
- return secretKey;
- } catch (InvalidKeyException
- | NoSuchAlgorithmException e) {
- throw new RuntimeException("Error performing key agreement", e);
- }
- }
-
- /**
- * Computes an HKDF.
- *
- * This is based on https://github.com/google/tink/blob/master/java/src/main/java/com/google
- * /crypto/tink/subtle/Hkdf.java
- * which is also Copyright (c) Google and also licensed under the Apache 2 license.
- *
- * @param macAlgorithm the MAC algorithm used for computing the Hkdf. I.e., "HMACSHA1" or
- * "HMACSHA256".
- * @param ikm the input keying material.
- * @param salt optional salt. A possibly non-secret random value. If no salt is
- * provided (i.e. if
- * salt has length 0) then an array of 0s of the same size as the hash
- * digest is used as salt.
- * @param info optional context and application specific information.
- * @param size The length of the generated pseudorandom string in bytes. The maximal
- * size is
- * 255.DigestSize, where DigestSize is the size of the underlying HMAC.
- * @return size pseudorandom bytes.
- */
- public static byte[] computeHkdf(
- String macAlgorithm, final byte[] ikm, final byte[] salt, final byte[] info, int size) {
- Mac mac = null;
- try {
- mac = Mac.getInstance(macAlgorithm);
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("No such algorithm: " + macAlgorithm, e);
- }
- if (size > 255 * mac.getMacLength()) {
- throw new RuntimeException("size too large");
- }
- try {
- if (salt == null || salt.length == 0) {
- // According to RFC 5869, Section 2.2 the salt is optional. If no salt is provided
- // then HKDF uses a salt that is an array of zeros of the same length as the hash
- // digest.
- mac.init(new SecretKeySpec(new byte[mac.getMacLength()], macAlgorithm));
- } else {
- mac.init(new SecretKeySpec(salt, macAlgorithm));
- }
- byte[] prk = mac.doFinal(ikm);
- byte[] result = new byte[size];
- int ctr = 1;
- int pos = 0;
- mac.init(new SecretKeySpec(prk, macAlgorithm));
- byte[] digest = new byte[0];
- while (true) {
- mac.update(digest);
- mac.update(info);
- mac.update((byte) ctr);
- digest = mac.doFinal();
- if (pos + digest.length < size) {
- System.arraycopy(digest, 0, result, pos, digest.length);
- pos += digest.length;
- ctr++;
- } else {
- System.arraycopy(digest, 0, result, pos, size - pos);
- break;
- }
- }
- return result;
- } catch (InvalidKeyException e) {
- throw new RuntimeException("Error MACing", e);
- }
- }
-
- static byte[] stripLeadingZeroes(byte[] value) {
- int n = 0;
- while (n < value.length && value[n] == 0) {
- n++;
- }
- int newLen = value.length - n;
- byte[] ret = new byte[newLen];
- int m = 0;
- while (n < value.length) {
- ret[m++] = value[n++];
- }
- return ret;
- }
-
- public static void hexdump(String name, byte[] data) {
- int n, m, o;
- StringBuilder sb = new StringBuilder();
- Formatter fmt = new Formatter(sb);
- for (n = 0; n < data.length; n += 16) {
- fmt.format("%04x ", n);
- for (m = 0; m < 16 && n + m < data.length; m++) {
- fmt.format("%02x ", data[n + m]);
- }
- for (o = m; o < 16; o++) {
- sb.append(" ");
- }
- sb.append(" ");
- for (m = 0; m < 16 && n + m < data.length; m++) {
- int c = data[n + m] & 0xff;
- fmt.format("%c", Character.isISOControl(c) ? '.' : c);
- }
- sb.append("\n");
- }
- sb.append("\n");
- Log.e(TAG, name + ": dumping " + data.length + " bytes\n" + fmt.toString());
- }
-
-
- // This returns a SessionTranscript which satisfy the requirement
- // that the uncompressed X and Y coordinates of the public key for the
- // mDL's ephemeral key-pair appear somewhere in the encoded
- // DeviceEngagement.
- public static byte[] buildSessionTranscript(KeyPair ephemeralKeyPair) {
- // Make the coordinates appear in an already encoded bstr - this
- // mimics how the mDL COSE_Key appear as encoded data inside the
- // encoded DeviceEngagement
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
- // X and Y are always positive so for interop we remove any leading zeroes
- // inserted by the BigInteger encoder.
- byte[] x = stripLeadingZeroes(w.getAffineX().toByteArray());
- byte[] y = stripLeadingZeroes(w.getAffineY().toByteArray());
- baos.write(new byte[]{42});
- baos.write(x);
- baos.write(y);
- baos.write(new byte[]{43, 44});
- } catch (IOException e) {
- e.printStackTrace();
- return null;
- }
- byte[] blobWithCoords = baos.toByteArray();
-
- baos = new ByteArrayOutputStream();
- try {
- new CborEncoder(baos).encode(new CborBuilder()
- .addArray()
- .add(blobWithCoords)
- .end()
- .build());
- } catch (CborException e) {
- e.printStackTrace();
- return null;
- }
- ByteString encodedDeviceEngagementItem = new ByteString(baos.toByteArray());
- ByteString encodedEReaderKeyItem = new ByteString(cborEncodeString("doesn't matter"));
- encodedDeviceEngagementItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
- encodedEReaderKeyItem.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
-
- baos = new ByteArrayOutputStream();
- try {
- new CborEncoder(baos).encode(new CborBuilder()
- .addArray()
- .add(encodedDeviceEngagementItem)
- .add(encodedEReaderKeyItem)
- .end()
- .build());
- } catch (CborException e) {
- e.printStackTrace();
- return null;
- }
- return baos.toByteArray();
- }
-
- /*
- * Helper function to create a CBOR data for requesting data items. The IntentToRetain
- * value will be set to false for all elements.
- *
- * <p>The returned CBOR data conforms to the following CDDL schema:</p>
- *
- * <pre>
- * ItemsRequest = {
- * ? "docType" : DocType,
- * "nameSpaces" : NameSpaces,
- * ? "RequestInfo" : {* tstr => any} ; Additional info the reader wants to provide
- * }
- *
- * NameSpaces = {
- * + NameSpace => DataElements ; Requested data elements for each NameSpace
- * }
- *
- * DataElements = {
- * + DataElement => IntentToRetain
- * }
- *
- * DocType = tstr
- *
- * DataElement = tstr
- * IntentToRetain = bool
- * NameSpace = tstr
- * </pre>
- *
- * @param entriesToRequest The entries to request, organized as a map of namespace
- * names with each value being a collection of data elements
- * in the given namespace.
- * @param docType The document type or {@code null} if there is no document
- * type.
- * @return CBOR data conforming to the CDDL mentioned above.
- */
- public static @NonNull byte[] createItemsRequest(
- @NonNull Map<String, Collection<String>> entriesToRequest,
- @Nullable String docType) {
- CborBuilder builder = new CborBuilder();
- MapBuilder<CborBuilder> mapBuilder = builder.addMap();
- if (docType != null) {
- mapBuilder.put("docType", docType);
- }
-
- MapBuilder<MapBuilder<CborBuilder>> nsMapBuilder = mapBuilder.putMap("nameSpaces");
- for (String namespaceName : entriesToRequest.keySet()) {
- Collection<String> entryNames = entriesToRequest.get(namespaceName);
- MapBuilder<MapBuilder<MapBuilder<CborBuilder>>> entryNameMapBuilder =
- nsMapBuilder.putMap(namespaceName);
- for (String entryName : entryNames) {
- entryNameMapBuilder.put(entryName, false);
- }
- }
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- CborEncoder encoder = new CborEncoder(baos);
- try {
- encoder.encode(builder.build());
- } catch (CborException e) {
- throw new RuntimeException("Error encoding CBOR", e);
- }
- return baos.toByteArray();
- }
-
- public static KeyPair createEphemeralKeyPair() {
- try {
- KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC);
- ECGenParameterSpec ecSpec = new ECGenParameterSpec("prime256v1");
- kpg.initialize(ecSpec);
- KeyPair keyPair = kpg.generateKeyPair();
- return keyPair;
- } catch (NoSuchAlgorithmException
- | InvalidAlgorithmParameterException e) {
- throw new RuntimeException("Error generating ephemeral key-pair", e);
- }
- }
-
- public static byte[] getPopSha256FromAuthKeyCert(X509Certificate cert) {
- byte[] octetString = cert.getExtensionValue("1.3.6.1.4.1.11129.2.1.26");
- if (octetString == null) {
- return null;
- }
- Util.hexdump("octetString", octetString);
-
- try {
- ASN1InputStream asn1InputStream = new ASN1InputStream(octetString);
- byte[] cborBytes = ((ASN1OctetString) asn1InputStream.readObject()).getOctets();
- Util.hexdump("cborBytes", cborBytes);
-
- ByteArrayInputStream bais = new ByteArrayInputStream(cborBytes);
- List<DataItem> dataItems = new CborDecoder(bais).decode();
- if (dataItems.size() != 1) {
- throw new RuntimeException("Expected 1 item, found " + dataItems.size());
- }
- if (!(dataItems.get(0) instanceof co.nstant.in.cbor.model.Array)) {
- throw new RuntimeException("Item is not a map");
- }
- co.nstant.in.cbor.model.Array array = (co.nstant.in.cbor.model.Array) dataItems.get(0);
- List<DataItem> items = array.getDataItems();
- if (items.size() < 2) {
- throw new RuntimeException(
- "Expected at least 2 array items, found " + items.size());
- }
- if (!(items.get(0) instanceof UnicodeString)) {
- throw new RuntimeException("First array item is not a string");
- }
- String id = ((UnicodeString) items.get(0)).getString();
- if (!id.equals("ProofOfBinding")) {
- throw new RuntimeException("Expected ProofOfBinding, got " + id);
- }
- if (!(items.get(1) instanceof ByteString)) {
- throw new RuntimeException("Second array item is not a bytestring");
- }
- byte[] popSha256 = ((ByteString) items.get(1)).getBytes();
- if (popSha256.length != 32) {
- throw new RuntimeException(
- "Expected bstr to be 32 bytes, it is " + popSha256.length);
- }
- return popSha256;
- } catch (IOException e) {
- throw new RuntimeException("Error decoding extension data", e);
- } catch (CborException e) {
- throw new RuntimeException("Error decoding data", e);
- }
- }
-
-}
diff --git a/identity/util/test/java/com/android/security/identity/internal/HkdfTest.java b/identity/util/test/java/com/android/security/identity/internal/HkdfTest.java
deleted file mode 100644
index 6a75090e..00000000
--- a/identity/util/test/java/com/android/security/identity/internal/HkdfTest.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright 2019 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 com.android.security.identity.internal;
-
-import androidx.test.runner.AndroidJUnit4;
-import com.android.security.identity.internal.Util;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.security.GeneralSecurityException;
-import java.util.Random;
-
-/*
- * This is based on https://github.com/google/tink/blob/master/java/src/test/java/com/google
- * /crypto/tink/subtle/HkdfTest.java
- * which is also Copyright (c) Google and licensed under the Apache 2 license.
- */
-@RunWith(AndroidJUnit4.class)
-public class HkdfTest {
-
- static Random sRandom = new Random();
-
- /** Encodes a byte array to hex. */
- static String hexEncode(final byte[] bytes) {
- String chars = "0123456789abcdef";
- StringBuilder result = new StringBuilder(2 * bytes.length);
- for (byte b : bytes) {
- // convert to unsigned
- int val = b & 0xff;
- result.append(chars.charAt(val / 16));
- result.append(chars.charAt(val % 16));
- }
- return result.toString();
- }
-
- /** Decodes a hex string to a byte array. */
- static byte[] hexDecode(String hex) {
- if (hex.length() % 2 != 0) {
- throw new IllegalArgumentException("Expected a string of even length");
- }
- int size = hex.length() / 2;
- byte[] result = new byte[size];
- for (int i = 0; i < size; i++) {
- int hi = Character.digit(hex.charAt(2 * i), 16);
- int lo = Character.digit(hex.charAt(2 * i + 1), 16);
- if ((hi == -1) || (lo == -1)) {
- throw new IllegalArgumentException("input is not hexadecimal");
- }
- result[i] = (byte) (16 * hi + lo);
- }
- return result;
- }
-
- static byte[] randBytes(int numBytes) {
- byte[] bytes = new byte[numBytes];
- sRandom.nextBytes(bytes);
- return bytes;
- }
-
- @Test
- public void testNullSaltOrInfo() throws Exception {
- byte[] ikm = randBytes(20);
- byte[] info = randBytes(20);
- int size = 40;
-
- byte[] hkdfWithNullSalt = Util.computeHkdf("HmacSha256", ikm, null, info, size);
- byte[] hkdfWithEmptySalt = Util.computeHkdf("HmacSha256", ikm, new byte[0], info, size);
- assertArrayEquals(hkdfWithNullSalt, hkdfWithEmptySalt);
-
- byte[] salt = randBytes(20);
- byte[] hkdfWithNullInfo = Util.computeHkdf("HmacSha256", ikm, salt, null, size);
- byte[] hkdfWithEmptyInfo = Util.computeHkdf("HmacSha256", ikm, salt, new byte[0], size);
- assertArrayEquals(hkdfWithNullInfo, hkdfWithEmptyInfo);
- }
-
- @Test
- public void testInvalidCodeSize() throws Exception {
- try {
- Util.computeHkdf("HmacSha256", new byte[0], new byte[0], new byte[0], 32 * 256);
- fail("Invalid size, should have thrown exception");
- } catch (RuntimeException expected) {
-
- // Expected
- }
- }
-
- /**
- * Tests the implementation against the test vectors from RFC 5869.
- */
- @Test
- public void testVectors() throws Exception {
- // Test case 1
- assertEquals(
- "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf"
- + "1a5a4c5db02d56ecc4c5bf34007208d5b887185865",
- computeHkdfHex("HmacSha256",
- "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
- "000102030405060708090a0b0c",
- "f0f1f2f3f4f5f6f7f8f9",
- 42));
-
- // Test case 2
- assertEquals(
- "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c"
- + "59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71"
- + "cc30c58179ec3e87c14c01d5c1f3434f1d87",
- computeHkdfHex("HmacSha256",
- "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
- + "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
- + "404142434445464748494a4b4c4d4e4f",
- "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f"
- + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
- + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
- "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
- + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef"
- + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
- 82));
-
- // Test case 3: salt is empty
- assertEquals(
- "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d"
- + "9d201395faa4b61a96c8",
- computeHkdfHex("HmacSha256",
- "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "", "",
- 42));
-
- // Test Case 4
- assertEquals(
- "085a01ea1b10f36933068b56efa5ad81a4f14b822f"
- + "5b091568a9cdd4f155fda2c22e422478d305f3f896",
- computeHkdfHex(
- "HmacSha1",
- "0b0b0b0b0b0b0b0b0b0b0b",
- "000102030405060708090a0b0c",
- "f0f1f2f3f4f5f6f7f8f9",
- 42));
-
- // Test Case 5
- assertEquals(
- "0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe"
- + "8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e"
- + "927336d0441f4c4300e2cff0d0900b52d3b4",
- computeHkdfHex(
- "HmacSha1",
- "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
- + "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
- + "404142434445464748494a4b4c4d4e4f",
- "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f"
- + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
- + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
- "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
- + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef"
- + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
- 82));
-
- // Test Case 6: salt is empty
- assertEquals(
- "0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0"
- + "ea00033de03984d34918",
- computeHkdfHex("HmacSha1", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "", "",
- 42));
-
- // Test Case 7
- assertEquals(
- "2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5"
- + "673a081d70cce7acfc48",
- computeHkdfHex("HmacSha1", "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", "", "",
- 42));
- }
-
- /**
- * Test version of Hkdf where all inputs and outputs are hexadecimal.
- */
- private String computeHkdfHex(String macAlgorithm, String ikmHex, String saltHex,
- String infoHex,
- int size) throws GeneralSecurityException {
- return hexEncode(
- Util.computeHkdf(macAlgorithm, hexDecode(ikmHex), hexDecode(saltHex),
- hexDecode(infoHex), size));
- }
-
-}
diff --git a/identity/util/test/java/com/android/security/identity/internal/UtilUnitTests.java b/identity/util/test/java/com/android/security/identity/internal/UtilUnitTests.java
deleted file mode 100644
index 9c27c140..00000000
--- a/identity/util/test/java/com/android/security/identity/internal/UtilUnitTests.java
+++ /dev/null
@@ -1,441 +0,0 @@
-/*
- * Copyright 2019 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 com.android.security.identity.internal;
-
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyProperties;
-import com.android.security.identity.internal.Util;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import java.security.cert.X509Certificate;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-import co.nstant.in.cbor.CborBuilder;
-import co.nstant.in.cbor.CborDecoder;
-import co.nstant.in.cbor.CborEncoder;
-import co.nstant.in.cbor.CborException;
-import co.nstant.in.cbor.builder.ArrayBuilder;
-import co.nstant.in.cbor.model.ByteString;
-import co.nstant.in.cbor.model.DataItem;
-import co.nstant.in.cbor.model.DoublePrecisionFloat;
-import co.nstant.in.cbor.model.HalfPrecisionFloat;
-import co.nstant.in.cbor.model.NegativeInteger;
-import co.nstant.in.cbor.model.SimpleValue;
-import co.nstant.in.cbor.model.SimpleValueType;
-import co.nstant.in.cbor.model.SinglePrecisionFloat;
-import co.nstant.in.cbor.model.UnicodeString;
-import co.nstant.in.cbor.model.UnsignedInteger;
-
-@RunWith(AndroidJUnit4.class)
-public class UtilUnitTests {
- @Test
- public void prettyPrintMultipleCompleteTypes() throws CborException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new CborBuilder()
- .add("text") // add string
- .add(1234) // add integer
- .add(new byte[]{0x10}) // add byte array
- .addArray() // add array
- .add(1)
- .add("text")
- .end()
- .build());
- assertEquals("'text',\n"
- + "1234,\n"
- + "[0x10],\n"
- + "[1, 'text']", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintString() throws CborException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new UnicodeString("foobar"));
- assertEquals("'foobar'", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintBytestring() throws CborException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new ByteString(new byte[]{1, 2, 33, (byte) 254}));
- assertEquals("[0x01, 0x02, 0x21, 0xfe]", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintUnsignedInteger() throws CborException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new UnsignedInteger(42));
- assertEquals("42", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintNegativeInteger() throws CborException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new NegativeInteger(-42));
- assertEquals("-42", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintDouble() throws CborException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new DoublePrecisionFloat(1.1));
- assertEquals("1.1", Util.cborPrettyPrint(baos.toByteArray()));
-
- baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new DoublePrecisionFloat(-42.0000000001));
- assertEquals("-42.0000000001", Util.cborPrettyPrint(baos.toByteArray()));
-
- baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new DoublePrecisionFloat(-5));
- assertEquals("-5", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintFloat() throws CborException {
- ByteArrayOutputStream baos;
-
- // TODO: These two tests yield different results on different devices, disable for now
- /*
- baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new SinglePrecisionFloat(1.1f));
- assertEquals("1.100000023841858", Util.cborPrettyPrint(baos.toByteArray()));
-
- baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new SinglePrecisionFloat(-42.0001f));
- assertEquals("-42.000099182128906", Util.cborPrettyPrint(baos.toByteArray()));
- */
-
- baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new SinglePrecisionFloat(-5f));
- assertEquals("-5", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintHalfFloat() throws CborException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new HalfPrecisionFloat(1.1f));
- assertEquals("1.099609375", Util.cborPrettyPrint(baos.toByteArray()));
-
- baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new HalfPrecisionFloat(-42.0001f));
- assertEquals("-42", Util.cborPrettyPrint(baos.toByteArray()));
-
- baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new HalfPrecisionFloat(-5f));
- assertEquals("-5", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintFalse() throws CborException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new SimpleValue(SimpleValueType.FALSE));
- assertEquals("false", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintTrue() throws CborException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new SimpleValue(SimpleValueType.TRUE));
- assertEquals("true", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintNull() throws CborException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new SimpleValue(SimpleValueType.NULL));
- assertEquals("null", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintUndefined() throws CborException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new SimpleValue(SimpleValueType.UNDEFINED));
- assertEquals("undefined", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintTag() throws CborException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new CborBuilder()
- .addTag(0)
- .add("ABC")
- .build());
- byte[] data = baos.toByteArray();
- assertEquals("tag 0 'ABC'", Util.cborPrettyPrint(data));
- }
-
- @Test
- public void prettyPrintArrayNoCompounds() throws CborException {
- // If an array has no compound elements, no newlines are used.
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new CborBuilder()
- .addArray() // add array
- .add(1)
- .add("text")
- .add(new ByteString(new byte[]{1, 2, 3}))
- .end()
- .build());
- assertEquals("[1, 'text', [0x01, 0x02, 0x03]]", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintArray() throws CborException {
- // This array contains a compound value so will use newlines
- CborBuilder array = new CborBuilder();
- ArrayBuilder<CborBuilder> arrayBuilder = array.addArray();
- arrayBuilder.add(2);
- arrayBuilder.add(3);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new CborBuilder()
- .addArray() // add array
- .add(1)
- .add("text")
- .add(new ByteString(new byte[]{1, 2, 3}))
- .add(array.build().get(0))
- .end()
- .build());
- assertEquals("[\n"
- + " 1,\n"
- + " 'text',\n"
- + " [0x01, 0x02, 0x03],\n"
- + " [2, 3]\n"
- + "]", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void prettyPrintMap() throws CborException {
- // If an array has no compound elements, no newlines are used.
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new CborBuilder()
- .addMap()
- .put("Foo", 42)
- .put("Bar", "baz")
- .put(43, 44)
- .put(new UnicodeString("bstr"), new ByteString(new byte[]{1, 2, 3}))
- .put(new ByteString(new byte[]{1, 2, 3}), new UnicodeString("other way"))
- .end()
- .build());
- assertEquals("{\n"
- + " 43 : 44,\n"
- + " [0x01, 0x02, 0x03] : 'other way',\n"
- + " 'Bar' : 'baz',\n"
- + " 'Foo' : 42,\n"
- + " 'bstr' : [0x01, 0x02, 0x03]\n"
- + "}", Util.cborPrettyPrint(baos.toByteArray()));
- }
-
- @Test
- public void testCanonicalizeCbor() throws Exception {
- // {"one":1, 2:"two"}
- byte[] first =
- new byte[]{(byte) 0xA2, 0x63, 0x6F, 0x6E, 0x65, 0x01, 0x02, 0x63, 0x74, 0x77, 0x6F};
-
- // {2: "two", "one": 1}
- byte[] second =
- new byte[]{(byte) 0xA2, 0x02, 0x63, 0x74, 0x77, 0x6F, 0x63, 0x6F, 0x6E, 0x65, 0x01};
-
- assertArrayEquals(Util.canonicalizeCbor(first), Util.canonicalizeCbor(second));
- }
-
- @Test
- public void cborEncodeDecodeSingle() throws Exception {
- List<DataItem> items = new CborBuilder()
- .addMap().put(1,"one").put("one", 1).end()
- .addArray().add(42).add(true).addMap().end().end()
- .add("STRING")
- .build();
- for (DataItem item: items) {
- assertEquals(item, Util.cborToDataItem(Util.cborEncode(item)));
- }
- }
-
- @Test
- public void cborEncodeDecodeBoolean() {
- assertEquals(true, Util.cborDecodeBoolean(Util.cborEncodeBoolean(true)));
- assertEquals(false, Util.cborDecodeBoolean(Util.cborEncodeBoolean(false)));
- }
-
- @Test
- public void cborEncodeDecodeString() {
- assertEquals("foo bar", Util.cborDecodeString(Util.cborEncodeString("foo bar")));
- }
-
- @Test
- public void cborEncodeDecodeBytestring() {
- byte[] bits = new byte[256];
- for (int i = 0; i < bits.length; ++i) {
- bits[i] = (byte)i;
- }
- assertArrayEquals(bits, Util.cborDecodeBytestring(Util.cborEncodeBytestring(bits)));
- }
-
- @Test
- public void cborEncodeDecodeInt() {
- assertEquals(0, Util.cborDecodeInt(Util.cborEncodeInt(0)));
- assertEquals(Integer.MAX_VALUE, Util.cborDecodeInt(Util.cborEncodeInt(Integer.MAX_VALUE)));
- assertEquals(Integer.MIN_VALUE, Util.cborDecodeInt(Util.cborEncodeInt(Integer.MIN_VALUE)));
- }
-
- @Test
- public void prependSemanticTagForEncodedCbor() throws Exception {
- byte[] inputBytes = new byte[] {1, 2, 3, 4};
- byte[] encodedInput = Util.cborEncodeBytestring(inputBytes);
- byte[] encodedWithTag = Util.prependSemanticTagForEncodedCbor(encodedInput);
-
- ByteString decodedWithTag = (ByteString)Util.cborToDataItem(encodedWithTag);
- assertEquals(decodedWithTag.getTag().getValue(), 24); // RFC 8949 defines 24
-
- byte[] decodedBytes = Util.cborDecodeBytestring(decodedWithTag.getBytes());
- assertArrayEquals(inputBytes, decodedBytes);
- }
-
- private KeyPair coseGenerateKeyPair() throws Exception {
- KeyPairGenerator kpg = KeyPairGenerator.getInstance(
- KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
- KeyGenParameterSpec.Builder builder =
- new KeyGenParameterSpec.Builder(
- "coseTestKeyPair",
- KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
- .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512);
- kpg.initialize(builder.build());
- return kpg.generateKeyPair();
- }
-
- @Test
- public void coseSignAndVerify() throws Exception {
- KeyPair keyPair = coseGenerateKeyPair();
- byte[] data = new byte[] {0x10, 0x11, 0x12, 0x13};
- byte[] detachedContent = new byte[] {};
- byte[] sig = Util.coseSign1Sign(keyPair.getPrivate(), data, detachedContent, null);
- assertTrue(Util.coseSign1CheckSignature(sig, detachedContent, keyPair.getPublic()));
- assertArrayEquals(data, Util.coseSign1GetData(sig));
- assertEquals(new ArrayList() {}, Util.coseSign1GetX5Chain(sig));
- }
-
- @Test
- public void coseSignAndVerifyDetachedContent() throws Exception {
- KeyPair keyPair = coseGenerateKeyPair();
- byte[] data = new byte[] {};
- byte[] detachedContent = new byte[] {0x20, 0x21, 0x22, 0x23, 0x24};
- byte[] sig = Util.coseSign1Sign(keyPair.getPrivate(), data, detachedContent, null);
- assertTrue(Util.coseSign1CheckSignature(sig, detachedContent, keyPair.getPublic()));
- assertArrayEquals(data, Util.coseSign1GetData(sig));
- assertEquals(new ArrayList() {}, Util.coseSign1GetX5Chain(sig));
- }
-
- @Test
- public void coseSignAndVerifySingleCertificate() throws Exception {
- KeyPair keyPair = coseGenerateKeyPair();
- byte[] data = new byte[] {};
- byte[] detachedContent = new byte[] {0x20, 0x21, 0x22, 0x23, 0x24};
- ArrayList<X509Certificate> certs = new ArrayList() {};
- certs.add(Util.signPublicKeyWithPrivateKey("coseTestKeyPair", "coseTestKeyPair"));
- byte[] sig = Util.coseSign1Sign(keyPair.getPrivate(), data, detachedContent, certs);
- assertTrue(Util.coseSign1CheckSignature(sig, detachedContent, keyPair.getPublic()));
- assertArrayEquals(data, Util.coseSign1GetData(sig));
- assertEquals(certs, Util.coseSign1GetX5Chain(sig));
- }
-
- @Test
- public void coseSignAndVerifyMultipleCertificates() throws Exception {
- KeyPair keyPair = coseGenerateKeyPair();
- byte[] data = new byte[] {};
- byte[] detachedContent = new byte[] {0x20, 0x21, 0x22, 0x23, 0x24};
- ArrayList<X509Certificate> certs = new ArrayList() {};
- certs.add(Util.signPublicKeyWithPrivateKey("coseTestKeyPair", "coseTestKeyPair"));
- certs.add(Util.signPublicKeyWithPrivateKey("coseTestKeyPair", "coseTestKeyPair"));
- certs.add(Util.signPublicKeyWithPrivateKey("coseTestKeyPair", "coseTestKeyPair"));
- byte[] sig = Util.coseSign1Sign(keyPair.getPrivate(), data, detachedContent, certs);
- assertTrue(Util.coseSign1CheckSignature(sig, detachedContent, keyPair.getPublic()));
- assertArrayEquals(data, Util.coseSign1GetData(sig));
- assertEquals(certs, Util.coseSign1GetX5Chain(sig));
- }
-
- @Test
- public void coseMac0() throws Exception {
- SecretKey secretKey = new SecretKeySpec(new byte[32], "");
- byte[] data = new byte[] {0x10, 0x11, 0x12, 0x13};
- byte[] detachedContent = new byte[] {};
- byte[] mac = Util.coseMac0(secretKey, data, detachedContent);
- assertEquals("[\n"
- + " [0xa1, 0x01, 0x05],\n"
- + " {},\n"
- + " [0x10, 0x11, 0x12, 0x13],\n"
- + " [0x6c, 0xec, 0xb5, 0x6a, 0xc9, 0x5c, 0xae, 0x3b, 0x41, 0x13, 0xde, 0xa4, "
- + "0xd8, 0x86, 0x5c, 0x28, 0x2c, 0xd5, 0xa5, 0x13, 0xff, 0x3b, 0xd1, 0xde, 0x70, "
- + "0x5e, 0xbb, 0xe2, 0x2d, 0x42, 0xbe, 0x53]\n"
- + "]", Util.cborPrettyPrint(mac));
- }
-
- @Test
- public void coseMac0DetachedContent() throws Exception {
- SecretKey secretKey = new SecretKeySpec(new byte[32], "");
- byte[] data = new byte[] {};
- byte[] detachedContent = new byte[] {0x10, 0x11, 0x12, 0x13};
- byte[] mac = Util.coseMac0(secretKey, data, detachedContent);
- // Same HMAC as in coseMac0 test, only difference is that payload is null.
- assertEquals("[\n"
- + " [0xa1, 0x01, 0x05],\n"
- + " {},\n"
- + " null,\n"
- + " [0x6c, 0xec, 0xb5, 0x6a, 0xc9, 0x5c, 0xae, 0x3b, 0x41, 0x13, 0xde, 0xa4, "
- + "0xd8, 0x86, 0x5c, 0x28, 0x2c, 0xd5, 0xa5, 0x13, 0xff, 0x3b, 0xd1, 0xde, 0x70, "
- + "0x5e, 0xbb, 0xe2, 0x2d, 0x42, 0xbe, 0x53]\n"
- + "]", Util.cborPrettyPrint(mac));
- }
-
- @Test
- public void replaceLineTest() {
- assertEquals("foo",
- Util.replaceLine("Hello World", 0, "foo"));
- assertEquals("foo\n",
- Util.replaceLine("Hello World\n", 0, "foo"));
- assertEquals("Hello World",
- Util.replaceLine("Hello World", 1, "foo"));
- assertEquals("Hello World\n",
- Util.replaceLine("Hello World\n", 1, "foo"));
- assertEquals("foo\ntwo\nthree",
- Util.replaceLine("one\ntwo\nthree", 0, "foo"));
- assertEquals("one\nfoo\nthree",
- Util.replaceLine("one\ntwo\nthree", 1, "foo"));
- assertEquals("one\ntwo\nfoo",
- Util.replaceLine("one\ntwo\nthree", 2, "foo"));
- assertEquals("one\ntwo\nfoo",
- Util.replaceLine("one\ntwo\nthree", -1, "foo"));
- assertEquals("one\ntwo\nthree\nfoo",
- Util.replaceLine("one\ntwo\nthree\nfour", -1, "foo"));
- assertEquals("one\ntwo\nfoo\nfour",
- Util.replaceLine("one\ntwo\nthree\nfour", -2, "foo"));
- }
-
-}
diff --git a/keystore-engine/Android.bp b/keystore-engine/Android.bp
index cb75cde7..0cecfd82 100644
--- a/keystore-engine/Android.bp
+++ b/keystore-engine/Android.bp
@@ -36,7 +36,7 @@ cc_library_shared {
],
shared_libs: [
- "android.system.keystore2-V1-ndk",
+ "android.system.keystore2-V1-ndk_platform",
"libbinder_ndk",
"libcrypto",
"libcutils",
@@ -66,7 +66,7 @@ cc_library_shared {
],
shared_libs: [
- "android.system.keystore2-V1-ndk",
+ "android.system.keystore2-V1-ndk_platform",
"libbase",
"libbinder_ndk",
"libcrypto",
diff --git a/keystore-engine/keystore2_engine.cpp b/keystore-engine/keystore2_engine.cpp
index 69caf510..69d2ca6f 100644
--- a/keystore-engine/keystore2_engine.cpp
+++ b/keystore-engine/keystore2_engine.cpp
@@ -23,13 +23,11 @@
#include <private/android_filesystem_config.h>
-#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/ec_key.h>
#include <openssl/ecdsa.h>
#include <openssl/engine.h>
-#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
@@ -329,31 +327,6 @@ extern "C" int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig
return 1;
}
-bssl::UniquePtr<EVP_PKEY> extractPubKey(const std::vector<uint8_t>& cert_bytes) {
- const uint8_t* p = cert_bytes.data();
- bssl::UniquePtr<X509> decoded_cert(d2i_X509(nullptr, &p, cert_bytes.size()));
- if (!decoded_cert) {
- LOG(INFO) << AT << "Could not decode the cert, trying decoding as PEM";
- bssl::UniquePtr<BIO> cert_bio(BIO_new_mem_buf(cert_bytes.data(), cert_bytes.size()));
- if (!cert_bio) {
- LOG(ERROR) << AT << "Failed to create BIO";
- return {};
- }
- decoded_cert =
- bssl::UniquePtr<X509>(PEM_read_bio_X509(cert_bio.get(), nullptr, nullptr, nullptr));
- }
- if (!decoded_cert) {
- LOG(ERROR) << AT << "Could not decode the cert.";
- return {};
- }
- bssl::UniquePtr<EVP_PKEY> pub_key(X509_get_pubkey(decoded_cert.get()));
- if (!pub_key) {
- LOG(ERROR) << AT << "Could not extract public key.";
- return {};
- }
- return pub_key;
-}
-
} // namespace
/* EVP_PKEY_from_keystore returns an |EVP_PKEY| that contains either an RSA or
@@ -384,7 +357,7 @@ extern "C" EVP_PKEY* EVP_PKEY_from_keystore2(const char* key_id) {
// If the key_id starts with the grant id prefix, we parse the following string as numeric
// grant id. We can then use the grant domain without alias to load the designated key.
- if (android::base::StartsWith(alias, keystore2_grant_id_prefix)) {
+ if (alias.find(keystore2_grant_id_prefix) == 0) {
std::stringstream s(alias.substr(keystore2_grant_id_prefix.size()));
s >> std::hex >> reinterpret_cast<uint64_t&>(descriptor.nspace);
descriptor.domain = ks2::Domain::GRANT;
@@ -410,7 +383,13 @@ extern "C" EVP_PKEY* EVP_PKEY_from_keystore2(const char* key_id) {
return nullptr;
}
- auto pkey = extractPubKey(*response.metadata.certificate);
+ const uint8_t* p = response.metadata.certificate->data();
+ bssl::UniquePtr<X509> x509(d2i_X509(nullptr, &p, response.metadata.certificate->size()));
+ if (!x509) {
+ LOG(ERROR) << AT << "Failed to parse x509 certificate.";
+ return nullptr;
+ }
+ bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(x509.get()));
if (!pkey) {
LOG(ERROR) << AT << "Failed to extract public key.";
return nullptr;
diff --git a/keystore/Android.bp b/keystore/Android.bp
index 892c5b41..0f2000cd 100644
--- a/keystore/Android.bp
+++ b/keystore/Android.bp
@@ -36,10 +36,7 @@ cc_defaults {
cc_binary {
name: "keystore_cli_v2",
- defaults: [
- "keystore_defaults",
- "keystore2_use_latest_aidl_ndk_shared",
- ],
+ defaults: ["keystore_defaults"],
cflags: [
"-DKEYMASTER_NAME_TAGS",
@@ -50,7 +47,8 @@ cc_binary {
"keystore_client.proto",
],
shared_libs: [
- "android.security.apc-ndk",
+ "android.security.apc-ndk_platform",
+ "android.system.keystore2-V1-ndk_platform",
"libbinder",
"libbinder_ndk",
"libchrome",
@@ -65,7 +63,7 @@ cc_binary {
// Library used by both keystore and credstore for generating the ASN.1 stored
// in Tag::ATTESTATION_APPLICATION_ID
-cc_library {
+cc_library_shared {
name: "libkeystore-attestation-application-id",
defaults: ["keystore_defaults"],
@@ -89,7 +87,7 @@ cc_library {
}
// Library for keystore clients using the WiFi HIDL interface
-cc_library {
+cc_library_shared {
name: "libkeystore-wifi-hidl",
defaults: ["keystore_defaults"],
diff --git a/keystore/keystore_cli_v2.cpp b/keystore/keystore_cli_v2.cpp
index d01c67d4..43f72a99 100644
--- a/keystore/keystore_cli_v2.cpp
+++ b/keystore/keystore_cli_v2.cpp
@@ -19,7 +19,6 @@
#include <iostream>
#include <memory>
#include <string>
-#include <variant>
#include <vector>
#include <base/command_line.h>
@@ -617,9 +616,9 @@ keymint::AuthorizationSet GetRSAEncryptParameters(uint32_t key_size) {
return std::move(parameters);
}
-keymint::AuthorizationSet GetECDSAParameters(keymint::EcCurve curve, bool sha256_only) {
+keymint::AuthorizationSet GetECDSAParameters(uint32_t key_size, bool sha256_only) {
keymint::AuthorizationSetBuilder parameters;
- parameters.EcdsaSigningKey(curve)
+ parameters.EcdsaSigningKey(key_size)
.Digest(keymint::Digest::SHA_2_256)
.Authorization(keymint::TAG_NO_AUTH_REQUIRED);
if (!sha256_only) {
@@ -663,12 +662,11 @@ std::vector<TestCase> GetTestCases() {
{"RSA-2048 Encrypt", true, GetRSAEncryptParameters(2048)},
{"RSA-3072 Encrypt", false, GetRSAEncryptParameters(3072)},
{"RSA-4096 Encrypt", false, GetRSAEncryptParameters(4096)},
- {"ECDSA-P256 Sign", true, GetECDSAParameters(keymint::EcCurve::P_256, true)},
- {"ECDSA-P256 Sign (more digests)", false,
- GetECDSAParameters(keymint::EcCurve::P_256, false)},
- {"ECDSA-P224 Sign", false, GetECDSAParameters(keymint::EcCurve::P_224, false)},
- {"ECDSA-P384 Sign", false, GetECDSAParameters(keymint::EcCurve::P_384, false)},
- {"ECDSA-P521 Sign", false, GetECDSAParameters(keymint::EcCurve::P_521, false)},
+ {"ECDSA-P256 Sign", true, GetECDSAParameters(256, true)},
+ {"ECDSA-P256 Sign (more digests)", false, GetECDSAParameters(256, false)},
+ {"ECDSA-P224 Sign", false, GetECDSAParameters(224, false)},
+ {"ECDSA-P384 Sign", false, GetECDSAParameters(384, false)},
+ {"ECDSA-P521 Sign", false, GetECDSAParameters(521, false)},
{"AES-128", true, GetAESParameters(128, false)},
{"AES-256", true, GetAESParameters(256, false)},
{"AES-128-GCM", false, GetAESParameters(128, true)},
@@ -1025,7 +1023,7 @@ int Confirmation(const std::string& promptText, const std::string& extraDataHex,
return 1;
}
- auto listener = ndk::SharedRefBase::make<ConfirmationListener>();
+ auto listener = std::make_shared<ConfirmationListener>();
auto future = listener->get_future();
auto rc = apcService->presentPrompt(listener, promptText, extraData, locale, uiOptionsAsFlags);
diff --git a/keystore/tests/Android.bp b/keystore/tests/Android.bp
index f51cc2f5..39601ebb 100644
--- a/keystore/tests/Android.bp
+++ b/keystore/tests/Android.bp
@@ -62,7 +62,7 @@ cc_test {
"libgtest_main",
"libutils",
"liblog",
- "android.security.apc-ndk",
+ "android.security.apc-ndk_platform",
],
shared_libs: [
"libbinder_ndk",
diff --git a/keystore/tests/confirmationui_invocation_test.cpp b/keystore/tests/confirmationui_invocation_test.cpp
index 822e6a4c..7f8a3738 100644
--- a/keystore/tests/confirmationui_invocation_test.cpp
+++ b/keystore/tests/confirmationui_invocation_test.cpp
@@ -55,7 +55,7 @@ TEST(ConfirmationInvocationTest, InvokeAndCancel) {
std::string locale("en");
std::vector<uint8_t> extraData{0xaa, 0xff, 0x00, 0x55};
- auto listener = ndk::SharedRefBase::make<ConfirmationListener>();
+ auto listener = std::make_shared<ConfirmationListener>();
auto future = listener->get_future();
diff --git a/keystore/tests/fuzzer/Android.bp b/keystore/tests/fuzzer/Android.bp
deleted file mode 100644
index 4116ae14..00000000
--- a/keystore/tests/fuzzer/Android.bp
+++ /dev/null
@@ -1,108 +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 {
- // 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"],
-}
-
-cc_fuzz {
- name: "keystoreGetWifiHidl_fuzzer",
- vendor: true,
- srcs: [
- "keystoreGetWifiHidl_fuzzer.cpp",
- ],
- static_libs: [
- "libkeystore-wifi-hidl",
- ],
- shared_libs: [
- "android.system.wifi.keystore@1.0",
- "libhidlbase",
- "liblog",
- "libutils",
- ],
- fuzz_config: {
- cc: [
- "android-media-fuzzing-reports@google.com",
- ],
- componentid: 155276,
- },
-}
-
-cc_defaults {
- name: "keystoreAttestation_defaults",
- static_libs: [
- "libkeystore-attestation-application-id",
- "liblog",
- "libbase",
- "libhidlbase",
- ],
- shared_libs: [
- "libbinder",
- "libcrypto",
- "libutils",
- ],
- fuzz_config: {
- cc: [
- "android-media-fuzzing-reports@google.com",
- ],
- componentid: 155276,
- },
-}
-
-cc_fuzz {
- name: "keystoreSignature_fuzzer",
- srcs: [
- "keystoreSignature_fuzzer.cpp",
- ],
- defaults: [
- "keystoreAttestation_defaults",
- ],
-}
-
-cc_fuzz {
- name: "keystorePackageInfo_fuzzer",
- srcs: [
- "keystorePackageInfo_fuzzer.cpp",
- ],
- defaults: [
- "keystoreAttestation_defaults",
- ],
-}
-
-cc_fuzz {
- name: "keystoreApplicationId_fuzzer",
- srcs: [
- "keystoreApplicationId_fuzzer.cpp",
- ],
- defaults: [
- "keystoreAttestation_defaults",
- ],
-}
-
-cc_fuzz {
- name: "keystoreAttestationId_fuzzer",
- srcs: [
- "keystoreAttestationId_fuzzer.cpp",
- ],
- defaults: [
- "keystoreAttestation_defaults",
- ],
-}
diff --git a/keystore/tests/fuzzer/README.md b/keystore/tests/fuzzer/README.md
deleted file mode 100644
index 25d53aba..00000000
--- a/keystore/tests/fuzzer/README.md
+++ /dev/null
@@ -1,103 +0,0 @@
-# Fuzzer for libkeystore
-## Table of contents
-+ [libkeystore-get-wifi-hidl](#libkeystore-get-wifi-hidl)
-+ [libkeystore_attestation_application_id](#libkeystore_attestation_application_id)
-
-# <a name="libkeystore-get-wifi-hidl"></a> Fuzzer for libkeystore-get-wifi-hidl
-## Plugin Design Considerations
-The fuzzer plugin for libkeystore-get-wifi-hidl is designed based on the understanding of the library and tries to achieve the following:
-
-##### Maximize code coverage
-The configuration parameters are not hardcoded, but instead selected based on
-incoming data. This ensures more code paths are reached by the fuzzer.
-
-libkeystore-get-wifi-hidl supports the following parameters:
-1. Key (parameter name: `key`)
-
-| Parameter| Valid Values| Configured Value|
-|------------- |-------------| ----- |
-| `key` | `String` | Value obtained from FuzzedDataProvider|
-
-This also ensures that the plugin is always deterministic for any given input.
-
-##### Maximize utilization of input data
-The plugin feeds the entire input data to the libkeystore-get-wifi-hidl module.
-This ensures that the plugin tolerates any kind of input (empty, huge,
-malformed, etc) and doesnt `exit()` on any input and thereby increasing the
-chance of identifying vulnerabilities.
-
-## Build
-
-This describes steps to build keystoreGetWifiHidl_fuzzer binary.
-
-### Android
-
-#### Steps to build
-Build the fuzzer
-```
- $ mm -j$(nproc) keystoreGetWifiHidl_fuzzer
-```
-#### Steps to run
-
-To run on device
-```
- $ adb sync data
- $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreGetWifiHidl_fuzzer/keystoreGetWifiHidl_fuzzer
-```
-
-# <a name="libkeystore_attestation_application_id"></a> Fuzzer for libkeystore_attestation_application_id
-## Plugin Design Considerations
-The fuzzer plugin for libkeystore-attestation-application-id are designed based on the understanding of the library and tries to achieve the following:
-
-##### Maximize code coverage
-The configuration parameters are not hardcoded, but instead selected based on
-incoming data. This ensures more code paths are reached by the fuzzer.
-
-libkeystore-attestation-application-id supports the following parameters:
-1. Package Name (parameter name: `packageName`)
-2. Version Code (parameter name: `versionCode`)
-3. Uid (parameter name: `uid`)
-
-
-| Parameter| Valid Values| Configured Value|
-|------------- |-------------| ----- |
-| `packageName` | `String` | Value obtained from FuzzedDataProvider|
-| `versionCode` | `INT64_MIN` to `INT64_MAX` | Value obtained from FuzzedDataProvider|
-| `uid` | `0` to `1000` | Value obtained from FuzzedDataProvider|
-
-This also ensures that the plugin is always deterministic for any given input.
-
-##### Maximize utilization of input data
-The plugins feed the entire input data to the libkeystore_attestation_application_id module.
-This ensures that the plugin tolerates any kind of input (empty, huge,
-malformed, etc) and doesnt `exit()` on any input and thereby increasing the
-chance of identifying vulnerabilities.
-
-## Build
-
-This describes steps to build keystoreSignature_fuzzer, keystorePackageInfo_fuzzer, keystoreApplicationId_fuzzer and keystoreAttestationId_fuzzer binary.
-
-### Android
-
-#### Steps to build
-Build the fuzzer
-```
- $ mm -j$(nproc) keystoreSignature_fuzzer
- $ mm -j$(nproc) keystorePackageInfo_fuzzer
- $ mm -j$(nproc) keystoreApplicationId_fuzzer
- $ mm -j$(nproc) keystoreAttestationId_fuzzer
-```
-#### Steps to run
-
-To run on device
-```
- $ adb sync data
- $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreSignature_fuzzer/keystoreSignature_fuzzer
- $ adb shell /data/fuzz/${TARGET_ARCH}/keystorePackageInfo_fuzzer/keystorePackageInfo_fuzzer
- $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreApplicationId_fuzzer/keystoreApplicationId_fuzzer
- $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreAttestationId_fuzzer/keystoreAttestationId_fuzzer
-```
-
-## References:
- * http://llvm.org/docs/LibFuzzer.html
- * https://github.com/google/oss-fuzz
diff --git a/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp b/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp
deleted file mode 100644
index 0eddb9a8..00000000
--- a/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp
+++ /dev/null
@@ -1,67 +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.
- */
-
-#include "keystoreCommon.h"
-#include <keystore/KeyAttestationApplicationId.h>
-
-using ::security::keymaster::KeyAttestationApplicationId;
-
-constexpr size_t kPackageVectorSizeMin = 1;
-constexpr size_t kPackageVectorSizeMax = 10;
-
-class KeystoreApplicationId {
- public:
- void process(const uint8_t* data, size_t size);
- ~KeystoreApplicationId() {}
-
- private:
- void invokeApplicationId();
- std::unique_ptr<FuzzedDataProvider> mFdp;
-};
-
-void KeystoreApplicationId::invokeApplicationId() {
- std::optional<KeyAttestationApplicationId> applicationId;
- bool shouldUsePackageInfoVector = mFdp->ConsumeBool();
- if (shouldUsePackageInfoVector) {
- KeyAttestationApplicationId::PackageInfoVector packageInfoVector;
- int32_t packageVectorSize =
- mFdp->ConsumeIntegralInRange<int32_t>(kPackageVectorSizeMin, kPackageVectorSizeMax);
- for (int32_t packageSize = 0; packageSize < packageVectorSize; ++packageSize) {
- auto packageInfoData = initPackageInfoData(mFdp.get());
- packageInfoVector.push_back(make_optional<KeyAttestationPackageInfo>(
- String16((packageInfoData.packageName).c_str()), packageInfoData.versionCode,
- packageInfoData.sharedSignaturesVector));
- }
- applicationId = KeyAttestationApplicationId(std::move(packageInfoVector));
- } else {
- auto packageInfoData = initPackageInfoData(mFdp.get());
- applicationId = KeyAttestationApplicationId(make_optional<KeyAttestationPackageInfo>(
- String16((packageInfoData.packageName).c_str()), packageInfoData.versionCode,
- packageInfoData.sharedSignaturesVector));
- }
- invokeReadWriteParcel(&applicationId.value());
-}
-
-void KeystoreApplicationId::process(const uint8_t* data, size_t size) {
- mFdp = std::make_unique<FuzzedDataProvider>(data, size);
- invokeApplicationId();
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- KeystoreApplicationId keystoreApplicationId;
- keystoreApplicationId.process(data, size);
- return 0;
-}
diff --git a/keystore/tests/fuzzer/keystoreAttestationId_fuzzer.cpp b/keystore/tests/fuzzer/keystoreAttestationId_fuzzer.cpp
deleted file mode 100644
index 581da466..00000000
--- a/keystore/tests/fuzzer/keystoreAttestationId_fuzzer.cpp
+++ /dev/null
@@ -1,29 +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.
- */
-
-#include <keystore/keystore_attestation_id.h>
-
-#include "fuzzer/FuzzedDataProvider.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
- uint32_t uid = fdp.ConsumeIntegral<uint32_t>();
- auto result = android::security::gather_attestation_application_id(uid);
- result.isOk();
- result.status();
- result.value();
- return 0;
-}
diff --git a/keystore/tests/fuzzer/keystoreCommon.h b/keystore/tests/fuzzer/keystoreCommon.h
deleted file mode 100644
index 7af3ba8c..00000000
--- a/keystore/tests/fuzzer/keystoreCommon.h
+++ /dev/null
@@ -1,77 +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.
- */
-#ifndef KEYSTORECOMMON_H
-#define KEYSTORECOMMON_H
-
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-#include <keystore/KeyAttestationPackageInfo.h>
-#include <keystore/Signature.h>
-#include <vector>
-
-#include "fuzzer/FuzzedDataProvider.h"
-
-using namespace android;
-using namespace std;
-using ::content::pm::Signature;
-using ::security::keymaster::KeyAttestationPackageInfo;
-
-constexpr size_t kSignatureSizeMin = 1;
-constexpr size_t kSignatureSizeMax = 1000;
-constexpr size_t kRandomStringLength = 256;
-constexpr size_t kSignatureVectorSizeMin = 1;
-constexpr size_t kSignatureVectorSizeMax = 1000;
-
-struct PackageInfoData {
- string packageName;
- int64_t versionCode;
- KeyAttestationPackageInfo::SharedSignaturesVector sharedSignaturesVector;
-};
-
-inline void invokeReadWriteParcel(Parcelable* obj) {
- Parcel parcel;
- obj->writeToParcel(&parcel);
- parcel.setDataPosition(0);
- obj->readFromParcel(&parcel);
-}
-
-inline vector<uint8_t> initSignatureData(FuzzedDataProvider* fdp) {
- size_t signatureSize = fdp->ConsumeIntegralInRange(kSignatureSizeMin, kSignatureSizeMax);
- vector<uint8_t> signatureData = fdp->ConsumeBytes<uint8_t>(signatureSize);
- return signatureData;
-}
-
-inline PackageInfoData initPackageInfoData(FuzzedDataProvider* fdp) {
- PackageInfoData packageInfoData;
- packageInfoData.packageName = fdp->ConsumeRandomLengthString(kRandomStringLength);
- packageInfoData.versionCode = fdp->ConsumeIntegral<int64_t>();
- size_t signatureVectorSize =
- fdp->ConsumeIntegralInRange(kSignatureVectorSizeMin, kSignatureVectorSizeMax);
- KeyAttestationPackageInfo::SignaturesVector signatureVector;
- for (size_t size = 0; size < signatureVectorSize; ++size) {
- bool shouldUseParameterizedConstructor = fdp->ConsumeBool();
- if (shouldUseParameterizedConstructor) {
- vector<uint8_t> signatureData = initSignatureData(fdp);
- signatureVector.push_back(make_optional<Signature>(signatureData));
- } else {
- signatureVector.push_back(std::nullopt);
- }
- }
- packageInfoData.sharedSignaturesVector =
- make_shared<KeyAttestationPackageInfo::SignaturesVector>(move(signatureVector));
- return packageInfoData;
-}
-#endif // KEYSTORECOMMON_H
diff --git a/keystore/tests/fuzzer/keystoreGetWifiHidl_fuzzer.cpp b/keystore/tests/fuzzer/keystoreGetWifiHidl_fuzzer.cpp
deleted file mode 100644
index 1e033c8d..00000000
--- a/keystore/tests/fuzzer/keystoreGetWifiHidl_fuzzer.cpp
+++ /dev/null
@@ -1,63 +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.
- */
-
-#include "fuzzer/FuzzedDataProvider.h"
-#include <inttypes.h>
-#include <keystore/keystore_get.h>
-
-using namespace std;
-
-constexpr int32_t kMaxKeySize = 256;
-const string kValidStrKeyPrefix[] = {"USRSKEY_",
- "PLATFORM_VPN_",
- "USRPKEY_",
- "CACERT_",
- "VPN_"
- "USRCERT_",
- "WIFI_"};
-constexpr char kStrGrantKeyPrefix[] = "ks2_keystore-engine_grant_id:";
-constexpr char kStrKeySuffix[] = "LOCKDOWN_VPN";
-constexpr size_t kGrantIdSize = 20;
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
- size_t keyLength = fdp.ConsumeIntegralInRange<size_t>(0, kMaxKeySize);
- bool usePrefix = fdp.ConsumeBool();
- string strKeyPrefix;
- size_t strKeyPrefixLength = 0;
- size_t strKeySuffixLength = min(fdp.remaining_bytes(), keyLength);
- if (usePrefix) {
- strKeyPrefix = fdp.PickValueInArray(kValidStrKeyPrefix);
- strKeyPrefixLength = sizeof(strKeyPrefix);
- strKeySuffixLength =
- (strKeySuffixLength > strKeyPrefixLength) ? strKeySuffixLength - strKeyPrefixLength : 0;
- }
- string strKeySuffix =
- fdp.ConsumeBool() ? string(kStrKeySuffix) : fdp.ConsumeBytesAsString(strKeySuffixLength);
- string strKey;
- strKey = usePrefix ? strKeyPrefix + strKeySuffix : strKeySuffix;
- if (fdp.ConsumeBool()) {
- uint64_t grant = fdp.ConsumeIntegral<uint64_t>();
- char grantId[kGrantIdSize] = "";
- snprintf(grantId, kGrantIdSize, "%" PRIx64, grant);
- strKey = strKey + string(kStrGrantKeyPrefix) + grantId;
- }
- const char* key = strKey.c_str();
- uint8_t* value = nullptr;
- keystore_get(key, strlen(key), &value);
- free(value);
- return 0;
-}
diff --git a/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp b/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp
deleted file mode 100644
index 63899ff8..00000000
--- a/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp
+++ /dev/null
@@ -1,46 +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.
- */
-
-#include "keystoreCommon.h"
-
-class KeystorePackageInfoFuzzer {
- public:
- void process(const uint8_t* data, size_t size);
- ~KeystorePackageInfoFuzzer() {}
-
- private:
- void invokePackageInfo();
- std::unique_ptr<FuzzedDataProvider> mFdp;
-};
-
-void KeystorePackageInfoFuzzer::invokePackageInfo() {
- auto packageInfoData = initPackageInfoData(mFdp.get());
- KeyAttestationPackageInfo packageInfo(String16((packageInfoData.packageName).c_str()),
- packageInfoData.versionCode,
- packageInfoData.sharedSignaturesVector);
- invokeReadWriteParcel(&packageInfo);
-}
-
-void KeystorePackageInfoFuzzer::process(const uint8_t* data, size_t size) {
- mFdp = std::make_unique<FuzzedDataProvider>(data, size);
- invokePackageInfo();
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- KeystorePackageInfoFuzzer keystorePackageInfoFuzzer;
- keystorePackageInfoFuzzer.process(data, size);
- return 0;
-}
diff --git a/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp b/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp
deleted file mode 100644
index b8f8a73e..00000000
--- a/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp
+++ /dev/null
@@ -1,50 +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.
- */
-#include "keystoreCommon.h"
-#include <keystore/Signature.h>
-
-class KeystoreSignatureFuzzer {
- public:
- void process(const uint8_t* data, size_t size);
- ~KeystoreSignatureFuzzer() {}
-
- private:
- void invokeSignature();
- std::unique_ptr<FuzzedDataProvider> mFdp;
-};
-
-void KeystoreSignatureFuzzer::invokeSignature() {
- std::optional<Signature> signature;
- bool shouldUseParameterizedConstructor = mFdp->ConsumeBool();
- if (shouldUseParameterizedConstructor) {
- std::vector<uint8_t> signatureData = initSignatureData(mFdp.get());
- signature = Signature(signatureData);
- } else {
- signature = Signature();
- }
- invokeReadWriteParcel(&signature.value());
-}
-
-void KeystoreSignatureFuzzer::process(const uint8_t* data, size_t size) {
- mFdp = std::make_unique<FuzzedDataProvider>(data, size);
- invokeSignature();
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- KeystoreSignatureFuzzer keystoreSignatureFuzzer;
- keystoreSignatureFuzzer.process(data, size);
- return 0;
-}
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index e6cb4fb5..0069f958 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -25,11 +25,9 @@ rust_defaults {
name: "libkeystore2_defaults",
crate_name: "keystore2",
srcs: ["src/lib.rs"],
- defaults: [
- "keymint_use_latest_hal_aidl_rust",
- ],
rustlibs: [
+ "android.hardware.security.keymint-V1-rust",
"android.hardware.security.secureclock-V1-rust",
"android.hardware.security.sharedsecret-V1-rust",
"android.os.permissions_aidl-rust",
@@ -39,23 +37,24 @@ rust_defaults {
"android.security.maintenance-rust",
"android.security.metrics-rust",
"android.security.remoteprovisioning-rust",
- "android.system.keystore2-V2-rust",
+ "android.system.keystore2-V1-rust",
"libanyhow",
"libbinder_rs",
+ "libcutils_bindgen",
"libkeystore2_aaid-rust",
"libkeystore2_apc_compat-rust",
"libkeystore2_crypto_rust",
"libkeystore2_km_compat",
"libkeystore2_selinux",
+ "libkeystore2_system_property-rust",
"libkeystore2_vintf_rust",
"liblazy_static",
"liblibc",
+ "liblibsqlite3_sys",
"liblog_event_list",
"liblog_rust",
"librand",
- "librustutils",
- "libserde",
- "libserde_cbor",
+ "librusqlite",
"libthiserror",
],
shared_libs: [
@@ -69,59 +68,15 @@ rust_defaults {
rust_library {
name: "libkeystore2",
defaults: ["libkeystore2_defaults"],
- rustlibs: [
- "liblibsqlite3_sys",
- "librusqlite",
- ],
}
rust_library {
name: "libkeystore2_test_utils",
crate_name: "keystore2_test_utils",
srcs: ["test_utils/lib.rs"],
- defaults: ["keymint_use_latest_hal_aidl_rust"],
- rustlibs: [
- "android.system.keystore2-V2-rust",
- "libbinder_rs",
- "libkeystore2_selinux",
- "liblog_rust",
- "libnix",
- "librand",
- "libserde",
- "libserde_cbor",
- ],
-}
-
-rust_library {
- name: "libkeystore2_with_test_utils",
- defaults: ["libkeystore2_defaults"],
- features: [
- "keystore2_blob_test_utils",
- ],
- rustlibs: [
- "liblibsqlite3_sys",
- "librusqlite",
- "libkeystore2_test_utils",
- ],
-}
-
-rust_test {
- name: "keystore2_test_utils_test",
- srcs: ["test_utils/lib.rs"],
- defaults: ["keymint_use_latest_hal_aidl_rust"],
- test_suites: ["general-tests"],
- require_root: true,
- auto_gen_config: true,
- compile_multilib: "first",
rustlibs: [
- "android.system.keystore2-V2-rust",
- "libbinder_rs",
- "libkeystore2_selinux",
"liblog_rust",
- "libnix",
"librand",
- "libserde",
- "libserde_cbor",
],
}
@@ -135,25 +90,23 @@ rust_test {
rustlibs: [
"libandroid_logger",
"libkeystore2_test_utils",
- "liblibsqlite3_sys",
"libnix",
- "librusqlite",
- "libkeystore2_with_test_utils",
],
// The test should always include watchdog.
features: [
"watchdog",
- "keystore2_blob_test_utils",
],
}
-rust_defaults {
- name: "keystore2_defaults",
+rust_binary {
+ name: "keystore2",
srcs: ["src/keystore2_main.rs"],
rustlibs: [
"libandroid_logger",
"libbinder_rs",
+ "libkeystore2",
"liblog_rust",
+ "liblegacykeystore-rust",
],
init_rc: ["keystore2.rc"],
@@ -165,18 +118,31 @@ rust_defaults {
// selection available in the build system.
prefer_rlib: true,
+ // TODO(b/187412695)
+ // This is a hack to work around the build system not installing
+ // dynamic dependencies of rlibs to the device. This section should
+ // be removed once that works correctly.
+ shared_libs: [
+ "android.hardware.confirmationui@1.0",
+ "android.hardware.security.sharedsecret-V1-ndk_platform",
+ "android.security.compat-ndk_platform",
+ "libc",
+ "libdl_android",
+ "libdl",
+ "libandroidicu",
+ "libkeymint",
+ "libkeystore2_aaid",
+ "libkeystore2_apc_compat",
+ "libkeystore2_crypto",
+ "libkeystore2_vintf_cpp",
+ "libkm_compat_service",
+ "libkm_compat",
+ "libm",
+ "libstatspull",
+ "libstatssocket",
+ ],
+
vintf_fragments: ["android.system.keystore2-service.xml"],
required: ["keystore_cli_v2"],
}
-
-rust_binary {
- name: "keystore2",
- defaults: ["keystore2_defaults"],
- rustlibs: [
- "libkeystore2",
- "liblegacykeystore-rust",
- "librusqlite",
- ],
- afdo: true,
-}
diff --git a/keystore2/TEST_MAPPING b/keystore2/TEST_MAPPING
index 5d0a7dd3..16b6f858 100644
--- a/keystore2/TEST_MAPPING
+++ b/keystore2/TEST_MAPPING
@@ -10,26 +10,7 @@
"name": "keystore2_test"
},
{
- "name": "keystore2_test_utils_test"
- },
- {
- "name": "keystore2_legacy_blobs_test"
- },
- {
"name": "CtsIdentityTestCases"
- },
- {
- "name": "CtsKeystoreTestCases",
- "options": [
- {
- "include-annotation": "android.platform.test.annotations.RequiresDevice"
- }
- ]
- }
- ],
- "postsubmit": [
- {
- "name": "CtsKeystorePerformanceTestCases"
}
]
}
diff --git a/keystore2/aaid/Android.bp b/keystore2/aaid/Android.bp
index 3417960d..c04ce511 100644
--- a/keystore2/aaid/Android.bp
+++ b/keystore2/aaid/Android.bp
@@ -57,13 +57,3 @@ rust_library {
"libkeystore2_aaid",
],
}
-
-rust_test {
- name: "libkeystore2_aaid_bindgen_test",
- srcs: [":libkeystore2_aaid_bindgen"],
- crate_name: "keystore2_aaid_bindgen_test",
- test_suites: ["general-tests"],
- auto_gen_config: true,
- clippy_lints: "none",
- lints: "none",
-}
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index ae08567d..4a7b7b46 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -24,11 +24,12 @@ package {
aidl_interface {
name: "android.security.attestationmanager",
srcs: [ "android/security/attestationmanager/*.aidl", ],
- imports: [ "android.hardware.security.keymint-V2" ],
+ imports: [ "android.hardware.security.keymint-V1" ],
unstable: true,
backend: {
java: {
platform_apis: true,
+ srcs_available: true,
},
rust: {
enabled: true,
@@ -44,13 +45,14 @@ aidl_interface {
name: "android.security.authorization",
srcs: [ "android/security/authorization/*.aidl" ],
imports: [
- "android.hardware.security.keymint-V2",
+ "android.hardware.security.keymint-V1",
"android.hardware.security.secureclock-V1",
],
unstable: true,
backend: {
java: {
platform_apis: true,
+ srcs_available: true,
},
rust: {
enabled: true,
@@ -69,6 +71,7 @@ aidl_interface {
backend: {
java: {
enabled: true,
+ srcs_available: true,
},
rust: {
enabled: true,
@@ -83,7 +86,7 @@ aidl_interface {
name: "android.security.compat",
srcs: [ "android/security/compat/*.aidl" ],
imports: [
- "android.hardware.security.keymint-V2",
+ "android.hardware.security.keymint-V1",
"android.hardware.security.secureclock-V1",
"android.hardware.security.sharedsecret-V1",
],
@@ -91,6 +94,7 @@ aidl_interface {
backend: {
java: {
platform_apis: true,
+ srcs_available: true,
},
rust: {
enabled: true,
@@ -106,12 +110,13 @@ aidl_interface {
name: "android.security.remoteprovisioning",
srcs: [ "android/security/remoteprovisioning/*.aidl" ],
imports: [
- "android.hardware.security.keymint-V2",
+ "android.hardware.security.keymint-V1",
],
unstable: true,
backend: {
java: {
platform_apis: true,
+ srcs_available: true,
},
ndk: {
enabled: true,
@@ -127,12 +132,13 @@ aidl_interface {
name: "android.security.maintenance",
srcs: [ "android/security/maintenance/*.aidl" ],
imports: [
- "android.system.keystore2-V2",
+ "android.system.keystore2-V1",
],
unstable: true,
backend: {
java: {
platform_apis: true,
+ srcs_available: true,
},
rust: {
enabled: true,
@@ -151,6 +157,7 @@ aidl_interface {
backend: {
java: {
platform_apis: true,
+ srcs_available: true,
},
rust: {
enabled: true,
@@ -166,12 +173,13 @@ aidl_interface {
name: "android.security.metrics",
srcs: [ "android/security/metrics/*.aidl" ],
imports: [
- "android.system.keystore2-V2",
+ "android.system.keystore2-V1",
],
unstable: true,
backend: {
java: {
platform_apis: true,
+ srcs_available: true,
},
rust: {
enabled: true,
@@ -183,19 +191,3 @@ aidl_interface {
},
}
-// cc_defaults that includes the latest Keystore2 AIDL library.
-// Modules that depend on KeyMint directly can include this cc_defaults to avoid
-// managing dependency versions explicitly.
-cc_defaults {
- name: "keystore2_use_latest_aidl_ndk_static",
- static_libs: [
- "android.system.keystore2-V2-ndk",
- ],
-}
-
-cc_defaults {
- name: "keystore2_use_latest_aidl_ndk_shared",
- shared_libs: [
- "android.system.keystore2-V2-ndk",
- ],
-}
diff --git a/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
index 8e347f0e..50bfa19f 100644
--- a/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
+++ b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
@@ -29,17 +29,8 @@ import android.hardware.security.sharedsecret.ISharedSecret;
*/
interface IKeystoreCompatService {
/**
- * Return an implementation of IKeyMintDevice, that it implemented by Keystore 2.0 itself.
- * The underlying implementation depends on the requested securityLevel:
- * - TRUSTED_ENVIRONMENT or STRONGBOX: implementation is by means of a hardware-backed
- * Keymaster 4.x instance. In this case, the returned device supports version 1 of
- * the IKeyMintDevice interface, with some small omissions:
- * - KeyPurpose::ATTEST_KEY is not supported (b/216437537)
- * - Specification of the MGF1 digest for RSA-OAEP is not supported (b/216436980)
- * - Specification of CERTIFICATE_{SUBJECT,SERIAL} is not supported for keys attested
- * by hardware (b/216468666).
- * - SOFTWARE: implementation is entirely software based. In this case, the returned device
- * supports the current version of the IKeyMintDevice interface.
+ * Return an implementation of IKeyMintDevice, that it implemented by Keystore 2.0 itself
+ * by means of Keymaster 4.1 or lower.
*/
IKeyMintDevice getKeyMintDevice (SecurityLevel securityLevel);
diff --git a/keystore2/aidl/android/security/metrics/EcCurve.aidl b/keystore2/aidl/android/security/metrics/EcCurve.aidl
index 7b1a5a28..b190d839 100644
--- a/keystore2/aidl/android/security/metrics/EcCurve.aidl
+++ b/keystore2/aidl/android/security/metrics/EcCurve.aidl
@@ -29,5 +29,4 @@ enum EcCurve {
P_256 = 2,
P_384 = 3,
P_521 = 4,
- CURVE_25519 = 5,
} \ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/RkpErrorStats.aidl b/keystore2/aidl/android/security/metrics/RkpErrorStats.aidl
index dcd51227..616d129e 100644
--- a/keystore2/aidl/android/security/metrics/RkpErrorStats.aidl
+++ b/keystore2/aidl/android/security/metrics/RkpErrorStats.aidl
@@ -17,7 +17,6 @@
package android.security.metrics;
import android.security.metrics.RkpError;
-import android.security.metrics.SecurityLevel;
/**
* Atom that encapsulates error information in remote key provisioning events.
* @hide
@@ -25,5 +24,4 @@ import android.security.metrics.SecurityLevel;
@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
parcelable RkpErrorStats {
RkpError rkpError;
- SecurityLevel security_level;
} \ No newline at end of file
diff --git a/keystore2/aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl b/keystore2/aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl
deleted file mode 100644
index 7d45e52e..00000000
--- a/keystore2/aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl
+++ /dev/null
@@ -1,49 +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.remoteprovisioning;
-
-import android.security.remoteprovisioning.RemotelyProvisionedKey;
-
-/**
- * This is the interface providing access to remotely-provisioned attestation keys
- * for an `IRemotelyProvisionedComponent`.
- *
- * @hide
- */
-interface IRemotelyProvisionedKeyPool {
-
- /**
- * Fetches an attestation key for the given uid and `IRemotelyProvisionedComponent`, as
- * identified by the given id.
-
- * Callers require the keystore2::get_attestation_key permission.
- *
- * ## Error conditions
- * `android.system.keystore2.ResponseCode::PERMISSION_DENIED` if the caller does not have the
- * `keystore2::get_attestation_key` permission
- *
- * @param clientUid The client application for which an attestation key is needed.
- *
- * @param irpcId The unique identifier for the `IRemotelyProvisionedComponent` for which a key
- * is requested. This id may be retrieved from a given component via the
- * `IRemotelyProvisionedComponent::getHardwareInfo` function.
- *
- * @return A `RemotelyProvisionedKey` parcelable containing a key and certification chain for
- * the given `IRemotelyProvisionedComponent`.
- */
- RemotelyProvisionedKey getAttestationKey(in int clientUid, in @utf8InCpp String irpcId);
-}
diff --git a/keystore2/aidl/android/security/remoteprovisioning/RemotelyProvisionedKey.aidl b/keystore2/aidl/android/security/remoteprovisioning/RemotelyProvisionedKey.aidl
deleted file mode 100644
index ae218550..00000000
--- a/keystore2/aidl/android/security/remoteprovisioning/RemotelyProvisionedKey.aidl
+++ /dev/null
@@ -1,42 +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.remoteprovisioning;
-
-/**
- * A `RemotelyProvisionedKey` holds an attestation key and the corresponding remotely provisioned
- * certificate chain.
- *
- * @hide
- */
-@RustDerive(Eq=true, PartialEq=true)
-parcelable RemotelyProvisionedKey {
- /**
- * The remotely-provisioned key that may be used to sign attestations. The format of this key
- * is opaque, and need only be understood by the IRemotelyProvisionedComponent that generated
- * it.
- *
- * Any private key material contained within this blob must be encrypted.
- */
- byte[] keyBlob;
-
- /**
- * Sequence of DER-encoded X.509 certificates that make up the attestation key's certificate
- * chain. This is the binary encoding for a chain that is supported by Java's
- * CertificateFactory.generateCertificates API.
- */
- byte[] encodedCertChain;
-}
diff --git a/keystore2/android.system.keystore2-service.xml b/keystore2/android.system.keystore2-service.xml
index 20c2fba9..6b8d0cb4 100644
--- a/keystore2/android.system.keystore2-service.xml
+++ b/keystore2/android.system.keystore2-service.xml
@@ -1,7 +1,6 @@
<manifest version="1.0" type="framework">
<hal format="aidl">
<name>android.system.keystore2</name>
- <version>2</version>
<interface>
<name>IKeystoreService</name>
<instance>default</instance>
diff --git a/keystore2/apc_compat/Android.bp b/keystore2/apc_compat/Android.bp
index df7521e1..bf216757 100644
--- a/keystore2/apc_compat/Android.bp
+++ b/keystore2/apc_compat/Android.bp
@@ -63,13 +63,3 @@ rust_library {
"libkeystore2_apc_compat",
],
}
-
-rust_test {
- name: "libkeystore2_apc_compat_bindgen_test",
- srcs: [":libkeystore2_apc_compat_bindgen"],
- crate_name: "keystore2_apc_compat_bindgen_test",
- test_suites: ["general-tests"],
- auto_gen_config: true,
- clippy_lints: "none",
- lints: "none",
-}
diff --git a/keystore2/keystore2.rc b/keystore2/keystore2.rc
index 6f88dd39..82bf3b86 100644
--- a/keystore2/keystore2.rc
+++ b/keystore2/keystore2.rc
@@ -10,4 +10,4 @@ service keystore2 /system/bin/keystore2 /data/misc/keystore
class early_hal
user keystore
group keystore readproc log
- task_profiles ProcessCapacityHigh
+ writepid /dev/cpuset/foreground/tasks
diff --git a/keystore2/legacykeystore/Android.bp b/keystore2/legacykeystore/Android.bp
index 505b1653..fb6f60f6 100644
--- a/keystore2/legacykeystore/Android.bp
+++ b/keystore2/legacykeystore/Android.bp
@@ -21,8 +21,8 @@ package {
default_applicable_licenses: ["system_security_license"],
}
-rust_defaults {
- name: "liblegacykeystore-rust_defaults",
+rust_library {
+ name: "liblegacykeystore-rust",
crate_name: "legacykeystore",
srcs: [
"lib.rs",
@@ -31,22 +31,14 @@ rust_defaults {
"android.security.legacykeystore-rust",
"libanyhow",
"libbinder_rs",
+ "libcutils_bindgen",
+ "libkeystore2",
"liblog_rust",
"librusqlite",
- "librustutils",
"libthiserror",
],
}
-rust_library {
- name: "liblegacykeystore-rust",
- defaults: ["liblegacykeystore-rust_defaults"],
- rustlibs: [
- "libkeystore2",
- "librusqlite",
- ],
-}
-
rust_test {
name: "legacykeystore_test",
crate_name: "legacykeystore",
@@ -57,11 +49,11 @@ rust_test {
"android.security.legacykeystore-rust",
"libanyhow",
"libbinder_rs",
+ "libcutils_bindgen",
"libkeystore2",
"libkeystore2_test_utils",
"liblog_rust",
"librusqlite",
- "librustutils",
"libthiserror",
],
}
diff --git a/keystore2/legacykeystore/lib.rs b/keystore2/legacykeystore/lib.rs
index e2d952d9..efa0870f 100644
--- a/keystore2/legacykeystore/lib.rs
+++ b/keystore2/legacykeystore/lib.rs
@@ -25,9 +25,8 @@ use android_security_legacykeystore::binder::{
};
use anyhow::{Context, Result};
use keystore2::{
- async_task::AsyncTask, error::anyhow_error_to_cstring, globals::SUPER_KEY,
- legacy_blob::LegacyBlobLoader, maintenance::DeleteListener, maintenance::Domain,
- utils::uid_to_android_user, utils::watchdog as wd,
+ async_task::AsyncTask, legacy_blob::LegacyBlobLoader, maintenance::DeleteListener,
+ maintenance::Domain, utils::watchdog as wd,
};
use rusqlite::{
params, Connection, OptionalExtension, Transaction, TransactionBehavior, NO_PARAMS,
@@ -162,7 +161,7 @@ impl DB {
self.with_transaction(TransactionBehavior::Immediate, |tx| {
tx.execute(
"DELETE FROM profiles WHERE cast ( ( owner/? ) as int) = ?;",
- params![rustutils::users::AID_USER_OFFSET, user_id],
+ params![cutils_bindgen::AID_USER_OFFSET, user_id],
)
.context("In remove_uid: Failed to delete.")
})?;
@@ -227,10 +226,7 @@ where
if log_error {
log::error!("{:?}", e);
}
- Err(BinderStatus::new_service_specific_error(
- rc,
- anyhow_error_to_cstring(&e).as_deref(),
- ))
+ Err(BinderStatus::new_service_specific_error(rc, None))
},
handle_ok,
)
@@ -316,8 +312,8 @@ impl LegacyKeystore {
if let Some(entry) = db.get(uid, alias).context("In get: Trying to load entry from DB.")? {
return Ok(entry);
}
- if self.get_legacy(uid, alias).context("In get: Trying to import legacy blob.")? {
- // If we were able to import a legacy blob try again.
+ if self.get_legacy(uid, alias).context("In get: Trying to migrate legacy blob.")? {
+ // If we were able to migrate a legacy blob try again.
if let Some(entry) =
db.get(uid, alias).context("In get: Trying to load entry from DB.")?
{
@@ -329,20 +325,19 @@ impl LegacyKeystore {
fn put(&self, alias: &str, uid: i32, entry: &[u8]) -> Result<()> {
let uid = Self::get_effective_uid(uid).context("In put.")?;
+ // In order to make sure that we don't have stale legacy entries, make sure they are
+ // migrated before replacing them.
+ let _ = self.get_legacy(uid, alias);
let mut db = self.open_db().context("In put.")?;
- db.put(uid, alias, entry).context("In put: Trying to insert entry into DB.")?;
- // When replacing an entry, make sure that there is no stale legacy file entry.
- let _ = self.remove_legacy(uid, alias);
- Ok(())
+ db.put(uid, alias, entry).context("In put: Trying to insert entry into DB.")
}
fn remove(&self, alias: &str, uid: i32) -> Result<()> {
let uid = Self::get_effective_uid(uid).context("In remove.")?;
let mut db = self.open_db().context("In remove.")?;
-
- if self.remove_legacy(uid, alias).context("In remove: trying to remove legacy entry")? {
- return Ok(());
- }
+ // In order to make sure that we don't have stale legacy entries, make sure they are
+ // migrated before removing them.
+ let _ = self.get_legacy(uid, alias);
let removed =
db.remove(uid, alias).context("In remove: Trying to remove entry from DB.")?;
if removed {
@@ -432,30 +427,17 @@ impl LegacyKeystore {
return Ok(true);
}
let mut db = DB::new(&state.db_path).context("In open_db: Failed to open db.")?;
- let imported =
- Self::import_one_legacy_entry(uid, &alias, &state.legacy_loader, &mut db)
- .context("Trying to import legacy keystore entries.")?;
- if imported {
+ let migrated =
+ Self::migrate_one_legacy_entry(uid, &alias, &state.legacy_loader, &mut db)
+ .context("Trying to migrate legacy keystore entries.")?;
+ if migrated {
state.recently_imported.insert((uid, alias));
}
- Ok(imported)
+ Ok(migrated)
})
.context("In get_legacy.")
}
- fn remove_legacy(&self, uid: u32, alias: &str) -> Result<bool> {
- let alias = alias.to_string();
- self.do_serialized(move |state| {
- if state.recently_imported.contains(&(uid, alias.clone())) {
- return Ok(false);
- }
- state
- .legacy_loader
- .remove_legacy_keystore_entry(uid, &alias)
- .context("Trying to remove legacy entry.")
- })
- }
-
fn bulk_delete_uid(&self, uid: u32) -> Result<()> {
self.do_serialized(move |state| {
let entries = state
@@ -488,31 +470,21 @@ impl LegacyKeystore {
})
}
- fn import_one_legacy_entry(
+ fn migrate_one_legacy_entry(
uid: u32,
alias: &str,
legacy_loader: &LegacyBlobLoader,
db: &mut DB,
) -> 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))
- {
- key.decrypt(ciphertext, iv, tag)
- } else {
- Err(Error::sys()).context("No key found for user. Device may be locked.")
- }
- })
- .context("In import_one_legacy_entry: Trying to read legacy keystore entry.")?;
+ .read_legacy_keystore_entry(uid, alias)
+ .context("In migrate_one_legacy_entry: Trying to read legacy keystore entry.")?;
if let Some(entry) = blob {
db.put(uid, alias, &entry)
- .context("In import_one_legacy_entry: Trying to insert entry into DB.")?;
+ .context("In migrate_one_legacy_entry: Trying to insert entry into DB.")?;
legacy_loader
.remove_legacy_keystore_entry(uid, alias)
- .context("In import_one_legacy_entry: Trying to delete legacy keystore entry.")?;
+ .context("In migrate_one_legacy_entry: Trying to delete legacy keystore entry.")?;
Ok(true)
} else {
Ok(false)
@@ -554,7 +526,7 @@ mod db_test {
use std::time::Duration;
use std::time::Instant;
- static TEST_ALIAS: &str = "test_alias";
+ static TEST_ALIAS: &str = &"test_alias";
static TEST_BLOB1: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
static TEST_BLOB2: &[u8] = &[2, 2, 3, 4, 5, 6, 7, 8, 9, 0];
static TEST_BLOB3: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
@@ -628,9 +600,9 @@ mod db_test {
.expect("Failed to open database.");
// Insert three entries for owner 2.
- db.put(2 + 2 * rustutils::users::AID_USER_OFFSET, "test1", TEST_BLOB1)
+ db.put(2 + 2 * cutils_bindgen::AID_USER_OFFSET, "test1", TEST_BLOB1)
.expect("Failed to insert test1.");
- db.put(4 + 2 * rustutils::users::AID_USER_OFFSET, "test2", TEST_BLOB2)
+ db.put(4 + 2 * cutils_bindgen::AID_USER_OFFSET, "test2", TEST_BLOB2)
.expect("Failed to insert test2.");
db.put(3, "test3", TEST_BLOB3).expect("Failed to insert test3.");
@@ -638,12 +610,12 @@ mod db_test {
assert_eq!(
Vec::<String>::new(),
- db.list(2 + 2 * rustutils::users::AID_USER_OFFSET).expect("Failed to list entries.")
+ db.list(2 + 2 * cutils_bindgen::AID_USER_OFFSET).expect("Failed to list entries.")
);
assert_eq!(
Vec::<String>::new(),
- db.list(4 + 2 * rustutils::users::AID_USER_OFFSET).expect("Failed to list entries.")
+ db.list(4 + 2 * cutils_bindgen::AID_USER_OFFSET).expect("Failed to list entries.")
);
assert_eq!(vec!["test3".to_string(),], db.list(3).expect("Failed to list entries."));
@@ -722,9 +694,9 @@ mod db_test {
}
let mut db = DB::new(&db_path3).expect("Failed to open database.");
- db.put(3, TEST_ALIAS, TEST_BLOB3).expect("Failed to add entry (3).");
+ db.put(3, &TEST_ALIAS, TEST_BLOB3).expect("Failed to add entry (3).");
- db.remove(3, TEST_ALIAS).expect("Remove failed (3).");
+ db.remove(3, &TEST_ALIAS).expect("Remove failed (3).");
}
});
@@ -738,7 +710,7 @@ mod db_test {
let mut db = DB::new(&db_path).expect("Failed to open database.");
// This may return Some or None but it must not fail.
- db.get(3, TEST_ALIAS).expect("Failed to get entry (4).");
+ db.get(3, &TEST_ALIAS).expect("Failed to get entry (4).");
}
});
diff --git a/keystore2/selinux/Android.bp b/keystore2/selinux/Android.bp
index 254f95e7..748e4063 100644
--- a/keystore2/selinux/Android.bp
+++ b/keystore2/selinux/Android.bp
@@ -63,24 +63,3 @@ rust_test {
"libthiserror",
],
}
-
-rust_test {
- name: "keystore2_selinux_concurrency_test",
- srcs: [
- "src/concurrency_test.rs",
- ],
- crate_name: "keystore2_selinux_concurrency_test",
- test_suites: ["general-tests"],
- auto_gen_config: true,
-
- rustlibs: [
- "libandroid_logger",
- "libanyhow",
- "libkeystore2_selinux",
- "liblazy_static",
- "liblog_rust",
- "libnix",
- "libnum_cpus",
- "libthiserror",
- ],
-}
diff --git a/keystore2/selinux/src/concurrency_test.rs b/keystore2/selinux/src/concurrency_test.rs
deleted file mode 100644
index a5d2df2c..00000000
--- a/keystore2/selinux/src/concurrency_test.rs
+++ /dev/null
@@ -1,190 +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 keystore2_selinux::{check_access, Context};
-use nix::sched::sched_setaffinity;
-use nix::sched::CpuSet;
-use nix::unistd::getpid;
-use std::thread;
-use std::{
- sync::{atomic::AtomicU8, atomic::Ordering, Arc},
- time::{Duration, Instant},
-};
-
-#[derive(Clone, Copy)]
-struct CatCount(u8, u8, u8, u8);
-
-impl CatCount {
- fn next(&mut self) -> CatCount {
- let result = *self;
- if self.3 == 255 {
- if self.2 == 254 {
- if self.1 == 253 {
- if self.0 == 252 {
- self.0 = 255;
- }
- self.0 += 1;
- self.1 = self.0;
- }
- self.1 += 1;
- self.2 = self.1;
- }
- self.2 += 1;
- self.3 = self.2;
- }
- self.3 += 1;
- result
- }
-
- fn make_string(&self) -> String {
- format!("c{},c{},c{},c{}", self.0, self.1, self.2, self.3)
- }
-}
-
-impl Default for CatCount {
- fn default() -> Self {
- Self(0, 1, 2, 3)
- }
-}
-
-/// This test calls selinux_check_access concurrently causing access vector cache misses
-/// in libselinux avc. The test then checks if any of the threads fails to report back
-/// after a burst of access checks. The purpose of the test is to draw out a specific
-/// access vector cache corruption that sends a calling thread into an infinite loop.
-/// This was observed when keystore2 used libselinux concurrently in a non thread safe
-/// way. See b/184006658.
-#[test]
-fn test_concurrent_check_access() {
- android_logger::init_once(
- android_logger::Config::default()
- .with_tag("keystore2_selinux_concurrency_test")
- .with_min_level(log::Level::Debug),
- );
-
- let cpus = num_cpus::get();
- let turnpike = Arc::new(AtomicU8::new(0));
- let complete_count = Arc::new(AtomicU8::new(0));
- let mut threads: Vec<thread::JoinHandle<()>> = Vec::new();
-
- for i in 0..cpus {
- log::info!("Spawning thread {}", i);
- let turnpike_clone = turnpike.clone();
- let complete_count_clone = complete_count.clone();
- threads.push(thread::spawn(move || {
- let mut cpu_set = CpuSet::new();
- cpu_set.set(i).unwrap();
- sched_setaffinity(getpid(), &cpu_set).unwrap();
- let mut cat_count: CatCount = Default::default();
-
- log::info!("Thread 0 reached turnpike");
- loop {
- turnpike_clone.fetch_add(1, Ordering::Relaxed);
- loop {
- match turnpike_clone.load(Ordering::Relaxed) {
- 0 => break,
- 255 => return,
- _ => {}
- }
- }
-
- for _ in 0..250 {
- let (tctx, sctx, perm, class) = (
- Context::new("u:object_r:keystore:s0").unwrap(),
- Context::new(&format!(
- "u:r:untrusted_app:s0:{}",
- cat_count.next().make_string()
- ))
- .unwrap(),
- "use",
- "keystore2_key",
- );
-
- check_access(&sctx, &tctx, class, perm).unwrap();
- }
-
- complete_count_clone.fetch_add(1, Ordering::Relaxed);
- while complete_count_clone.load(Ordering::Relaxed) as usize != cpus {
- thread::sleep(Duration::from_millis(5));
- }
- }
- }));
- }
-
- let mut i = 0;
- let run_time = Instant::now();
-
- loop {
- const TEST_ITERATIONS: u32 = 500;
- const MAX_SLEEPS: u64 = 500;
- const SLEEP_MILLISECONDS: u64 = 5;
- let mut sleep_count: u64 = 0;
- while turnpike.load(Ordering::Relaxed) as usize != cpus {
- thread::sleep(Duration::from_millis(SLEEP_MILLISECONDS));
- sleep_count += 1;
- assert!(
- sleep_count < MAX_SLEEPS,
- "Waited too long to go ready on iteration {}, only {} are ready",
- i,
- turnpike.load(Ordering::Relaxed)
- );
- }
-
- if i % 100 == 0 {
- let elapsed = run_time.elapsed().as_secs();
- println!("{:02}:{:02}: Iteration {}", elapsed / 60, elapsed % 60, i);
- }
-
- // Give the threads some time to reach and spin on the turn pike.
- assert_eq!(turnpike.load(Ordering::Relaxed) as usize, cpus, "i = {}", i);
- if i >= TEST_ITERATIONS {
- turnpike.store(255, Ordering::Relaxed);
- break;
- }
-
- // Now go.
- complete_count.store(0, Ordering::Relaxed);
- turnpike.store(0, Ordering::Relaxed);
- i += 1;
-
- // Wait for them to all complete.
- sleep_count = 0;
- while complete_count.load(Ordering::Relaxed) as usize != cpus {
- thread::sleep(Duration::from_millis(SLEEP_MILLISECONDS));
- sleep_count += 1;
- if sleep_count >= MAX_SLEEPS {
- // Enable the following block to park the thread to allow attaching a debugger.
- if false {
- println!(
- "Waited {} seconds and we seem stuck. Going to sleep forever.",
- (MAX_SLEEPS * SLEEP_MILLISECONDS) as f32 / 1000.0
- );
- loop {
- thread::park();
- }
- } else {
- assert!(
- sleep_count < MAX_SLEEPS,
- "Waited too long to complete on iteration {}, only {} are complete",
- i,
- complete_count.load(Ordering::Relaxed)
- );
- }
- }
- }
- }
-
- for t in threads {
- t.join().unwrap();
- }
-}
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
index c0593b7a..5197cf65 100644
--- a/keystore2/selinux/src/lib.rs
+++ b/keystore2/selinux/src/lib.rs
@@ -130,7 +130,7 @@ impl Deref for Context {
fn deref(&self) -> &Self::Target {
match self {
Self::Raw(p) => unsafe { CStr::from_ptr(*p) },
- Self::CString(cstr) => cstr,
+ Self::CString(cstr) => &cstr,
}
}
}
@@ -321,323 +321,6 @@ pub fn check_access(source: &CStr, target: &CStr, tclass: &str, perm: &str) -> R
}
}
-/// Safe wrapper around setcon.
-pub fn setcon(target: &CStr) -> std::io::Result<()> {
- // SAFETY: `setcon` takes a const char* and only performs read accesses on it
- // using strdup and strcmp. `setcon` does not retain a pointer to `target`
- // and `target` outlives the call to `setcon`.
- if unsafe { selinux::setcon(target.as_ptr()) } != 0 {
- Err(std::io::Error::last_os_error())
- } else {
- Ok(())
- }
-}
-
-/// Represents an SEPolicy permission belonging to a specific class.
-pub trait ClassPermission {
- /// The permission string of the given instance as specified in the class vector.
- fn name(&self) -> &'static str;
- /// The class of the permission.
- fn class_name(&self) -> &'static str;
-}
-
-/// This macro implements an enum with values mapped to SELinux permission names.
-/// The example below implements `enum MyPermission with public visibility:
-/// * From<i32> and Into<i32> are implemented. Where the implementation of From maps
-/// any variant not specified to the default `None` with value `0`.
-/// * `MyPermission` implements ClassPermission.
-/// * An implicit default values `MyPermission::None` is created with a numeric representation
-/// of `0` and a string representation of `"none"`.
-/// * Specifying a value is optional. If the value is omitted it is set to the value of the
-/// previous variant left shifted by 1.
-///
-/// ## Example
-/// ```
-/// implement_class!(
-/// /// MyPermission documentation.
-/// #[derive(Clone, Copy, Debug, Eq, PartialEq)]
-/// #[selinux(class_name = my_class)]
-/// pub enum MyPermission {
-/// #[selinux(name = foo)]
-/// Foo = 1,
-/// #[selinux(name = bar)]
-/// Bar = 2,
-/// #[selinux(name = snafu)]
-/// Snafu, // Implicit value: MyPermission::Bar << 1 -> 4
-/// }
-/// assert_eq!(MyPermission::Foo.name(), &"foo");
-/// assert_eq!(MyPermission::Foo.class_name(), &"my_class");
-/// assert_eq!(MyPermission::Snafu as i32, 4);
-/// );
-/// ```
-#[macro_export]
-macro_rules! implement_class {
- // First rule: Public interface.
- (
- $(#[$($enum_meta:tt)+])*
- $enum_vis:vis enum $enum_name:ident $body:tt
- ) => {
- implement_class! {
- @extract_class
- []
- [$(#[$($enum_meta)+])*]
- $enum_vis enum $enum_name $body
- }
- };
-
- // The next two rules extract the #[selinux(class_name = <name>)] meta field from
- // the types meta list.
- // This first rule finds the field and terminates the recursion through the meta fields.
- (
- @extract_class
- [$(#[$mout:meta])*]
- [
- #[selinux(class_name = $class_name:ident)]
- $(#[$($mtail:tt)+])*
- ]
- $enum_vis:vis enum $enum_name:ident {
- $(
- $(#[$($emeta:tt)+])*
- $vname:ident$( = $vval:expr)?
- ),* $(,)?
- }
- ) => {
- implement_class!{
- @extract_perm_name
- $class_name
- $(#[$mout])*
- $(#[$($mtail)+])*
- $enum_vis enum $enum_name {
- 1;
- []
- [$(
- [] [$(#[$($emeta)+])*]
- $vname$( = $vval)?,
- )*]
- }
- }
- };
-
- // The second rule iterates through the type global meta fields.
- (
- @extract_class
- [$(#[$mout:meta])*]
- [
- #[$front:meta]
- $(#[$($mtail:tt)+])*
- ]
- $enum_vis:vis enum $enum_name:ident $body:tt
- ) => {
- implement_class!{
- @extract_class
- [
- $(#[$mout])*
- #[$front]
- ]
- [$(#[$($mtail)+])*]
- $enum_vis enum $enum_name $body
- }
- };
-
- // The next four rules implement two nested recursions. The outer iterates through
- // the enum variants and the inner iterates through the meta fields of each variant.
- // The first two rules find the #[selinux(name = <name>)] stanza, terminate the inner
- // recursion and descend a level in the outer recursion.
- // The first rule matches variants with explicit initializer $vval. And updates the next
- // value to ($vval << 1).
- (
- @extract_perm_name
- $class_name:ident
- $(#[$enum_meta:meta])*
- $enum_vis:vis enum $enum_name:ident {
- $next_val:expr;
- [$($out:tt)*]
- [
- [$(#[$mout:meta])*]
- [
- #[selinux(name = $selinux_name:ident)]
- $(#[$($mtail:tt)+])*
- ]
- $vname:ident = $vval:expr,
- $($tail:tt)*
- ]
- }
- ) => {
- implement_class!{
- @extract_perm_name
- $class_name
- $(#[$enum_meta])*
- $enum_vis enum $enum_name {
- ($vval << 1);
- [
- $($out)*
- $(#[$mout])*
- $(#[$($mtail)+])*
- $selinux_name $vname = $vval,
- ]
- [$($tail)*]
- }
- }
- };
-
- // The second rule differs form the previous in that there is no explicit initializer.
- // Instead $next_val is used as initializer and the next value is set to (&next_val << 1).
- (
- @extract_perm_name
- $class_name:ident
- $(#[$enum_meta:meta])*
- $enum_vis:vis enum $enum_name:ident {
- $next_val:expr;
- [$($out:tt)*]
- [
- [$(#[$mout:meta])*]
- [
- #[selinux(name = $selinux_name:ident)]
- $(#[$($mtail:tt)+])*
- ]
- $vname:ident,
- $($tail:tt)*
- ]
- }
- ) => {
- implement_class!{
- @extract_perm_name
- $class_name
- $(#[$enum_meta])*
- $enum_vis enum $enum_name {
- ($next_val << 1);
- [
- $($out)*
- $(#[$mout])*
- $(#[$($mtail)+])*
- $selinux_name $vname = $next_val,
- ]
- [$($tail)*]
- }
- }
- };
-
- // The third rule descends a step in the inner recursion.
- (
- @extract_perm_name
- $class_name:ident
- $(#[$enum_meta:meta])*
- $enum_vis:vis enum $enum_name:ident {
- $next_val:expr;
- [$($out:tt)*]
- [
- [$(#[$mout:meta])*]
- [
- #[$front:meta]
- $(#[$($mtail:tt)+])*
- ]
- $vname:ident$( = $vval:expr)?,
- $($tail:tt)*
- ]
- }
- ) => {
- implement_class!{
- @extract_perm_name
- $class_name
- $(#[$enum_meta])*
- $enum_vis enum $enum_name {
- $next_val;
- [$($out)*]
- [
- [
- $(#[$mout])*
- #[$front]
- ]
- [$(#[$($mtail)+])*]
- $vname$( = $vval)?,
- $($tail)*
- ]
- }
- }
- };
-
- // The fourth rule terminates the outer recursion and transitions to the
- // implementation phase @spill.
- (
- @extract_perm_name
- $class_name:ident
- $(#[$enum_meta:meta])*
- $enum_vis:vis enum $enum_name:ident {
- $next_val:expr;
- [$($out:tt)*]
- []
- }
- ) => {
- implement_class!{
- @spill
- $class_name
- $(#[$enum_meta])*
- $enum_vis enum $enum_name {
- $($out)*
- }
- }
- };
-
- (
- @spill
- $class_name:ident
- $(#[$enum_meta:meta])*
- $enum_vis:vis enum $enum_name:ident {
- $(
- $(#[$emeta:meta])*
- $selinux_name:ident $vname:ident = $vval:expr,
- )*
- }
- ) => {
- $(#[$enum_meta])*
- $enum_vis enum $enum_name {
- /// The default variant of the enum.
- None = 0,
- $(
- $(#[$emeta])*
- $vname = $vval,
- )*
- }
-
- impl From<i32> for $enum_name {
- #[allow(non_upper_case_globals)]
- fn from (p: i32) -> Self {
- // Creating constants forces the compiler to evaluate the value expressions
- // so that they can be used in the match statement below.
- $(const $vname: i32 = $vval;)*
- match p {
- 0 => Self::None,
- $($vname => Self::$vname,)*
- _ => Self::None,
- }
- }
- }
-
- impl From<$enum_name> for i32 {
- fn from(p: $enum_name) -> i32 {
- p as i32
- }
- }
-
- impl ClassPermission for $enum_name {
- fn name(&self) -> &'static str {
- match self {
- Self::None => &"none",
- $(Self::$vname => stringify!($selinux_name),)*
- }
- }
- fn class_name(&self) -> &'static str {
- stringify!($class_name)
- }
- }
- };
-}
-
-/// Calls `check_access` on the given class permission.
-pub fn check_permission<T: ClassPermission>(source: &CStr, target: &CStr, perm: T) -> Result<()> {
- check_access(source, target, perm.class_name(), perm.name())
-}
-
#[cfg(test)]
mod tests {
use super::*;
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
index 7d56dc9f..0096686a 100644
--- a/keystore2/src/apc.rs
+++ b/keystore2/src/apc.rs
@@ -21,7 +21,6 @@ use std::{
sync::{mpsc::Sender, Arc, Mutex},
};
-use crate::error::anyhow_error_to_cstring;
use crate::utils::{compat_2_response_code, ui_opts_2_compat, watchdog as wd};
use android_security_apc::aidl::android::security::apc::{
IConfirmationCallback::IConfirmationCallback,
@@ -111,10 +110,7 @@ where
_ => ResponseCode::SYSTEM_ERROR.0,
},
};
- Err(BinderStatus::new_service_specific_error(
- rc,
- anyhow_error_to_cstring(&e).as_deref(),
- ))
+ Err(BinderStatus::new_service_specific_error(rc, None))
},
handle_ok,
)
diff --git a/keystore2/src/async_task.rs b/keystore2/src/async_task.rs
index 0515c8f8..e130024f 100644
--- a/keystore2/src/async_task.rs
+++ b/keystore2/src/async_task.rs
@@ -417,9 +417,7 @@ mod tests {
Err(RecvTimeoutError::Timeout)
);
done_receiver.recv().unwrap();
- // Now that the last low-priority job has completed, the idle task should
- // fire pretty much immediately.
- idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
+ idle_done_receiver.recv_timeout(Duration::from_millis(1)).unwrap();
// Idle callback not executed again even if we wait for a while.
assert_eq!(
@@ -440,7 +438,7 @@ mod tests {
Err(RecvTimeoutError::Timeout)
);
done_receiver.recv().unwrap();
- idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
+ idle_done_receiver.recv_timeout(Duration::from_millis(1)).unwrap();
}
#[test]
diff --git a/keystore2/src/attestation_key_utils.rs b/keystore2/src/attestation_key_utils.rs
index 8354ba5c..ca00539b 100644
--- a/keystore2/src/attestation_key_utils.rs
+++ b/keystore2/src/attestation_key_utils.rs
@@ -35,7 +35,6 @@ use keystore2_crypto::parse_subject_from_certificate;
/// handled quite differently, thus the different representations.
pub enum AttestationKeyInfo {
RemoteProvisioned {
- key_id_guard: KeyIdGuard,
attestation_key: AttestationKey,
attestation_certs: Certificate,
},
@@ -61,22 +60,18 @@ pub fn get_attest_key_info(
let challenge_present = params.iter().any(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE);
match attest_key_descriptor {
None if challenge_present => rem_prov_state
- .get_remotely_provisioned_attestation_key_and_certs(key, caller_uid, params, db)
+ .get_remotely_provisioned_attestation_key_and_certs(&key, caller_uid, params, db)
.context(concat!(
"In get_attest_key_and_cert_chain: ",
"Trying to get remotely provisioned attestation key."
))
.map(|result| {
- result.map(|(key_id_guard, attestation_key, attestation_certs)| {
- AttestationKeyInfo::RemoteProvisioned {
- key_id_guard,
- attestation_key,
- attestation_certs,
- }
+ result.map(|(attestation_key, attestation_certs)| {
+ AttestationKeyInfo::RemoteProvisioned { attestation_key, attestation_certs }
})
}),
None => Ok(None),
- Some(attest_key) => get_user_generated_attestation_key(attest_key, caller_uid, db)
+ Some(attest_key) => get_user_generated_attestation_key(&attest_key, caller_uid, db)
.context("In get_attest_key_and_cert_chain: Trying to load attest key")
.map(Some),
}
@@ -88,7 +83,7 @@ fn get_user_generated_attestation_key(
db: &mut KeystoreDB,
) -> Result<AttestationKeyInfo> {
let (key_id_guard, blob, cert, blob_metadata) =
- load_attest_key_blob_and_cert(key, caller_uid, db)
+ load_attest_key_blob_and_cert(&key, caller_uid, db)
.context("In get_user_generated_attestation_key: Failed to load blob and cert")?;
let issuer_subject: Vec<u8> = parse_subject_from_certificate(&cert).context(
@@ -110,11 +105,11 @@ fn load_attest_key_blob_and_cert(
_ => {
let (key_id_guard, mut key_entry) = db
.load_key_entry(
- key,
+ &key,
KeyType::Client,
KeyEntryLoadBits::BOTH,
caller_uid,
- |k, av| check_key_permission(KeyPerm::Use, k, &av),
+ |k, av| check_key_permission(KeyPerm::use_(), k, &av),
)
.context("In load_attest_key_blob_and_cert: Failed to load key.")?;
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index 8265dd02..777089f4 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -15,8 +15,7 @@
//! This module implements IKeystoreAuthorization AIDL interface.
use crate::error::Error as KeystoreError;
-use crate::error::anyhow_error_to_cstring;
-use crate::globals::{ENFORCEMENTS, SUPER_KEY, DB, LEGACY_IMPORTER};
+use crate::globals::{ENFORCEMENTS, SUPER_KEY, DB, LEGACY_MIGRATOR};
use crate::permission::KeystorePerm;
use crate::super_key::UserState;
use crate::utils::{check_keystore_permission, watchdog as wd};
@@ -89,10 +88,7 @@ where
// as well.
_ => ResponseCode::SYSTEM_ERROR.0,
};
- return Err(BinderStatus::new_service_specific_error(
- rc,
- anyhow_error_to_cstring(&e).as_deref(),
- ));
+ return Err(BinderStatus::new_service_specific_error(rc, None));
}
let rc = match root_cause.downcast_ref::<Error>() {
Some(Error::Rc(rcode)) => rcode.0,
@@ -102,10 +98,7 @@ where
_ => ResponseCode::SYSTEM_ERROR.0,
},
};
- Err(BinderStatus::new_service_specific_error(
- rc,
- anyhow_error_to_cstring(&e).as_deref(),
- ))
+ Err(BinderStatus::new_service_specific_error(rc, None))
},
handle_ok,
)
@@ -126,7 +119,7 @@ impl AuthorizationManager {
fn add_auth_token(&self, auth_token: &HardwareAuthToken) -> Result<()> {
// Check keystore permission.
- check_keystore_permission(KeystorePerm::AddAuth).context("In add_auth_token.")?;
+ check_keystore_permission(KeystorePerm::add_auth()).context("In add_auth_token.")?;
ENFORCEMENTS.add_auth_token(auth_token.clone());
Ok(())
@@ -150,14 +143,12 @@ impl AuthorizationManager {
(LockScreenEvent::UNLOCK, Some(password)) => {
// This corresponds to the unlock() method in legacy keystore API.
// check permission
- check_keystore_permission(KeystorePerm::Unlock)
+ check_keystore_permission(KeystorePerm::unlock())
.context("In on_lock_screen_event: Unlock with password.")?;
ENFORCEMENTS.set_device_locked(user_id, false);
- let mut skm = SUPER_KEY.write().unwrap();
-
DB.with(|db| {
- skm.unlock_screen_lock_bound_key(
+ SUPER_KEY.unlock_screen_lock_bound_key(
&mut db.borrow_mut(),
user_id as u32,
&password,
@@ -168,9 +159,10 @@ impl AuthorizationManager {
// Unlock super key.
if let UserState::Uninitialized = DB
.with(|db| {
- skm.unlock_and_get_user_state(
+ UserState::get_with_password_unlock(
&mut db.borrow_mut(),
- &LEGACY_IMPORTER,
+ &LEGACY_MIGRATOR,
+ &SUPER_KEY,
user_id as u32,
&password,
)
@@ -185,23 +177,21 @@ impl AuthorizationManager {
Ok(())
}
(LockScreenEvent::UNLOCK, None) => {
- check_keystore_permission(KeystorePerm::Unlock)
+ check_keystore_permission(KeystorePerm::unlock())
.context("In on_lock_screen_event: Unlock.")?;
ENFORCEMENTS.set_device_locked(user_id, false);
- let mut skm = SUPER_KEY.write().unwrap();
DB.with(|db| {
- skm.try_unlock_user_with_biometric(&mut db.borrow_mut(), user_id as u32)
+ SUPER_KEY.try_unlock_user_with_biometric(&mut db.borrow_mut(), user_id as u32)
})
.context("In on_lock_screen_event: try_unlock_user_with_biometric failed")?;
Ok(())
}
(LockScreenEvent::LOCK, None) => {
- check_keystore_permission(KeystorePerm::Lock)
+ check_keystore_permission(KeystorePerm::lock())
.context("In on_lock_screen_event: Lock")?;
ENFORCEMENTS.set_device_locked(user_id, true);
- let mut skm = SUPER_KEY.write().unwrap();
DB.with(|db| {
- skm.lock_screen_lock_bound_key(
+ SUPER_KEY.lock_screen_lock_bound_key(
&mut db.borrow_mut(),
user_id as u32,
unlocking_sids.unwrap_or(&[]),
@@ -225,7 +215,7 @@ impl AuthorizationManager {
) -> Result<AuthorizationTokens> {
// Check permission. Function should return if this failed. Therefore having '?' at the end
// is very important.
- check_keystore_permission(KeystorePerm::GetAuthToken)
+ check_keystore_permission(KeystorePerm::get_auth_token())
.context("In get_auth_tokens_for_credstore.")?;
// If the challenge is zero, return error
@@ -275,7 +265,7 @@ impl IKeystoreAuthorization for AuthorizationManager {
challenge: i64,
secure_user_id: i64,
auth_token_max_age_millis: i64,
- ) -> binder::Result<AuthorizationTokens> {
+ ) -> binder::public_api::Result<AuthorizationTokens> {
let _wp = wd::watch_millis("IKeystoreAuthorization::getAuthTokensForCredStore", 500);
map_or_log_err(
self.get_auth_tokens_for_credstore(
diff --git a/keystore2/src/boot_level_keys.rs b/keystore2/src/boot_level_keys.rs
index 08c52af1..1110caf9 100644
--- a/keystore2/src/boot_level_keys.rs
+++ b/keystore2/src/boot_level_keys.rs
@@ -243,40 +243,40 @@ mod test {
fn test_output_is_consistent() -> Result<()> {
let initial_key = b"initial key";
let mut blkc = BootLevelKeyCache::new(ZVec::try_from(initial_key as &[u8])?);
- assert!(blkc.level_accessible(0));
- assert!(blkc.level_accessible(9));
- assert!(blkc.level_accessible(10));
- assert!(blkc.level_accessible(100));
+ assert_eq!(true, blkc.level_accessible(0));
+ assert_eq!(true, blkc.level_accessible(9));
+ assert_eq!(true, blkc.level_accessible(10));
+ assert_eq!(true, blkc.level_accessible(100));
let v0 = blkc.aes_key(0).unwrap().unwrap();
let v10 = blkc.aes_key(10).unwrap().unwrap();
assert_eq!(Some(&v0), blkc.aes_key(0)?.as_ref());
assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
blkc.advance_boot_level(5)?;
- assert!(!blkc.level_accessible(0));
- assert!(blkc.level_accessible(9));
- assert!(blkc.level_accessible(10));
- assert!(blkc.level_accessible(100));
+ assert_eq!(false, blkc.level_accessible(0));
+ assert_eq!(true, blkc.level_accessible(9));
+ assert_eq!(true, blkc.level_accessible(10));
+ assert_eq!(true, blkc.level_accessible(100));
assert_eq!(None, blkc.aes_key(0)?);
assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
blkc.advance_boot_level(10)?;
- assert!(!blkc.level_accessible(0));
- assert!(!blkc.level_accessible(9));
- assert!(blkc.level_accessible(10));
- assert!(blkc.level_accessible(100));
+ assert_eq!(false, blkc.level_accessible(0));
+ assert_eq!(false, blkc.level_accessible(9));
+ assert_eq!(true, blkc.level_accessible(10));
+ assert_eq!(true, blkc.level_accessible(100));
assert_eq!(None, blkc.aes_key(0)?);
assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
blkc.advance_boot_level(0)?;
- assert!(!blkc.level_accessible(0));
- assert!(!blkc.level_accessible(9));
- assert!(blkc.level_accessible(10));
- assert!(blkc.level_accessible(100));
+ assert_eq!(false, blkc.level_accessible(0));
+ assert_eq!(false, blkc.level_accessible(9));
+ assert_eq!(true, blkc.level_accessible(10));
+ assert_eq!(true, blkc.level_accessible(100));
assert_eq!(None, blkc.aes_key(0)?);
assert_eq!(Some(v10), blkc.aes_key(10)?);
blkc.finish();
- assert!(!blkc.level_accessible(0));
- assert!(!blkc.level_accessible(9));
- assert!(!blkc.level_accessible(10));
- assert!(!blkc.level_accessible(100));
+ assert_eq!(false, blkc.level_accessible(0));
+ assert_eq!(false, blkc.level_accessible(9));
+ assert_eq!(false, blkc.level_accessible(10));
+ assert_eq!(false, blkc.level_accessible(100));
assert_eq!(None, blkc.aes_key(0)?);
assert_eq!(None, blkc.aes_key(10)?);
Ok(())
diff --git a/keystore2/src/crypto/Android.bp b/keystore2/src/crypto/Android.bp
index c3f6f3c8..3ba47cd3 100644
--- a/keystore2/src/crypto/Android.bp
+++ b/keystore2/src/crypto/Android.bp
@@ -35,7 +35,6 @@ rust_library {
"libkeystore2_crypto",
"libcrypto",
],
- vendor_available: true,
}
cc_library {
@@ -49,7 +48,6 @@ cc_library {
"libcrypto",
"liblog",
],
- vendor_available: true,
}
rust_bindgen {
@@ -58,11 +56,9 @@ rust_bindgen {
crate_name: "keystore2_crypto_bindgen",
source_stem: "bindings",
host_supported: true,
- vendor_available: true,
shared_libs: ["libcrypto"],
bindgen_flags: [
"--size_t-is-usize",
- "--allowlist-function", "hmacSha256",
"--allowlist-function", "randomBytes",
"--allowlist-function", "AES_gcm_encrypt",
"--allowlist-function", "AES_gcm_decrypt",
@@ -129,13 +125,3 @@ cc_test {
"libcrypto",
],
}
-
-rust_test {
- name: "libkeystore2_crypto_bindgen_test",
- srcs: [":libkeystore2_crypto_bindgen"],
- crate_name: "keystore2_crypto_bindgen_test",
- test_suites: ["general-tests"],
- auto_gen_config: true,
- clippy_lints: "none",
- lints: "none",
-}
diff --git a/keystore2/src/crypto/certificate_utils.cpp b/keystore2/src/crypto/certificate_utils.cpp
index 64bf1d00..24b37934 100644
--- a/keystore2/src/crypto/certificate_utils.cpp
+++ b/keystore2/src/crypto/certificate_utils.cpp
@@ -19,7 +19,6 @@
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
-#include <openssl/ossl_typ.h>
#include <openssl/x509v3.h>
#include <functional>
@@ -518,7 +517,10 @@ std::variant<CertUtilsError, ASN1_STRING_Ptr> buildRsaPssParameter(Digest digest
return ASN1_STRING_Ptr(algo_str);
}
-std::variant<CertUtilsError, X509_ALGOR_Ptr> makeAlgo(Algo algo, Padding padding, Digest digest) {
+CertUtilsError makeAndSetAlgo(X509_ALGOR* algo_field, Algo algo, Padding padding, Digest digest) {
+ if (algo_field == nullptr) {
+ return CertUtilsError::UnexpectedNullPointer;
+ }
ASN1_STRING_Ptr param;
int param_type = V_ASN1_UNDEF;
int nid = 0;
@@ -587,29 +589,23 @@ std::variant<CertUtilsError, X509_ALGOR_Ptr> makeAlgo(Algo algo, Padding padding
return CertUtilsError::InvalidArgument;
}
- X509_ALGOR_Ptr result(X509_ALGOR_new());
- if (!result) {
- return CertUtilsError::MemoryAllocation;
- }
- if (!X509_ALGOR_set0(result.get(), OBJ_nid2obj(nid), param_type, param.get())) {
+ if (!X509_ALGOR_set0(algo_field, OBJ_nid2obj(nid), param_type, param.get())) {
return CertUtilsError::Encoding;
}
// The X509 struct took ownership.
param.release();
- return result;
+ return CertUtilsError::Ok;
}
// This function allows for signing a
CertUtilsError signCertWith(X509* certificate,
std::function<std::vector<uint8_t>(const uint8_t*, size_t)> sign,
Algo algo, Padding padding, Digest digest) {
- auto algo_objV = makeAlgo(algo, padding, digest);
- if (auto error = std::get_if<CertUtilsError>(&algo_objV)) {
- return *error;
+ if (auto error = makeAndSetAlgo(certificate->sig_alg, algo, padding, digest)) {
+ return error;
}
- auto& algo_obj = std::get<X509_ALGOR_Ptr>(algo_objV);
- if (!X509_set1_signature_algo(certificate, algo_obj.get())) {
- return CertUtilsError::BoringSsl;
+ if (auto error = makeAndSetAlgo(certificate->cert_info->signature, algo, padding, digest)) {
+ return error;
}
uint8_t* cert_buf = nullptr;
@@ -624,10 +620,13 @@ CertUtilsError signCertWith(X509* certificate,
return CertUtilsError::SignatureFailed;
}
- if (!X509_set1_signature_value(certificate, signature.data(), signature.size())) {
+ if (!ASN1_STRING_set(certificate->signature, signature.data(), signature.size())) {
return CertUtilsError::BoringSsl;
}
+ certificate->signature->flags &= ~(0x07);
+ certificate->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
return CertUtilsError::Ok;
}
diff --git a/keystore2/src/crypto/crypto.cpp b/keystore2/src/crypto/crypto.cpp
index 34a9a403..5d360a1e 100644
--- a/keystore2/src/crypto/crypto.cpp
+++ b/keystore2/src/crypto/crypto.cpp
@@ -25,7 +25,6 @@
#include <openssl/ecdh.h>
#include <openssl/evp.h>
#include <openssl/hkdf.h>
-#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
@@ -67,14 +66,6 @@ const EVP_CIPHER* getAesCipherForKey(size_t key_size) {
return cipher;
}
-bool hmacSha256(const uint8_t* key, size_t key_size, const uint8_t* msg, size_t msg_size,
- uint8_t* out, size_t out_size) {
- const EVP_MD* digest = EVP_sha256();
- unsigned int actual_out_size = out_size;
- uint8_t* p = HMAC(digest, key, key_size, msg, msg_size, out, &actual_out_size);
- return (p != nullptr);
-}
-
bool randomBytes(uint8_t* out, size_t len) {
return RAND_bytes(out, len);
}
diff --git a/keystore2/src/crypto/crypto.hpp b/keystore2/src/crypto/crypto.hpp
index d66532f7..f841eb38 100644
--- a/keystore2/src/crypto/crypto.hpp
+++ b/keystore2/src/crypto/crypto.hpp
@@ -22,8 +22,6 @@
#include <stddef.h>
extern "C" {
- bool hmacSha256(const uint8_t* key, size_t key_size, const uint8_t* msg, size_t msg_size,
- uint8_t* out, size_t out_size);
bool randomBytes(uint8_t* out, size_t len);
bool AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len,
const uint8_t* key, size_t key_size, const uint8_t* iv, uint8_t* tag);
diff --git a/keystore2/src/crypto/error.rs b/keystore2/src/crypto/error.rs
index 48a2d4cb..a369012c 100644
--- a/keystore2/src/crypto/error.rs
+++ b/keystore2/src/crypto/error.rs
@@ -13,7 +13,6 @@
// limitations under the License.
//! This module implements Error for the keystore2_crypto library.
-use crate::zvec;
/// Crypto specific error codes.
#[derive(Debug, thiserror::Error, Eq, PartialEq)]
@@ -94,12 +93,4 @@ pub enum Error {
/// This is returned if the C implementation of extractSubjectFromCertificate failed.
#[error("Failed to extract certificate subject.")]
ExtractSubjectFailed,
-
- /// This is returned if the C implementation of hmacSha256 failed.
- #[error("Failed to calculate HMAC-SHA256.")]
- HmacSha256Failed,
-
- /// Zvec error.
- #[error(transparent)]
- ZVec(#[from] zvec::Error),
}
diff --git a/keystore2/src/crypto/include/certificate_utils.h b/keystore2/src/crypto/include/certificate_utils.h
index cad82b61..b483d88d 100644
--- a/keystore2/src/crypto/include/certificate_utils.h
+++ b/keystore2/src/crypto/include/certificate_utils.h
@@ -39,7 +39,6 @@ DEFINE_OPENSSL_OBJECT_POINTER(ASN1_OCTET_STRING);
DEFINE_OPENSSL_OBJECT_POINTER(ASN1_TIME);
DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY);
DEFINE_OPENSSL_OBJECT_POINTER(X509);
-DEFINE_OPENSSL_OBJECT_POINTER(X509_ALGOR);
DEFINE_OPENSSL_OBJECT_POINTER(X509_EXTENSION);
DEFINE_OPENSSL_OBJECT_POINTER(X509_NAME);
DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY_CTX);
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index 14bdf045..5f8a2ef2 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -16,11 +16,11 @@
//! Keystore 2.0.
mod error;
-pub mod zvec;
+mod zvec;
pub use error::Error;
use keystore2_crypto_bindgen::{
- extractSubjectFromCertificate, generateKeyFromPassword, hmacSha256, randomBytes,
- AES_gcm_decrypt, AES_gcm_encrypt, ECDHComputeKey, ECKEYGenerateKey, ECKEYMarshalPrivateKey,
+ extractSubjectFromCertificate, generateKeyFromPassword, randomBytes, AES_gcm_decrypt,
+ AES_gcm_encrypt, ECDHComputeKey, ECKEYGenerateKey, ECKEYMarshalPrivateKey,
ECKEYParsePrivateKey, ECPOINTOct2Point, ECPOINTPoint2Oct, EC_KEY_free, EC_KEY_get0_public_key,
EC_POINT_free, HKDFExpand, HKDFExtract, EC_KEY, EC_MAX_BYTES, EC_POINT, EVP_MAX_MD_SIZE,
};
@@ -39,8 +39,6 @@ pub const AES_256_KEY_LENGTH: usize = 32;
pub const AES_128_KEY_LENGTH: usize = 16;
/// Length of the expected salt for key from password generation.
pub const SALT_LENGTH: usize = 16;
-/// Length of an HMAC-SHA256 tag in bytes.
-pub const HMAC_SHA256_LEN: usize = 32;
/// Older versions of keystore produced IVs with four extra
/// ignored zero bytes at the end; recognise and trim those.
@@ -74,21 +72,6 @@ pub fn generate_random_data(size: usize) -> Result<Vec<u8>, Error> {
}
}
-/// Perform HMAC-SHA256.
-pub fn hmac_sha256(key: &[u8], msg: &[u8]) -> Result<Vec<u8>, Error> {
- let mut tag = vec![0; HMAC_SHA256_LEN];
- // Safety: The first two pairs of arguments must point to const buffers with
- // size given by the second arg of the pair. The final pair of arguments
- // must point to an output buffer with size given by the second arg of the
- // pair.
- match unsafe {
- hmacSha256(key.as_ptr(), key.len(), msg.as_ptr(), msg.len(), tag.as_mut_ptr(), tag.len())
- } {
- true => Ok(tag),
- false => Err(Error::HmacSha256Failed),
- }
-}
-
/// Uses AES GCM to decipher a message given an initialization vector, aead tag, and key.
/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based
/// on the key length.
@@ -582,18 +565,4 @@ mod tests {
assert_eq!(left_key, right_key);
Ok(())
}
-
- #[test]
- fn test_hmac_sha256() {
- let key = b"This is the key";
- let msg1 = b"This is a message";
- let msg2 = b"This is another message";
- let tag1a = hmac_sha256(key, msg1).unwrap();
- assert_eq!(tag1a.len(), HMAC_SHA256_LEN);
- let tag1b = hmac_sha256(key, msg1).unwrap();
- assert_eq!(tag1a, tag1b);
- let tag2 = hmac_sha256(key, msg2).unwrap();
- assert_eq!(tag2.len(), HMAC_SHA256_LEN);
- assert_ne!(tag1a, tag2);
- }
}
diff --git a/keystore2/src/crypto/tests/certificate_utils_test.cpp b/keystore2/src/crypto/tests/certificate_utils_test.cpp
index bd949282..ebd67929 100644
--- a/keystore2/src/crypto/tests/certificate_utils_test.cpp
+++ b/keystore2/src/crypto/tests/certificate_utils_test.cpp
@@ -334,4 +334,4 @@ TEST(TimeStringTests, toTimeStringTest) {
// And one millisecond earlier must be GeneralizedTime.
// This also checks that the rounding direction does not flip when the input is negative.
ASSERT_EQ(std::string(toTimeString(-631152000001)->data()), "19491231235959Z");
-}
+} \ No newline at end of file
diff --git a/keystore2/src/crypto/zvec.rs b/keystore2/src/crypto/zvec.rs
index 5a173c30..78b474ea 100644
--- a/keystore2/src/crypto/zvec.rs
+++ b/keystore2/src/crypto/zvec.rs
@@ -12,9 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! Implements ZVec, a vector that is mlocked during its lifetime and zeroed
-//! when dropped.
-
+use crate::error::Error;
use nix::sys::mman::{mlock, munlock};
use std::convert::TryFrom;
use std::fmt;
@@ -31,14 +29,6 @@ pub struct ZVec {
len: usize,
}
-/// ZVec specific error codes.
-#[derive(Debug, thiserror::Error, Eq, PartialEq)]
-pub enum Error {
- /// Underlying libc error.
- #[error(transparent)]
- NixError(#[from] nix::Error),
-}
-
impl ZVec {
/// Create a ZVec with the given size.
pub fn new(size: usize) -> Result<Self, Error> {
@@ -58,14 +48,6 @@ impl ZVec {
self.len = len;
}
}
-
- /// Attempts to make a clone of the Zvec. This may fail due trying to mlock
- /// the new memory region.
- pub fn try_clone(&self) -> Result<Self, Error> {
- let mut result = Self::new(self.len())?;
- result[..].copy_from_slice(&self[..]);
- Ok(result)
- }
}
impl Drop for ZVec {
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index a3979bd5..c7887200 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -45,8 +45,6 @@ mod perboot;
pub(crate) mod utils;
mod versioning;
-use crate::gc::Gc;
-use crate::globals::get_keymint_dev_by_uuid;
use crate::impl_metadata; // This is in db_utils.rs
use crate::key_parameter::{KeyParameter, Tag};
use crate::metrics_store::log_rkp_error_stats;
@@ -56,6 +54,7 @@ use crate::{
error::{Error as KsError, ErrorCode, ResponseCode},
super_key::SuperKeyType,
};
+use crate::{gc::Gc, super_key::USER_SUPER_KEY};
use anyhow::{anyhow, Context, Result};
use std::{convert::TryFrom, convert::TryInto, ops::Deref, time::SystemTimeError};
use utils as db_utils;
@@ -83,7 +82,7 @@ use log::error;
#[cfg(not(test))]
use rand::prelude::random;
use rusqlite::{
- params, params_from_iter,
+ params,
types::FromSql,
types::FromSqlResult,
types::ToSqlOutput,
@@ -143,7 +142,7 @@ impl KeyMetaData {
let db_tag: i64 = row.get(0).context("Failed to read tag.")?;
metadata.insert(
db_tag,
- KeyMetaEntry::new_from_sql(db_tag, &SqlField::new(1, row))
+ KeyMetaEntry::new_from_sql(db_tag, &SqlField::new(1, &row))
.context("Failed to read KeyMetaEntry.")?,
);
Ok(())
@@ -218,7 +217,7 @@ impl BlobMetaData {
let db_tag: i64 = row.get(0).context("Failed to read tag.")?;
metadata.insert(
db_tag,
- BlobMetaEntry::new_from_sql(db_tag, &SqlField::new(1, row))
+ BlobMetaEntry::new_from_sql(db_tag, &SqlField::new(1, &row))
.context("Failed to read BlobMetaEntry.")?,
);
Ok(())
@@ -324,8 +323,6 @@ pub static KEYSTORE_UUID: Uuid = Uuid([
0x41, 0xe3, 0xb9, 0xce, 0x27, 0x58, 0x4e, 0x91, 0xbc, 0xfd, 0xa5, 0x5d, 0x91, 0x85, 0xab, 0x11,
]);
-static EXPIRATION_BUFFER_MS: i64 = 20000;
-
/// Indicates how the sensitive part of this key blob is encrypted.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum EncryptedBy {
@@ -391,12 +388,12 @@ impl DateTime {
}
/// Returns unix epoch time in milliseconds.
- pub fn to_millis_epoch(self) -> i64 {
+ pub fn to_millis_epoch(&self) -> i64 {
self.0
}
/// Returns unix epoch time in seconds.
- pub fn to_secs_epoch(self) -> i64 {
+ pub fn to_secs_epoch(&self) -> i64 {
self.0 / 1000
}
}
@@ -581,36 +578,6 @@ pub struct CertificateInfo {
cert_chain: Option<Vec<u8>>,
}
-/// This type represents a Blob with its metadata and an optional superseded blob.
-#[derive(Debug)]
-pub struct BlobInfo<'a> {
- blob: &'a [u8],
- metadata: &'a BlobMetaData,
- /// Superseded blobs are an artifact of legacy import. In some rare occasions
- /// the key blob needs to be upgraded during import. In that case two
- /// blob are imported, the superseded one will have to be imported first,
- /// so that the garbage collector can reap it.
- superseded_blob: Option<(&'a [u8], &'a BlobMetaData)>,
-}
-
-impl<'a> BlobInfo<'a> {
- /// Create a new instance of blob info with blob and corresponding metadata
- /// and no superseded blob info.
- pub fn new(blob: &'a [u8], metadata: &'a BlobMetaData) -> Self {
- Self { blob, metadata, superseded_blob: None }
- }
-
- /// Create a new instance of blob info with blob and corresponding metadata
- /// as well as superseded blob info.
- pub fn new_with_superseded(
- blob: &'a [u8],
- metadata: &'a BlobMetaData,
- superseded_blob: Option<(&'a [u8], &'a BlobMetaData)>,
- ) -> Self {
- Self { blob, metadata, superseded_blob }
- }
-}
-
impl CertificateInfo {
/// Constructs a new CertificateInfo object from `cert` and `cert_chain`
pub fn new(cert: Option<Vec<u8>>, cert_chain: Option<Vec<u8>>) -> Self {
@@ -865,7 +832,7 @@ impl KeystoreDB {
const UPGRADERS: &'static [fn(&Transaction) -> Result<u32>] = &[Self::from_0_to_1];
/// Name of the file that holds the cross-boot persistent database.
- pub const PERSISTENT_DB_FILENAME: &'static str = "persistent.sqlite";
+ pub const PERSISTENT_DB_FILENAME: &'static str = &"persistent.sqlite";
/// This will create a new database connection connecting the two
/// files persistent.sqlite and perboot.sqlite in the given directory.
@@ -875,7 +842,7 @@ impl KeystoreDB {
pub fn new(db_root: &Path, gc: Option<Arc<Gc>>) -> Result<Self> {
let _wp = wd::watch_millis("KeystoreDB::new", 500);
- let persistent_path = Self::make_persistent_path(db_root)?;
+ let persistent_path = Self::make_persistent_path(&db_root)?;
let conn = Self::make_connection(&persistent_path)?;
let mut db = Self { conn, gc, perboot: perboot::PERBOOT_DB.clone() };
@@ -1062,7 +1029,7 @@ impl KeystoreDB {
params: &[&str],
) -> Result<StorageStats> {
let (total, unused) = self.with_transaction(TransactionBehavior::Deferred, |tx| {
- tx.query_row(query, params_from_iter(params), |row| Ok((row.get(0)?, row.get(1)?)))
+ tx.query_row(query, params, |row| Ok((row.get(0)?, row.get(1)?)))
.with_context(|| {
format!("get_storage_stat: Error size of storage type {}", storage_type.0)
})
@@ -1277,7 +1244,7 @@ impl KeystoreDB {
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let key_descriptor =
KeyDescriptor { domain, nspace, alias: Some(alias.to_string()), blob: None };
- let result = Self::load_key_entry_id(tx, &key_descriptor, key_type);
+ let result = Self::load_key_entry_id(&tx, &key_descriptor, key_type);
match result {
Ok(_) => Ok(true),
Err(error) => match error.root_cause().downcast_ref::<KsError>() {
@@ -1323,7 +1290,7 @@ impl KeystoreDB {
key_metadata.store_in_db(key_id, tx).context("KeyMetaData::store_in_db failed")?;
Self::set_blob_internal(
- tx,
+ &tx,
key_id,
SubComponentType::KEY_BLOB,
Some(blob),
@@ -1353,10 +1320,10 @@ impl KeystoreDB {
alias: Some(key_type.alias.into()),
blob: None,
};
- let id = Self::load_key_entry_id(tx, &key_descriptor, KeyType::Super);
+ let id = Self::load_key_entry_id(&tx, &key_descriptor, KeyType::Super);
match id {
Ok(id) => {
- let key_entry = Self::load_key_components(tx, KeyEntryLoadBits::KM, id)
+ let key_entry = Self::load_key_components(&tx, KeyEntryLoadBits::KM, id)
.context("In load_super_key. Failed to load key entry.")?;
Ok(Some((KEY_ID_LOCK.get(id), key_entry)))
}
@@ -1416,7 +1383,7 @@ impl KeystoreDB {
let (id, entry) = match id {
Some(id) => (
id,
- Self::load_key_components(tx, KeyEntryLoadBits::KM, id)
+ Self::load_key_components(&tx, KeyEntryLoadBits::KM, id)
.context("In get_or_create_key_with.")?,
),
@@ -1442,7 +1409,7 @@ impl KeystoreDB {
let (blob, metadata) =
create_new_key().context("In get_or_create_key_with.")?;
Self::set_blob_internal(
- tx,
+ &tx,
id,
SubComponentType::KEY_BLOB,
Some(&blob),
@@ -1593,7 +1560,7 @@ impl KeystoreDB {
.context("In create_key_entry")?,
);
Self::set_blob_internal(
- tx,
+ &tx,
key_id.0,
SubComponentType::KEY_BLOB,
Some(private_key),
@@ -1602,7 +1569,7 @@ impl KeystoreDB {
let mut metadata = KeyMetaData::new();
metadata.add(KeyMetaEntry::AttestationMacedPublicKey(maced_public_key.to_vec()));
metadata.add(KeyMetaEntry::AttestationRawPubKey(raw_public_key.to_vec()));
- metadata.store_in_db(key_id.0, tx)?;
+ metadata.store_in_db(key_id.0, &tx)?;
Ok(()).no_gc()
})
.context("In create_attestation_key_entry")
@@ -1625,7 +1592,7 @@ impl KeystoreDB {
let _wp = wd::watch_millis("KeystoreDB::set_blob", 500);
self.with_transaction(TransactionBehavior::Immediate, |tx| {
- Self::set_blob_internal(tx, key_id.0, sc_type, blob, blob_metadata).need_gc()
+ Self::set_blob_internal(&tx, key_id.0, sc_type, blob, blob_metadata).need_gc()
})
.context("In set_blob.")
}
@@ -1639,7 +1606,7 @@ impl KeystoreDB {
self.with_transaction(TransactionBehavior::Immediate, |tx| {
Self::set_blob_internal(
- tx,
+ &tx,
Self::UNASSIGNED_KEY_ID,
SubComponentType::KEY_BLOB,
Some(blob),
@@ -1732,7 +1699,7 @@ impl KeystoreDB {
#[cfg(test)]
fn insert_key_metadata(&mut self, key_id: &KeyIdGuard, metadata: &KeyMetaData) -> Result<()> {
self.with_transaction(TransactionBehavior::Immediate, |tx| {
- metadata.store_in_db(key_id.0, tx).no_gc()
+ metadata.store_in_db(key_id.0, &tx).no_gc()
})
.context("In insert_key_metadata.")
}
@@ -1794,16 +1761,16 @@ impl KeystoreDB {
metadata.add(KeyMetaEntry::AttestationExpirationDate(DateTime::from_millis_epoch(
expiration_date,
)));
- metadata.store_in_db(key_id, tx).context("Failed to insert key metadata.")?;
+ metadata.store_in_db(key_id, &tx).context("Failed to insert key metadata.")?;
Self::set_blob_internal(
- tx,
+ &tx,
key_id,
SubComponentType::CERT_CHAIN,
Some(cert_chain),
None,
)
.context("Failed to insert cert chain")?;
- Self::set_blob_internal(tx, key_id, SubComponentType::CERT, Some(batch_cert), None)
+ Self::set_blob_internal(&tx, key_id, SubComponentType::CERT, Some(batch_cert), None)
.context("Failed to insert cert")?;
Ok(()).no_gc()
})
@@ -1864,9 +1831,7 @@ impl KeystoreDB {
)
.context("Failed to assign attestation key")?;
if result == 0 {
- 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);
+ log_rkp_error_stats(MetricsRkpError::OUT_OF_KEYS);
return Err(KsError::Rc(ResponseCode::OUT_OF_KEYS)).context("Out of keys.");
} else if result > 1 {
return Err(KsError::sys())
@@ -1944,15 +1909,12 @@ impl KeystoreDB {
)?
.collect::<rusqlite::Result<Vec<(i64, DateTime)>>>()
.context("Failed to get date metadata")?;
- // Calculate curr_time with a discount factor to avoid a key that's milliseconds away
- // from expiration dodging this delete call.
let curr_time = DateTime::from_millis_epoch(
- SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64
- + EXPIRATION_BUFFER_MS,
+ SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64,
);
let mut num_deleted = 0;
for id in key_ids_to_check.iter().filter(|kt| kt.1 < curr_time).map(|kt| kt.0) {
- if Self::mark_unreferenced(tx, id)? {
+ if Self::mark_unreferenced(&tx, id)? {
num_deleted += 1;
}
}
@@ -1979,7 +1941,7 @@ impl KeystoreDB {
.context("Failed to execute statement")?;
let num_deleted = keys_to_delete
.iter()
- .map(|id| Self::mark_unreferenced(tx, *id))
+ .map(|id| Self::mark_unreferenced(&tx, *id))
.collect::<Result<Vec<bool>>>()
.context("Failed to execute mark_unreferenced on a keyid")?
.into_iter()
@@ -2057,41 +2019,6 @@ impl KeystoreDB {
.context("In get_attestation_pool_status: ")
}
- fn query_kid_for_attestation_key_and_cert_chain(
- &self,
- tx: &Transaction,
- domain: Domain,
- namespace: i64,
- km_uuid: &Uuid,
- ) -> Result<Option<i64>> {
- let mut stmt = tx.prepare(
- "SELECT id
- FROM persistent.keyentry
- WHERE key_type = ?
- AND domain = ?
- AND namespace = ?
- AND state = ?
- AND km_uuid = ?;",
- )?;
- let rows = stmt
- .query_map(
- params![
- KeyType::Attestation,
- domain.0 as u32,
- namespace,
- KeyLifeCycle::Live,
- km_uuid
- ],
- |row| row.get(0),
- )?
- .collect::<rusqlite::Result<Vec<i64>>>()
- .context("query failed.")?;
- if rows.is_empty() {
- return Ok(None);
- }
- Ok(Some(rows[0]))
- }
-
/// Fetches the private key and corresponding certificate chain assigned to a
/// domain/namespace pair. Will either return nothing if the domain/namespace is
/// not assigned, or one CertificateChain.
@@ -2100,7 +2027,7 @@ impl KeystoreDB {
domain: Domain,
namespace: i64,
km_uuid: &Uuid,
- ) -> Result<Option<(KeyIdGuard, CertificateChain)>> {
+ ) -> Result<Option<CertificateChain>> {
let _wp = wd::watch_millis("KeystoreDB::retrieve_attestation_key_and_cert_chain", 500);
match domain {
@@ -2110,71 +2037,69 @@ impl KeystoreDB {
.context(format!("Domain {:?} must be either App or SELinux.", domain));
}
}
-
- self.delete_expired_attestation_keys().context(
- "In retrieve_attestation_key_and_cert_chain: failed to prune expired attestation keys",
- )?;
- let tx = self.conn.unchecked_transaction().context(
- "In retrieve_attestation_key_and_cert_chain: Failed to initialize transaction.",
- )?;
- let key_id: i64 = match self
- .query_kid_for_attestation_key_and_cert_chain(&tx, domain, namespace, km_uuid)?
- {
- None => return Ok(None),
- Some(kid) => kid,
- };
- tx.commit()
- .context("In retrieve_attestation_key_and_cert_chain: Failed to commit keyid query")?;
- let key_id_guard = KEY_ID_LOCK.get(key_id);
- let tx = self.conn.unchecked_transaction().context(
- "In retrieve_attestation_key_and_cert_chain: Failed to initialize transaction.",
- )?;
- let mut stmt = tx.prepare(
- "SELECT subcomponent_type, blob
- FROM persistent.blobentry
- WHERE keyentryid = ?;",
- )?;
- let rows = stmt
- .query_map(params![key_id_guard.id()], |row| Ok((row.get(0)?, row.get(1)?)))?
- .collect::<rusqlite::Result<Vec<(SubComponentType, Vec<u8>)>>>()
- .context("query failed.")?;
- if rows.is_empty() {
- return Ok(None);
- } else if rows.len() != 3 {
- return Err(KsError::sys()).context(format!(
- concat!(
- "Expected to get a single attestation",
- "key, cert, and cert chain for a total of 3 entries, but instead got {}."
- ),
- rows.len()
- ));
- }
- let mut km_blob: Vec<u8> = Vec::new();
- let mut cert_chain_blob: Vec<u8> = Vec::new();
- let mut batch_cert_blob: Vec<u8> = Vec::new();
- for row in rows {
- let sub_type: SubComponentType = row.0;
- match sub_type {
- SubComponentType::KEY_BLOB => {
- km_blob = row.1;
- }
- SubComponentType::CERT_CHAIN => {
- cert_chain_blob = row.1;
- }
- SubComponentType::CERT => {
- batch_cert_blob = row.1;
+ self.with_transaction(TransactionBehavior::Deferred, |tx| {
+ let mut stmt = tx.prepare(
+ "SELECT subcomponent_type, blob
+ FROM persistent.blobentry
+ WHERE keyentryid IN
+ (SELECT id
+ FROM persistent.keyentry
+ WHERE key_type = ?
+ AND domain = ?
+ AND namespace = ?
+ AND state = ?
+ AND km_uuid = ?);",
+ )?;
+ let rows = stmt
+ .query_map(
+ params![
+ KeyType::Attestation,
+ domain.0 as u32,
+ namespace,
+ KeyLifeCycle::Live,
+ km_uuid
+ ],
+ |row| Ok((row.get(0)?, row.get(1)?)),
+ )?
+ .collect::<rusqlite::Result<Vec<(SubComponentType, Vec<u8>)>>>()
+ .context("query failed.")?;
+ if rows.is_empty() {
+ return Ok(None).no_gc();
+ } else if rows.len() != 3 {
+ return Err(KsError::sys()).context(format!(
+ concat!(
+ "Expected to get a single attestation",
+ "key, cert, and cert chain for a total of 3 entries, but instead got {}."
+ ),
+ rows.len()
+ ));
+ }
+ let mut km_blob: Vec<u8> = Vec::new();
+ let mut cert_chain_blob: Vec<u8> = Vec::new();
+ let mut batch_cert_blob: Vec<u8> = Vec::new();
+ for row in rows {
+ let sub_type: SubComponentType = row.0;
+ match sub_type {
+ SubComponentType::KEY_BLOB => {
+ km_blob = row.1;
+ }
+ SubComponentType::CERT_CHAIN => {
+ cert_chain_blob = row.1;
+ }
+ SubComponentType::CERT => {
+ batch_cert_blob = row.1;
+ }
+ _ => Err(KsError::sys()).context("Unknown or incorrect subcomponent type.")?,
}
- _ => Err(KsError::sys()).context("Unknown or incorrect subcomponent type.")?,
}
- }
- Ok(Some((
- key_id_guard,
- CertificateChain {
+ Ok(Some(CertificateChain {
private_key: ZVec::try_from(km_blob)?,
batch_cert: batch_cert_blob,
cert_chain: cert_chain_blob,
- },
- )))
+ }))
+ .no_gc()
+ })
+ .context("In retrieve_attestation_key_and_cert_chain:")
}
/// Updates the alias column of the given key id `newid` with the given alias,
@@ -2302,13 +2227,13 @@ impl KeystoreDB {
/// fields, and rebinds the given alias to the new key.
/// The boolean returned is a hint for the garbage collector. If true, a key was replaced,
/// is now unreferenced and needs to be collected.
- #[allow(clippy::too_many_arguments)]
+ #[allow(clippy::clippy::too_many_arguments)]
pub fn store_new_key(
&mut self,
key: &KeyDescriptor,
key_type: KeyType,
params: &[KeyParameter],
- blob_info: &BlobInfo,
+ blob_info: &(&[u8], &BlobMetaData),
cert_info: &CertificateInfo,
metadata: &KeyMetaData,
km_uuid: &Uuid,
@@ -2328,37 +2253,17 @@ impl KeystoreDB {
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let key_id = Self::create_key_entry_internal(tx, &domain, namespace, key_type, km_uuid)
.context("Trying to create new key entry.")?;
- let BlobInfo { blob, metadata: blob_metadata, superseded_blob } = *blob_info;
-
- // In some occasions the key blob is already upgraded during the import.
- // In order to make sure it gets properly deleted it is inserted into the
- // database here and then immediately replaced by the superseding blob.
- // The garbage collector will then subject the blob to deleteKey of the
- // KM back end to permanently invalidate the key.
- let need_gc = if let Some((blob, blob_metadata)) = superseded_blob {
- Self::set_blob_internal(
- tx,
- key_id.id(),
- SubComponentType::KEY_BLOB,
- Some(blob),
- Some(blob_metadata),
- )
- .context("Trying to insert superseded key blob.")?;
- true
- } else {
- false
- };
-
+ let (blob, blob_metadata) = *blob_info;
Self::set_blob_internal(
tx,
key_id.id(),
SubComponentType::KEY_BLOB,
Some(blob),
- Some(blob_metadata),
+ Some(&blob_metadata),
)
.context("Trying to insert the key blob.")?;
if let Some(cert) = &cert_info.cert {
- Self::set_blob_internal(tx, key_id.id(), SubComponentType::CERT, Some(cert), None)
+ Self::set_blob_internal(tx, key_id.id(), SubComponentType::CERT, Some(&cert), None)
.context("Trying to insert the certificate.")?;
}
if let Some(cert_chain) = &cert_info.cert_chain {
@@ -2366,7 +2271,7 @@ impl KeystoreDB {
tx,
key_id.id(),
SubComponentType::CERT_CHAIN,
- Some(cert_chain),
+ Some(&cert_chain),
None,
)
.context("Trying to insert the certificate chain.")?;
@@ -2374,9 +2279,8 @@ impl KeystoreDB {
Self::insert_keyparameter_internal(tx, &key_id, params)
.context("Trying to insert key parameters.")?;
metadata.store_in_db(key_id.id(), tx).context("Trying to insert key metadata.")?;
- let need_gc = Self::rebind_alias(tx, &key_id, alias, &domain, namespace, key_type)
- .context("Trying to rebind alias.")?
- || need_gc;
+ let need_gc = Self::rebind_alias(tx, &key_id, &alias, &domain, namespace, key_type)
+ .context("Trying to rebind alias.")?;
Ok(key_id).do_gc(need_gc)
})
.context("In store_new_key.")
@@ -2425,7 +2329,7 @@ impl KeystoreDB {
metadata.store_in_db(key_id.id(), tx).context("Trying to insert key metadata.")?;
- let need_gc = Self::rebind_alias(tx, &key_id, alias, &domain, namespace, key_type)
+ let need_gc = Self::rebind_alias(tx, &key_id, &alias, &domain, namespace, key_type)
.context("Trying to rebind alias.")?;
Ok(key_id).do_gc(need_gc)
})
@@ -2494,7 +2398,7 @@ impl KeystoreDB {
if access_key.domain == Domain::APP {
access_key.nspace = caller_uid as i64;
}
- let key_id = Self::load_key_entry_id(tx, &access_key, key_type)
+ let key_id = Self::load_key_entry_id(&tx, &access_key, key_type)
.with_context(|| format!("With key.domain = {:?}.", access_key.domain))?;
Ok((key_id, access_key, None))
@@ -2659,7 +2563,7 @@ impl KeystoreDB {
let tag = Tag(row.get(0).context("Failed to read tag.")?);
let sec_level = SecurityLevel(row.get(2).context("Failed to read sec_level.")?);
parameters.push(
- KeyParameter::new_from_sql(tag, &SqlField::new(1, row), sec_level)
+ KeyParameter::new_from_sql(tag, &SqlField::new(1, &row), sec_level)
.context("Failed to read KeyParameter.")?,
);
Ok(())
@@ -2893,33 +2797,33 @@ impl KeystoreDB {
"DELETE FROM persistent.keymetadata
WHERE keyentryid IN (
SELECT id FROM persistent.keyentry
- WHERE domain = ? AND namespace = ? AND (key_type = ? OR key_type = ?)
+ WHERE domain = ? AND namespace = ? AND key_type = ?
);",
- params![domain.0, namespace, KeyType::Client, KeyType::Attestation],
+ params![domain.0, namespace, KeyType::Client],
)
.context("Trying to delete keymetadata.")?;
tx.execute(
"DELETE FROM persistent.keyparameter
WHERE keyentryid IN (
SELECT id FROM persistent.keyentry
- WHERE domain = ? AND namespace = ? AND (key_type = ? OR key_type = ?)
+ WHERE domain = ? AND namespace = ? AND key_type = ?
);",
- params![domain.0, namespace, KeyType::Client, KeyType::Attestation],
+ params![domain.0, namespace, KeyType::Client],
)
.context("Trying to delete keyparameters.")?;
tx.execute(
"DELETE FROM persistent.grant
WHERE keyentryid IN (
SELECT id FROM persistent.keyentry
- WHERE domain = ? AND namespace = ? AND (key_type = ? OR key_type = ?)
+ WHERE domain = ? AND namespace = ? AND key_type = ?
);",
- params![domain.0, namespace, KeyType::Client, KeyType::Attestation],
+ params![domain.0, namespace, KeyType::Client],
)
.context("Trying to delete grants.")?;
tx.execute(
"DELETE FROM persistent.keyentry
- WHERE domain = ? AND namespace = ? AND (key_type = ? OR key_type = ?);",
- params![domain.0, namespace, KeyType::Client, KeyType::Attestation],
+ WHERE domain = ? AND namespace = ? AND key_type = ?;",
+ params![domain.0, namespace, KeyType::Client],
)
.context("Trying to delete keyentry.")?;
Ok(()).need_gc()
@@ -2991,6 +2895,7 @@ impl KeystoreDB {
) OR (
key_type = ?
AND namespace = ?
+ AND alias = ?
AND state = ?
);",
aid_user_offset = AID_USER_OFFSET
@@ -3010,6 +2915,7 @@ impl KeystoreDB {
// OR super key:
KeyType::Super,
user_id,
+ USER_SUPER_KEY.alias,
KeyLifeCycle::Live
])
.context("In unbind_keys_for_user. Failed to query the keys created by apps.")?;
@@ -3035,7 +2941,7 @@ impl KeystoreDB {
}
}
}
- notify_gc = Self::mark_unreferenced(tx, key_id)
+ notify_gc = Self::mark_unreferenced(&tx, key_id)
.context("In unbind_keys_for_user.")?
|| notify_gc;
}
@@ -3049,15 +2955,16 @@ impl KeystoreDB {
load_bits: KeyEntryLoadBits,
key_id: i64,
) -> Result<KeyEntry> {
- let metadata = KeyMetaData::load_from_db(key_id, tx).context("In load_key_components.")?;
+ let metadata = KeyMetaData::load_from_db(key_id, &tx).context("In load_key_components.")?;
let (has_km_blob, key_blob_info, cert_blob, cert_chain_blob) =
- Self::load_blob_components(key_id, load_bits, tx).context("In load_key_components.")?;
+ Self::load_blob_components(key_id, load_bits, &tx)
+ .context("In load_key_components.")?;
- let parameters = Self::load_key_parameters(key_id, tx)
+ let parameters = Self::load_key_parameters(key_id, &tx)
.context("In load_key_components: Trying to load key parameters.")?;
- let km_uuid = Self::get_key_km_uuid(tx, key_id)
+ let km_uuid = Self::get_key_km_uuid(&tx, key_id)
.context("In load_key_components: Trying to get KM uuid.")?;
Ok(KeyEntry {
@@ -3141,7 +3048,7 @@ impl KeystoreDB {
// But even if we load the access tuple by grant here, the permission
// check denies the attempt to create a grant by grant descriptor.
let (key_id, access_key_descriptor, _) =
- Self::load_access_tuple(tx, key, KeyType::Client, caller_uid)
+ Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid)
.context("In grant")?;
// Perform access control. It is vital that we return here if the permission
@@ -3201,7 +3108,7 @@ impl KeystoreDB {
// Load the key_id and complete the access control tuple.
// We ignore the access vector here because grants cannot be granted.
let (key_id, access_key_descriptor, _) =
- Self::load_access_tuple(tx, key, KeyType::Client, caller_uid)
+ Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid)
.context("In ungrant.")?;
// Perform access control. We must return here if the permission
@@ -3303,7 +3210,7 @@ impl KeystoreDB {
}
#[cfg(test)]
-pub mod tests {
+mod tests {
use super::*;
use crate::key_parameter::{
@@ -3312,7 +3219,7 @@ pub mod tests {
};
use crate::key_perm_set;
use crate::permission::{KeyPerm, KeyPermSet};
- use crate::super_key::{SuperKeyManager, USER_SUPER_KEY, SuperEncryptionAlgorithm, SuperKeyType};
+ use crate::super_key::SuperKeyManager;
use keystore2_test_utils::TempDir;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
HardwareAuthToken::HardwareAuthToken,
@@ -3327,14 +3234,13 @@ pub mod tests {
use std::collections::BTreeMap;
use std::fmt::Write;
use std::sync::atomic::{AtomicU8, Ordering};
- use std::sync::{Arc, RwLock};
+ use std::sync::Arc;
use std::thread;
use std::time::{Duration, SystemTime};
- use crate::utils::AesGcm;
#[cfg(disabled)]
use std::time::Instant;
- pub fn new_test_db() -> Result<KeystoreDB> {
+ fn new_test_db() -> Result<KeystoreDB> {
let conn = KeystoreDB::make_connection("file::memory:")?;
let mut db = KeystoreDB { conn, gc: None, perboot: Arc::new(perboot::PerbootDB::new()) };
@@ -3348,7 +3254,7 @@ pub mod tests {
where
F: Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static,
{
- let super_key: Arc<RwLock<SuperKeyManager>> = Default::default();
+ let super_key: Arc<SuperKeyManager> = Default::default();
let gc_db = KeystoreDB::new(path, None).expect("Failed to open test gc db_connection.");
let gc = Gc::new_init_with(Default::default(), move || (Box::new(cb), gc_db, super_key));
@@ -3553,18 +3459,15 @@ pub mod tests {
#[test]
fn test_store_signed_attestation_certificate_chain() -> Result<()> {
let mut db = new_test_db()?;
- let expiration_date: i64 =
- SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64
- + EXPIRATION_BUFFER_MS
- + 10000;
+ let expiration_date: i64 = 20;
let namespace: i64 = 30;
let base_byte: u8 = 1;
let loaded_values =
load_attestation_key_pool(&mut db, expiration_date, namespace, base_byte)?;
let chain =
db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace, &KEYSTORE_UUID)?;
- assert!(chain.is_some());
- let (_, cert_chain) = chain.unwrap();
+ assert_eq!(true, chain.is_some());
+ let cert_chain = chain.unwrap();
assert_eq!(cert_chain.private_key.to_vec(), loaded_values.priv_key);
assert_eq!(cert_chain.batch_cert, loaded_values.batch_cert);
assert_eq!(cert_chain.cert_chain, loaded_values.cert_chain);
@@ -3633,9 +3536,7 @@ pub mod tests {
TempDir::new("test_remove_expired_certs_").expect("Failed to create temp dir.");
let mut db = new_test_db_with_gc(temp_dir.path(), |_, _| Ok(()))?;
let expiration_date: i64 =
- SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64
- + EXPIRATION_BUFFER_MS
- + 10000;
+ SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64 + 10000;
let namespace: i64 = 30;
let namespace_del1: i64 = 45;
let namespace_del2: i64 = 60;
@@ -3646,7 +3547,7 @@ pub mod tests {
0x01, /* base_byte */
)?;
load_attestation_key_pool(&mut db, 45, namespace_del1, 0x02)?;
- load_attestation_key_pool(&mut db, expiration_date - 10001, namespace_del2, 0x03)?;
+ load_attestation_key_pool(&mut db, 60, namespace_del2, 0x03)?;
let blob_entry_row_count: u32 = db
.conn
@@ -3661,7 +3562,7 @@ pub mod tests {
let mut cert_chain =
db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace, &KEYSTORE_UUID)?;
assert!(cert_chain.is_some());
- let (_, value) = cert_chain.unwrap();
+ let value = cert_chain.unwrap();
assert_eq!(entry_values.batch_cert, value.batch_cert);
assert_eq!(entry_values.cert_chain, value.cert_chain);
assert_eq!(entry_values.priv_key, value.private_key.to_vec());
@@ -3671,13 +3572,13 @@ pub mod tests {
namespace_del1,
&KEYSTORE_UUID,
)?;
- assert!(cert_chain.is_none());
+ assert!(!cert_chain.is_some());
cert_chain = db.retrieve_attestation_key_and_cert_chain(
Domain::APP,
namespace_del2,
&KEYSTORE_UUID,
)?;
- assert!(cert_chain.is_none());
+ assert!(!cert_chain.is_some());
// Give the garbage collector half a second to catch up.
std::thread::sleep(Duration::from_millis(500));
@@ -3693,73 +3594,6 @@ pub mod tests {
Ok(())
}
- fn compare_rem_prov_values(
- expected: &RemoteProvValues,
- actual: Option<(KeyIdGuard, CertificateChain)>,
- ) {
- assert!(actual.is_some());
- let (_, value) = actual.unwrap();
- assert_eq!(expected.batch_cert, value.batch_cert);
- assert_eq!(expected.cert_chain, value.cert_chain);
- assert_eq!(expected.priv_key, value.private_key.to_vec());
- }
-
- #[test]
- fn test_dont_remove_valid_certs() -> Result<()> {
- let temp_dir =
- TempDir::new("test_remove_expired_certs_").expect("Failed to create temp dir.");
- let mut db = new_test_db_with_gc(temp_dir.path(), |_, _| Ok(()))?;
- let expiration_date: i64 =
- SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64
- + EXPIRATION_BUFFER_MS
- + 10000;
- let namespace1: i64 = 30;
- let namespace2: i64 = 45;
- let namespace3: i64 = 60;
- let entry_values1 = load_attestation_key_pool(
- &mut db,
- expiration_date,
- namespace1,
- 0x01, /* base_byte */
- )?;
- let entry_values2 =
- load_attestation_key_pool(&mut db, expiration_date + 40000, namespace2, 0x02)?;
- let entry_values3 =
- load_attestation_key_pool(&mut db, expiration_date - 9000, namespace3, 0x03)?;
-
- let blob_entry_row_count: u32 = db
- .conn
- .query_row("SELECT COUNT(id) FROM persistent.blobentry;", NO_PARAMS, |row| row.get(0))
- .expect("Failed to get blob entry row count.");
- // We expect 9 rows here because there are three blobs per attestation key, i.e.,
- // one key, one certificate chain, and one certificate.
- assert_eq!(blob_entry_row_count, 9);
-
- let mut cert_chain =
- db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace1, &KEYSTORE_UUID)?;
- compare_rem_prov_values(&entry_values1, cert_chain);
-
- cert_chain =
- db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace2, &KEYSTORE_UUID)?;
- compare_rem_prov_values(&entry_values2, cert_chain);
-
- cert_chain =
- db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace3, &KEYSTORE_UUID)?;
- compare_rem_prov_values(&entry_values3, cert_chain);
-
- // Give the garbage collector half a second to catch up.
- std::thread::sleep(Duration::from_millis(500));
-
- let blob_entry_row_count: u32 = db
- .conn
- .query_row("SELECT COUNT(id) FROM persistent.blobentry;", NO_PARAMS, |row| row.get(0))
- .expect("Failed to get blob entry row count.");
- // There shound be 9 blob entries left, because all three keys are valid with
- // three blobs each.
- assert_eq!(blob_entry_row_count, 9);
-
- Ok(())
- }
#[test]
fn test_delete_all_attestation_keys() -> Result<()> {
let mut db = new_test_db()?;
@@ -3871,8 +3705,8 @@ pub mod tests {
alias: Some("key".to_string()),
blob: None,
};
- const PVEC1: KeyPermSet = key_perm_set![KeyPerm::Use, KeyPerm::GetInfo];
- const PVEC2: KeyPermSet = key_perm_set![KeyPerm::Use];
+ const PVEC1: KeyPermSet = key_perm_set![KeyPerm::use_(), KeyPerm::get_info()];
+ const PVEC2: KeyPermSet = key_perm_set![KeyPerm::use_()];
// Reset totally predictable random number generator in case we
// are not the first test running on this thread.
@@ -4348,7 +4182,7 @@ pub mod tests {
},
1,
2,
- key_perm_set![KeyPerm::Use],
+ key_perm_set![KeyPerm::use_()],
|_k, _av| Ok(()),
)
.unwrap();
@@ -4358,7 +4192,7 @@ pub mod tests {
let (_key_guard, key_entry) = db
.load_key_entry(&granted_key, KeyType::Client, KeyEntryLoadBits::BOTH, 2, |k, av| {
assert_eq!(Domain::GRANT, k.domain);
- assert!(av.unwrap().includes(KeyPerm::Use));
+ assert!(av.unwrap().includes(KeyPerm::use_()));
Ok(())
})
.unwrap();
@@ -4405,7 +4239,7 @@ pub mod tests {
},
OWNER_UID,
GRANTEE_UID,
- key_perm_set![KeyPerm::Use],
+ key_perm_set![KeyPerm::use_()],
|_k, _av| Ok(()),
)
.unwrap();
@@ -4424,7 +4258,7 @@ pub mod tests {
|k, av| {
assert_eq!(Domain::APP, k.domain);
assert_eq!(OWNER_UID as i64, k.nspace);
- assert!(av.unwrap().includes(KeyPerm::Use));
+ assert!(av.unwrap().includes(KeyPerm::use_()));
Ok(())
},
)
@@ -4475,8 +4309,8 @@ pub mod tests {
let mut db = new_test_db()?;
const SOURCE_UID: u32 = 1u32;
const DESTINATION_UID: u32 = 2u32;
- static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
- static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+ static SOURCE_ALIAS: &str = &"SOURCE_ALIAS";
+ static DESTINATION_ALIAS: &str = &"DESTINATION_ALIAS";
let key_id_guard =
make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
.context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
@@ -4544,8 +4378,8 @@ pub mod tests {
const SOURCE_UID: u32 = 1u32;
const DESTINATION_UID: u32 = 2u32;
const DESTINATION_NAMESPACE: i64 = 1000i64;
- static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
- static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+ static SOURCE_ALIAS: &str = &"SOURCE_ALIAS";
+ static DESTINATION_ALIAS: &str = &"DESTINATION_ALIAS";
let key_id_guard =
make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
.context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
@@ -4612,8 +4446,8 @@ pub mod tests {
let mut db = new_test_db()?;
const SOURCE_UID: u32 = 1u32;
const DESTINATION_UID: u32 = 2u32;
- static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
- static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+ static SOURCE_ALIAS: &str = &"SOURCE_ALIAS";
+ static DESTINATION_ALIAS: &str = &"DESTINATION_ALIAS";
let key_id_guard =
make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
.context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
@@ -4645,9 +4479,9 @@ pub mod tests {
#[test]
fn test_upgrade_0_to_1() {
- const ALIAS1: &str = "test_upgrade_0_to_1_1";
- const ALIAS2: &str = "test_upgrade_0_to_1_2";
- const ALIAS3: &str = "test_upgrade_0_to_1_3";
+ const ALIAS1: &str = &"test_upgrade_0_to_1_1";
+ const ALIAS2: &str = &"test_upgrade_0_to_1_2";
+ const ALIAS3: &str = &"test_upgrade_0_to_1_3";
const UID: u32 = 33;
let temp_dir = Arc::new(TempDir::new("test_upgrade_0_to_1").unwrap());
let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
@@ -5132,7 +4966,10 @@ pub mod tests {
Ok(KeyEntryRow {
id: row.get(0)?,
key_type: row.get(1)?,
- domain: row.get::<_, Option<_>>(2)?.map(Domain),
+ domain: match row.get(2)? {
+ Some(i) => Some(Domain(i)),
+ None => None,
+ },
namespace: row.get(3)?,
alias: row.get(4)?,
state: row.get(5)?,
@@ -5623,80 +5460,6 @@ pub mod tests {
}
#[test]
- fn test_unbind_keys_for_user_removes_superkeys() -> Result<()> {
- let mut db = new_test_db()?;
- let super_key = keystore2_crypto::generate_aes256_key()?;
- let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
- let (encrypted_super_key, metadata) =
- SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
-
- let key_name_enc = SuperKeyType {
- alias: "test_super_key_1",
- algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
- };
-
- let key_name_nonenc = SuperKeyType {
- alias: "test_super_key_2",
- algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
- };
-
- // Install two super keys.
- db.store_super_key(
- 1,
- &key_name_nonenc,
- &super_key,
- &BlobMetaData::new(),
- &KeyMetaData::new(),
- )?;
- db.store_super_key(1, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
-
- // Check that both can be found in the database.
- assert!(db.load_super_key(&key_name_enc, 1)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
-
- // Install the same keys for a different user.
- db.store_super_key(
- 2,
- &key_name_nonenc,
- &super_key,
- &BlobMetaData::new(),
- &KeyMetaData::new(),
- )?;
- db.store_super_key(2, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
-
- // Check that the second pair of keys can be found in the database.
- assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
-
- // Delete only encrypted keys.
- db.unbind_keys_for_user(1, true)?;
-
- // The encrypted superkey should be gone now.
- assert!(db.load_super_key(&key_name_enc, 1)?.is_none());
- assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
-
- // Reinsert the encrypted key.
- db.store_super_key(1, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
-
- // Check that both can be found in the database, again..
- assert!(db.load_super_key(&key_name_enc, 1)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
-
- // Delete all even unencrypted keys.
- db.unbind_keys_for_user(1, false)?;
-
- // Both should be gone now.
- assert!(db.load_super_key(&key_name_enc, 1)?.is_none());
- assert!(db.load_super_key(&key_name_nonenc, 1)?.is_none());
-
- // Check that the second pair of keys was untouched.
- assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
-
- Ok(())
- }
-
- #[test]
fn test_store_super_key() -> Result<()> {
let mut db = new_test_db()?;
let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
@@ -5715,8 +5478,8 @@ pub mod tests {
&KeyMetaData::new(),
)?;
- // Check if super key exists.
- assert!(db.key_exists(Domain::APP, 1, USER_SUPER_KEY.alias, KeyType::Super)?);
+ //check if super key exists
+ assert!(db.key_exists(Domain::APP, 1, &USER_SUPER_KEY.alias, KeyType::Super)?);
let (_, key_entry) = db.load_super_key(&USER_SUPER_KEY, 1)?.unwrap();
let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(
@@ -5726,9 +5489,9 @@ pub mod tests {
None,
)?;
- let decrypted_secret_bytes = loaded_super_key.decrypt(&encrypted_secret, &iv, &tag)?;
+ let decrypted_secret_bytes =
+ loaded_super_key.aes_gcm_decrypt(&encrypted_secret, &iv, &tag)?;
assert_eq!(secret_bytes, &*decrypted_secret_bytes);
-
Ok(())
}
@@ -5822,7 +5585,7 @@ pub mod tests {
&& updated_stats[&k].unused_size == baseline[&k].unused_size,
"updated_stats:\n{}\nbaseline:\n{}",
stringify(&updated_stats),
- stringify(baseline)
+ stringify(&baseline)
);
}
}
@@ -5916,7 +5679,7 @@ pub mod tests {
},
OWNER as u32,
123,
- key_perm_set![KeyPerm::Use],
+ key_perm_set![KeyPerm::use_()],
|_, _| Ok(()),
)?;
diff --git a/keystore2/src/database/utils.rs b/keystore2/src/database/utils.rs
index b4590dae..90f56160 100644
--- a/keystore2/src/database/utils.rs
+++ b/keystore2/src/database/utils.rs
@@ -44,7 +44,7 @@ where
loop {
match rows.next().context("In with_rows_extract_all: Failed to unpack row")? {
Some(row) => {
- row_extractor(row).context("In with_rows_extract_all.")?;
+ row_extractor(&row).context("In with_rows_extract_all.")?;
}
None => break Ok(()),
}
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index cb6a2667..29a3f0b0 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -28,13 +28,14 @@ use android_hardware_security_keymint::aidl::android::hardware::security::keymin
KeyParameter::KeyParameter as KmKeyParameter, KeyPurpose::KeyPurpose, Tag::Tag,
};
use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
- TimeStampToken::TimeStampToken,
+ ISecureClock::ISecureClock, TimeStampToken::TimeStampToken,
};
use android_security_authorization::aidl::android::security::authorization::ResponseCode::ResponseCode as AuthzResponseCode;
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, IKeystoreSecurityLevel::KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING,
OperationChallenge::OperationChallenge,
};
+use android_system_keystore2::binder::Strong;
use anyhow::{Context, Result};
use std::{
collections::{HashMap, HashSet},
@@ -218,10 +219,13 @@ impl TokenReceiver {
}
fn get_timestamp_token(challenge: i64) -> Result<TimeStampToken, Error> {
- let dev = get_timestamp_service().expect(concat!(
- "Secure Clock service must be present ",
- "if TimeStampTokens are required."
- ));
+ let dev: Strong<dyn ISecureClock> = get_timestamp_service()
+ .expect(concat!(
+ "Secure Clock service must be present ",
+ "if TimeStampTokens are required."
+ ))
+ .get_interface()
+ .expect("Fatal: Timestamp service does not implement ISecureClock.");
map_binder_status(dev.generateTimeStamp(challenge))
}
@@ -450,7 +454,7 @@ impl Enforcements {
KeyParameterValue::Algorithm(Algorithm::RSA)
| KeyParameterValue::Algorithm(Algorithm::EC) => {
return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE)).context(
- "In authorize_create: public operations on asymmetric keys are not \
+ "In authorize_create: public operations on asymmetric keys are not
supported.",
);
}
@@ -566,7 +570,8 @@ impl Enforcements {
// if both NO_AUTH_REQUIRED and USER_SECURE_ID tags are present, return error
if !user_secure_ids.is_empty() && no_auth_required {
return Err(Error::Km(Ec::INVALID_KEY_BLOB)).context(
- "In authorize_create: key has both NO_AUTH_REQUIRED and USER_SECURE_ID tags.",
+ "In authorize_create: key has both NO_AUTH_REQUIRED
+ and USER_SECURE_ID tags.",
);
}
@@ -575,8 +580,8 @@ impl Enforcements {
|| (user_auth_type.is_none() && !user_secure_ids.is_empty())
{
return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
- "In authorize_create: Auth required, but either auth type or secure ids \
- are not present.",
+ "In authorize_create: Auth required, but either auth type or secure ids
+ are not present.",
);
}
@@ -586,7 +591,8 @@ impl Enforcements {
&& op_params.iter().any(|kp| kp.tag == Tag::NONCE)
{
return Err(Error::Km(Ec::CALLER_NONCE_PROHIBITED)).context(
- "In authorize_create, NONCE is present, although CALLER_NONCE is not present",
+ "In authorize_create, NONCE is present,
+ although CALLER_NONCE is not present",
);
}
@@ -600,7 +606,7 @@ impl Enforcements {
}
if let Some(level) = max_boot_level {
- if !SUPER_KEY.read().unwrap().level_accessible(level) {
+ if !SUPER_KEY.level_accessible(level) {
return Err(Error::Km(Ec::BOOT_LEVEL_EXCEEDED))
.context("In authorize_create: boot level is too late.");
}
@@ -627,7 +633,7 @@ impl Enforcements {
let hat_and_last_off_body = if need_auth_token {
let hat_and_last_off_body = Self::find_auth_token(|hat: &AuthTokenEntry| {
- if let (Some(auth_type), true) = (user_auth_type, timeout_bound) {
+ if let (Some(auth_type), true) = (user_auth_type, has_sids) {
hat.satisfies(&user_secure_ids, auth_type)
} else {
unlocked_device_required
@@ -835,12 +841,8 @@ impl Enforcements {
.context("In get_auth_tokens: No auth token found.");
}
} else {
- return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND)).context(
- concat!(
- "In get_auth_tokens: No auth token found for ",
- "the given challenge and passed-in auth token max age is zero."
- ),
- );
+ return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND))
+ .context("In get_auth_tokens: Passed-in auth token max age is zero.");
}
};
// Wait and obtain the timestamp token from secure clock service.
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index f34c5daa..f969cb6c 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -37,7 +37,6 @@ use android_system_keystore2::binder::{
};
use keystore2_selinux as selinux;
use std::cmp::PartialEq;
-use std::ffi::CString;
/// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
/// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
@@ -67,15 +66,10 @@ impl Error {
Error::Rc(ResponseCode::SYSTEM_ERROR)
}
- /// Short hand for `Error::Rc(ResponseCode::PERMISSION_DENIED)`
+ /// Short hand for `Error::Rc(ResponseCode::PERMISSION_DENIED`
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
@@ -190,20 +184,6 @@ where
)
}
-/// This function turns an anyhow error into an optional CString.
-/// This is especially useful to add a message string to a service specific error.
-/// If the formatted string was not convertible because it contained a nul byte,
-/// None is returned and a warning is logged.
-pub fn anyhow_error_to_cstring(e: &anyhow::Error) -> Option<CString> {
- match CString::new(format!("{:?}", e)) {
- Ok(msg) => Some(msg),
- Err(_) => {
- log::warn!("Cannot convert error message to CStr. It contained a nul byte.");
- None
- }
- }
-}
-
/// 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.
@@ -220,10 +200,7 @@ where
|e| {
let e = map_err(e);
let rc = get_error_code(&e);
- Err(BinderStatus::new_service_specific_error(
- rc,
- anyhow_error_to_cstring(&e).as_deref(),
- ))
+ Err(BinderStatus::new_service_specific_error(rc, None))
},
handle_ok,
)
diff --git a/keystore2/src/fuzzers/Android.bp b/keystore2/src/fuzzers/Android.bp
deleted file mode 100644
index 384ab77f..00000000
--- a/keystore2/src/fuzzers/Android.bp
+++ /dev/null
@@ -1,29 +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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-rust_fuzz {
- name: "legacy_blob_fuzzer",
- srcs: ["legacy_blob_fuzzer.rs"],
- rustlibs: [
- "libkeystore2",
- ],
- fuzz_config: {
- fuzz_on_haiku_device: true,
- fuzz_on_haiku_host: false,
- },
-}
diff --git a/keystore2/src/fuzzers/legacy_blob_fuzzer.rs b/keystore2/src/fuzzers/legacy_blob_fuzzer.rs
deleted file mode 100644
index 7e3e848c..00000000
--- a/keystore2/src/fuzzers/legacy_blob_fuzzer.rs
+++ /dev/null
@@ -1,26 +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.
-
-#![allow(missing_docs)]
-#![no_main]
-#[macro_use]
-extern crate libfuzzer_sys;
-use keystore2::legacy_blob::LegacyBlobLoader;
-
-fuzz_target!(|data: &[u8]| {
- if !data.is_empty() {
- let string = data.iter().filter_map(|c| std::char::from_u32(*c as u32)).collect::<String>();
- let _res = LegacyBlobLoader::decode_alias(&string);
- }
-});
diff --git a/keystore2/src/gc.rs b/keystore2/src/gc.rs
index 341aa0a6..2010c796 100644
--- a/keystore2/src/gc.rs
+++ b/keystore2/src/gc.rs
@@ -27,7 +27,7 @@ use anyhow::{Context, Result};
use async_task::AsyncTask;
use std::sync::{
atomic::{AtomicU8, Ordering},
- Arc, RwLock,
+ Arc,
};
pub struct Gc {
@@ -47,7 +47,7 @@ impl Gc {
F: FnOnce() -> (
Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
KeystoreDB,
- Arc<RwLock<SuperKeyManager>>,
+ Arc<SuperKeyManager>,
) + Send
+ 'static,
{
@@ -87,7 +87,7 @@ struct GcInternal {
invalidate_key: Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
db: KeystoreDB,
async_task: std::sync::Weak<AsyncTask>,
- super_key: Arc<RwLock<SuperKeyManager>>,
+ super_key: Arc<SuperKeyManager>,
notified: Arc<AtomicU8>,
}
@@ -121,11 +121,9 @@ impl GcInternal {
if let Some(uuid) = blob_metadata.km_uuid() {
let blob = self
.super_key
- .read()
- .unwrap()
.unwrap_key_if_required(&blob_metadata, &blob)
.context("In process_one_key: Trying to unwrap to-be-deleted blob.")?;
- (self.invalidate_key)(uuid, &*blob)
+ (self.invalidate_key)(&uuid, &*blob)
.context("In process_one_key: Trying to invalidate key.")?;
}
}
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index 14b36010..8212213e 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -18,24 +18,21 @@
use crate::gc::Gc;
use crate::legacy_blob::LegacyBlobLoader;
-use crate::legacy_importer::LegacyImporter;
+use crate::legacy_migrator::LegacyMigrator;
use crate::super_key::SuperKeyManager;
use crate::utils::watchdog as wd;
+use crate::utils::Asp;
use crate::{async_task::AsyncTask, database::MonotonicRawTime};
use crate::{
database::KeystoreDB,
database::Uuid,
error::{map_binder_status, map_binder_status_code, Error, ErrorCode},
};
-use crate::km_compat::{KeyMintV1, BacklevelKeyMintWrapper};
use crate::{enforcements::Enforcements, error::map_km_error};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
IKeyMintDevice::IKeyMintDevice, IRemotelyProvisionedComponent::IRemotelyProvisionedComponent,
KeyMintHardwareInfo::KeyMintHardwareInfo, SecurityLevel::SecurityLevel,
};
-use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
- ISecureClock::ISecureClock,
-};
use android_hardware_security_keymint::binder::{StatusCode, Strong};
use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
use anyhow::{Context, Result};
@@ -88,33 +85,34 @@ thread_local! {
RefCell::new(create_thread_local_db());
}
-struct DevicesMap<T: FromIBinder + ?Sized> {
- devices_by_uuid: HashMap<Uuid, (Strong<T>, KeyMintHardwareInfo)>,
+#[derive(Default)]
+struct DevicesMap {
+ devices_by_uuid: HashMap<Uuid, (Asp, KeyMintHardwareInfo)>,
uuid_by_sec_level: HashMap<SecurityLevel, Uuid>,
}
-impl<T: FromIBinder + ?Sized> DevicesMap<T> {
+impl DevicesMap {
fn dev_by_sec_level(
&self,
sec_level: &SecurityLevel,
- ) -> Option<(Strong<T>, KeyMintHardwareInfo, Uuid)> {
+ ) -> Option<(Asp, KeyMintHardwareInfo, Uuid)> {
self.uuid_by_sec_level.get(sec_level).and_then(|uuid| self.dev_by_uuid(uuid))
}
- fn dev_by_uuid(&self, uuid: &Uuid) -> Option<(Strong<T>, KeyMintHardwareInfo, Uuid)> {
+ fn dev_by_uuid(&self, uuid: &Uuid) -> Option<(Asp, KeyMintHardwareInfo, Uuid)> {
self.devices_by_uuid
.get(uuid)
.map(|(dev, hw_info)| ((*dev).clone(), (*hw_info).clone(), *uuid))
}
- fn devices(&self) -> Vec<Strong<T>> {
- self.devices_by_uuid.values().map(|(dev, _)| dev.clone()).collect()
+ fn devices<T: FromIBinder + ?Sized>(&self) -> Vec<Strong<T>> {
+ self.devices_by_uuid.values().filter_map(|(asp, _)| asp.get_interface::<T>().ok()).collect()
}
/// The requested security level and the security level of the actual implementation may
/// differ. So we map the requested security level to the uuid of the implementation
/// so that there cannot be any confusion as to which KeyMint instance is requested.
- fn insert(&mut self, sec_level: SecurityLevel, dev: Strong<T>, hw_info: KeyMintHardwareInfo) {
+ fn insert(&mut self, sec_level: SecurityLevel, dev: Asp, hw_info: KeyMintHardwareInfo) {
// For now we use the reported security level of the KM instance as UUID.
// TODO update this section once UUID was added to the KM hardware info.
let uuid: Uuid = sec_level.into();
@@ -123,31 +121,17 @@ impl<T: FromIBinder + ?Sized> DevicesMap<T> {
}
}
-impl<T: FromIBinder + ?Sized> Default for DevicesMap<T> {
- fn default() -> Self {
- Self {
- devices_by_uuid: HashMap::<Uuid, (Strong<T>, KeyMintHardwareInfo)>::new(),
- uuid_by_sec_level: Default::default(),
- }
- }
-}
-
-struct RemotelyProvisionedDevicesMap<T: FromIBinder + ?Sized> {
- devices_by_sec_level: HashMap<SecurityLevel, Strong<T>>,
+#[derive(Default)]
+struct RemotelyProvisionedDevicesMap {
+ devices_by_sec_level: HashMap<SecurityLevel, Asp>,
}
-impl<T: FromIBinder + ?Sized> Default for RemotelyProvisionedDevicesMap<T> {
- fn default() -> Self {
- Self { devices_by_sec_level: HashMap::<SecurityLevel, Strong<T>>::new() }
- }
-}
-
-impl<T: FromIBinder + ?Sized> RemotelyProvisionedDevicesMap<T> {
- fn dev_by_sec_level(&self, sec_level: &SecurityLevel) -> Option<Strong<T>> {
+impl RemotelyProvisionedDevicesMap {
+ fn dev_by_sec_level(&self, sec_level: &SecurityLevel) -> Option<Asp> {
self.devices_by_sec_level.get(sec_level).map(|dev| (*dev).clone())
}
- fn insert(&mut self, sec_level: SecurityLevel, dev: Strong<T>) {
+ fn insert(&mut self, sec_level: SecurityLevel, dev: Asp) {
self.devices_by_sec_level.insert(sec_level, dev);
}
}
@@ -157,15 +141,13 @@ lazy_static! {
pub static ref DB_PATH: RwLock<PathBuf> = RwLock::new(
Path::new("/data/misc/keystore").to_path_buf());
/// Runtime database of unwrapped super keys.
- pub static ref SUPER_KEY: Arc<RwLock<SuperKeyManager>> = Default::default();
+ pub static ref SUPER_KEY: Arc<SuperKeyManager> = Default::default();
/// Map of KeyMint devices.
- static ref KEY_MINT_DEVICES: Mutex<DevicesMap<dyn IKeyMintDevice>> = Default::default();
+ static ref KEY_MINT_DEVICES: Mutex<DevicesMap> = Default::default();
/// Timestamp service.
- static ref TIME_STAMP_DEVICE: Mutex<Option<Strong<dyn ISecureClock>>> = Default::default();
+ static ref TIME_STAMP_DEVICE: Mutex<Option<Asp>> = Default::default();
/// RemotelyProvisionedComponent HAL devices.
- static ref REMOTELY_PROVISIONED_COMPONENT_DEVICES:
- Mutex<RemotelyProvisionedDevicesMap<dyn IRemotelyProvisionedComponent>> =
- Default::default();
+ static ref REMOTELY_PROVISIONED_COMPONENT_DEVICES: Mutex<RemotelyProvisionedDevicesMap> = Default::default();
/// A single on-demand worker thread that handles deferred tasks with two different
/// priorities.
pub static ref ASYNC_TASK: Arc<AsyncTask> = Default::default();
@@ -176,15 +158,16 @@ lazy_static! {
pub static ref LEGACY_BLOB_LOADER: Arc<LegacyBlobLoader> = Arc::new(LegacyBlobLoader::new(
&DB_PATH.read().expect("Could not get the database path for legacy blob loader.")));
/// Legacy migrator. Atomically migrates legacy blobs to the database.
- pub static ref LEGACY_IMPORTER: Arc<LegacyImporter> =
- Arc::new(LegacyImporter::new(Arc::new(Default::default())));
+ pub static ref LEGACY_MIGRATOR: Arc<LegacyMigrator> =
+ Arc::new(LegacyMigrator::new(Arc::new(Default::default())));
/// Background thread which handles logging via statsd and logd
pub static ref LOGS_HANDLER: Arc<AsyncTask> = Default::default();
static ref GC: Arc<Gc> = Arc::new(Gc::new_init_with(ASYNC_TASK.clone(), || {
(
Box::new(|uuid, blob| {
- let km_dev = get_keymint_dev_by_uuid(uuid).map(|(dev, _)| dev)?;
+ let km_dev: Strong<dyn IKeyMintDevice> =
+ get_keymint_dev_by_uuid(uuid).map(|(dev, _)| dev)?.get_interface()?;
let _wp = wd::watch_millis("In invalidate key closure: calling deleteKey", 500);
map_km_error(km_dev.deleteKey(&*blob))
.context("In invalidate key closure: Trying to invalidate key blob.")
@@ -198,70 +181,40 @@ lazy_static! {
static KEYMINT_SERVICE_NAME: &str = "android.hardware.security.keymint.IKeyMintDevice";
-/// Determine the service name for a KeyMint device of the given security level
-/// which implements at least the specified version of the `IKeyMintDevice`
-/// interface.
-fn keymint_service_name_by_version(
- security_level: &SecurityLevel,
- version: i32,
-) -> Result<Option<(i32, String)>> {
+/// Make a new connection to a KeyMint device of the given security level.
+/// If no native KeyMint device can be found this function also brings
+/// up the compatibility service and attempts to connect to the legacy wrapper.
+fn connect_keymint(security_level: &SecurityLevel) -> Result<(Asp, KeyMintHardwareInfo)> {
let keymint_instances =
- get_aidl_instances("android.hardware.security.keymint", version as usize, "IKeyMintDevice");
+ get_aidl_instances("android.hardware.security.keymint", 1, "IKeyMintDevice");
let service_name = match *security_level {
SecurityLevel::TRUSTED_ENVIRONMENT => {
- if keymint_instances.iter().any(|instance| *instance == "default") {
+ if keymint_instances.as_vec()?.iter().any(|instance| *instance == "default") {
Some(format!("{}/default", KEYMINT_SERVICE_NAME))
} else {
None
}
}
SecurityLevel::STRONGBOX => {
- if keymint_instances.iter().any(|instance| *instance == "strongbox") {
+ if keymint_instances.as_vec()?.iter().any(|instance| *instance == "strongbox") {
Some(format!("{}/strongbox", KEYMINT_SERVICE_NAME))
} else {
None
}
}
_ => {
- return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)).context(format!(
- "In keymint_service_name_by_version: Trying to find keymint V{} for security level: {:?}",
- version, security_level
- ));
+ return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
+ .context("In connect_keymint.")
}
};
- Ok(service_name.map(|service_name| (version, service_name)))
-}
-
-/// Make a new connection to a KeyMint device of the given security level.
-/// If no native KeyMint device can be found this function also brings
-/// up the compatibility service and attempts to connect to the legacy wrapper.
-fn connect_keymint(
- security_level: &SecurityLevel,
-) -> Result<(Strong<dyn IKeyMintDevice>, KeyMintHardwareInfo)> {
- // Count down from the current interface version back to one in order to
- // also find out the interface version -- an implementation of V2 will show
- // up in the list of V1-capable devices, but not vice-versa.
- let service_name = keymint_service_name_by_version(security_level, 2)
- .and_then(|sl| {
- if sl.is_none() {
- keymint_service_name_by_version(security_level, 1)
- } else {
- Ok(sl)
- }
- })
- .context("In connect_keymint.")?;
-
- let (keymint, hal_version) = if let Some((version, service_name)) = service_name {
- let km: Strong<dyn IKeyMintDevice> =
+ let (keymint, hal_version) = if let Some(service_name) = service_name {
+ (
map_binder_status_code(binder::get_interface(&service_name))
- .context("In connect_keymint: Trying to connect to genuine KeyMint service.")?;
- // Map the HAL version code for KeyMint to be <AIDL version> * 100, so
- // - V1 is 100
- // - V2 is 200
- // etc.
- (km, Some(version * 100))
+ .context("In connect_keymint: Trying to connect to genuine KeyMint service.")?,
+ Some(100i32), // The HAL version code for KeyMint V1 is 100.
+ )
} else {
// This is a no-op if it was called before.
keystore2_km_compat::add_keymint_device_service();
@@ -282,48 +235,6 @@ fn connect_keymint(
)
};
- // If the KeyMint device is back-level, use a wrapper that intercepts and
- // emulates things that are not supported by the hardware.
- let keymint = match hal_version {
- Some(200) => {
- // Current KeyMint version: use as-is.
- log::info!(
- "KeyMint device is current version ({:?}) for security level: {:?}",
- hal_version,
- security_level
- );
- keymint
- }
- Some(100) => {
- // KeyMint v1: perform software emulation.
- log::info!(
- "Add emulation wrapper around {:?} device for security level: {:?}",
- hal_version,
- security_level
- );
- BacklevelKeyMintWrapper::wrap(KeyMintV1::new(*security_level), keymint)
- .context("In connect_keymint: Trying to create V1 compatibility wrapper.")?
- }
- None => {
- // Compatibility wrapper around a KeyMaster device: this roughly
- // behaves like KeyMint V1 (e.g. it includes AGREE_KEY support,
- // albeit in software.)
- log::info!(
- "Add emulation wrapper around Keymaster device for security level: {:?}",
- security_level
- );
- BacklevelKeyMintWrapper::wrap(KeyMintV1::new(*security_level), keymint).context(
- "In connect_keymint: Trying to create km_compat V1 compatibility wrapper .",
- )?
- }
- _ => {
- return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)).context(format!(
- "In connect_keymint: unexpected hal_version {:?} for security level: {:?}",
- hal_version, security_level
- ))
- }
- };
-
let wp = wd::watch_millis("In connect_keymint: calling getHardwareInfo()", 500);
let mut hw_info = map_km_error(keymint.getHardwareInfo())
.context("In connect_keymint: Failed to get hardware info.")?;
@@ -331,19 +242,16 @@ fn connect_keymint(
// The legacy wrapper sets hw_info.versionNumber to the underlying HAL version like so:
// 10 * <major> + <minor>, e.g., KM 3.0 = 30. So 30, 40, and 41 are the only viable values.
- //
- // For KeyMint the returned versionNumber is implementation defined and thus completely
- // meaningless to Keystore 2.0. So set the versionNumber field that is returned to
- // the rest of the code to be the <AIDL version> * 100, so KeyMint V1 is 100, KeyMint V2 is 200
- // and so on.
- //
- // This ensures that versionNumber value across KeyMaster and KeyMint is monotonically
- // increasing (and so comparisons like `versionNumber >= KEY_MINT_1` are valid).
+ // For KeyMint the versionNumber is implementation defined and thus completely meaningless
+ // to Keystore 2.0. So at this point the versionNumber field is set to the HAL version, so
+ // that higher levels have a meaningful guide as to which feature set to expect from the
+ // implementation. As of this writing the only meaningful version number is 100 for KeyMint V1,
+ // and future AIDL versions should follow the pattern <AIDL version> * 100.
if let Some(hal_version) = hal_version {
hw_info.versionNumber = hal_version;
}
- Ok((keymint, hw_info))
+ Ok((Asp::new(keymint.as_binder()), hw_info))
}
/// Get a keymint device for the given security level either from our cache or
@@ -351,9 +259,9 @@ fn connect_keymint(
/// TODO the latter can be removed when the uuid is part of the hardware info.
pub fn get_keymint_device(
security_level: &SecurityLevel,
-) -> Result<(Strong<dyn IKeyMintDevice>, KeyMintHardwareInfo, Uuid)> {
+) -> Result<(Asp, KeyMintHardwareInfo, Uuid)> {
let mut devices_map = KEY_MINT_DEVICES.lock().unwrap();
- if let Some((dev, hw_info, uuid)) = devices_map.dev_by_sec_level(security_level) {
+ if let Some((dev, hw_info, uuid)) = devices_map.dev_by_sec_level(&security_level) {
Ok((dev, hw_info, uuid))
} else {
let (dev, hw_info) = connect_keymint(security_level).context("In get_keymint_device.")?;
@@ -367,9 +275,7 @@ pub fn get_keymint_device(
/// attempt to establish a new connection. It is assumed that the cache is already populated
/// when this is called. This is a fair assumption, because service.rs iterates through all
/// security levels when it gets instantiated.
-pub fn get_keymint_dev_by_uuid(
- uuid: &Uuid,
-) -> Result<(Strong<dyn IKeyMintDevice>, KeyMintHardwareInfo)> {
+pub fn get_keymint_dev_by_uuid(uuid: &Uuid) -> Result<(Asp, KeyMintHardwareInfo)> {
let devices_map = KEY_MINT_DEVICES.lock().unwrap();
if let Some((dev, hw_info, _)) = devices_map.dev_by_uuid(uuid) {
Ok((dev, hw_info))
@@ -388,12 +294,12 @@ static TIME_STAMP_SERVICE_NAME: &str = "android.hardware.security.secureclock.IS
/// Make a new connection to a secure clock service.
/// If no native SecureClock device can be found brings up the compatibility service and attempts
/// to connect to the legacy wrapper.
-fn connect_secureclock() -> Result<Strong<dyn ISecureClock>> {
+fn connect_secureclock() -> Result<Asp> {
let secureclock_instances =
get_aidl_instances("android.hardware.security.secureclock", 1, "ISecureClock");
let secure_clock_available =
- secureclock_instances.iter().any(|instance| *instance == "default");
+ secureclock_instances.as_vec()?.iter().any(|instance| *instance == "default");
let default_time_stamp_service_name = format!("{}/default", TIME_STAMP_SERVICE_NAME);
@@ -419,12 +325,12 @@ fn connect_secureclock() -> Result<Strong<dyn ISecureClock>> {
.context("In connect_secureclock: Trying to get Legacy wrapper.")
}?;
- Ok(secureclock)
+ Ok(Asp::new(secureclock.as_binder()))
}
/// Get the timestamp service that verifies auth token timeliness towards security levels with
/// different clocks.
-pub fn get_timestamp_service() -> Result<Strong<dyn ISecureClock>> {
+pub fn get_timestamp_service() -> Result<Asp> {
let mut ts_device = TIME_STAMP_DEVICE.lock().unwrap();
if let Some(dev) = &*ts_device {
Ok(dev.clone())
@@ -438,22 +344,20 @@ pub fn get_timestamp_service() -> Result<Strong<dyn ISecureClock>> {
static REMOTE_PROVISIONING_HAL_SERVICE_NAME: &str =
"android.hardware.security.keymint.IRemotelyProvisionedComponent";
-fn connect_remotely_provisioned_component(
- security_level: &SecurityLevel,
-) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
+fn connect_remotely_provisioned_component(security_level: &SecurityLevel) -> Result<Asp> {
let remotely_prov_instances =
get_aidl_instances("android.hardware.security.keymint", 1, "IRemotelyProvisionedComponent");
let service_name = match *security_level {
SecurityLevel::TRUSTED_ENVIRONMENT => {
- if remotely_prov_instances.iter().any(|instance| *instance == "default") {
+ if remotely_prov_instances.as_vec()?.iter().any(|instance| *instance == "default") {
Some(format!("{}/default", REMOTE_PROVISIONING_HAL_SERVICE_NAME))
} else {
None
}
}
SecurityLevel::STRONGBOX => {
- if remotely_prov_instances.iter().any(|instance| *instance == "strongbox") {
+ if remotely_prov_instances.as_vec()?.iter().any(|instance| *instance == "strongbox") {
Some(format!("{}/strongbox", REMOTE_PROVISIONING_HAL_SERVICE_NAME))
} else {
None
@@ -471,16 +375,14 @@ fn connect_remotely_provisioned_component(
" RemotelyProvisionedComponent service."
))
.map_err(|e| e)?;
- Ok(rem_prov_hal)
+ Ok(Asp::new(rem_prov_hal.as_binder()))
}
/// Get a remote provisiong component device for the given security level either from the cache or
/// by making a new connection. Returns the device.
-pub fn get_remotely_provisioned_component(
- security_level: &SecurityLevel,
-) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
+pub fn get_remotely_provisioned_component(security_level: &SecurityLevel) -> Result<Asp> {
let mut devices_map = REMOTELY_PROVISIONED_COMPONENT_DEVICES.lock().unwrap();
- if let Some(dev) = devices_map.dev_by_sec_level(security_level) {
+ if let Some(dev) = devices_map.dev_by_sec_level(&security_level) {
Ok(dev)
} else {
let dev = connect_remotely_provisioned_component(security_level)
diff --git a/keystore2/src/id_rotation.rs b/keystore2/src/id_rotation.rs
index e3992d89..dbf0fc9b 100644
--- a/keystore2/src/id_rotation.rs
+++ b/keystore2/src/id_rotation.rs
@@ -27,7 +27,7 @@ use std::path::{Path, PathBuf};
use std::time::Duration;
const ID_ROTATION_PERIOD: Duration = Duration::from_secs(30 * 24 * 60 * 60); // Thirty days.
-static TIMESTAMP_FILE_NAME: &str = "timestamp";
+static TIMESTAMP_FILE_NAME: &str = &"timestamp";
/// The IdRotationState stores the path to the timestamp file for deferred usage. The data
/// partition is usually not available when Keystore 2.0 starts up. So this object is created
@@ -83,7 +83,7 @@ mod test {
fn test_had_factory_reset_since_id_rotation() -> Result<()> {
let temp_dir = TempDir::new("test_had_factory_reset_since_id_rotation_")
.expect("Failed to create temp dir.");
- let id_rotation_state = IdRotationState::new(temp_dir.path());
+ let id_rotation_state = IdRotationState::new(&temp_dir.path());
let mut temp_file_path = temp_dir.path().to_owned();
temp_file_path.push(TIMESTAMP_FILE_NAME);
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
index 9854974d..771d609c 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -107,9 +107,6 @@ use android_system_keystore2::aidl::android::system::keystore2::Authorization::A
use anyhow::{Context, Result};
use rusqlite::types::{Null, ToSql, ToSqlOutput};
use rusqlite::Result as SqlResult;
-use serde::de::Deserializer;
-use serde::ser::Serializer;
-use serde::{Deserialize, Serialize};
/// This trait is used to associate a primitive to any type that can be stored inside a
/// KeyParameterValue, especially the AIDL enum types, e.g., keymint::{Algorithm, Digest, ...}.
@@ -124,7 +121,7 @@ use serde::{Deserialize, Serialize};
/// there is no wrapped type):
/// `KeyParameterValue::$vname(<$vtype>::from_primitive(row.get(0)))`
trait AssociatePrimitive {
- type Primitive: Into<Primitive> + TryFrom<Primitive>;
+ type Primitive;
fn from_primitive(v: Self::Primitive) -> Self;
fn to_primitive(&self) -> Self::Primitive;
@@ -180,7 +177,6 @@ implement_associate_primitive_identity! {i32}
/// This enum allows passing a primitive value to `KeyParameterValue::new_from_tag_primitive_pair`
/// Usually, it is not necessary to use this type directly because the function uses
/// `Into<Primitive>` as a trait bound.
-#[derive(Deserialize, Serialize)]
pub enum Primitive {
/// Wraps an i64.
I64(i64),
@@ -217,57 +213,37 @@ pub enum PrimitiveError {
UnknownTag,
}
-impl TryFrom<Primitive> for i64 {
+impl TryInto<i64> for Primitive {
type Error = PrimitiveError;
- fn try_from(p: Primitive) -> Result<i64, Self::Error> {
- match p {
- Primitive::I64(v) => Ok(v),
+ fn try_into(self) -> Result<i64, Self::Error> {
+ match self {
+ Self::I64(v) => Ok(v),
_ => Err(Self::Error::TypeMismatch),
}
}
}
-impl TryFrom<Primitive> for i32 {
+impl TryInto<i32> for Primitive {
type Error = PrimitiveError;
- fn try_from(p: Primitive) -> Result<i32, Self::Error> {
- match p {
- Primitive::I32(v) => Ok(v),
+ fn try_into(self) -> Result<i32, Self::Error> {
+ match self {
+ Self::I32(v) => Ok(v),
_ => Err(Self::Error::TypeMismatch),
}
}
}
-impl TryFrom<Primitive> for Vec<u8> {
+impl TryInto<Vec<u8>> for Primitive {
type Error = PrimitiveError;
- fn try_from(p: Primitive) -> Result<Vec<u8>, Self::Error> {
- match p {
- Primitive::Vec(v) => Ok(v),
+ fn try_into(self) -> Result<Vec<u8>, Self::Error> {
+ match self {
+ Self::Vec(v) => Ok(v),
_ => Err(Self::Error::TypeMismatch),
}
}
}
-fn serialize_primitive<S, P>(v: &P, serializer: S) -> Result<S::Ok, S::Error>
-where
- S: Serializer,
- P: AssociatePrimitive,
-{
- let primitive: Primitive = v.to_primitive().into();
- primitive.serialize(serializer)
-}
-
-fn deserialize_primitive<'de, D, T>(deserializer: D) -> Result<T, D::Error>
-where
- D: Deserializer<'de>,
- T: AssociatePrimitive,
-{
- let primitive: Primitive = serde::de::Deserialize::deserialize(deserializer)?;
- Ok(T::from_primitive(
- primitive.try_into().map_err(|_| serde::de::Error::custom("Type Mismatch"))?,
- ))
-}
-
/// Expands the list of KeyParameterValue variants as follows:
///
/// Input:
@@ -787,14 +763,6 @@ macro_rules! implement_key_parameter_value {
value: KmKeyParameterValue::$field_name(Default::default())}
),*]
}
-
- #[cfg(test)]
- fn make_key_parameter_defaults_vector() -> Vec<KeyParameter> {
- vec![$(KeyParameter{
- value: KeyParameterValue::$vname$((<$vtype as Default>::default()))?,
- security_level: SecurityLevel(100),
- }),*]
- }
}
implement_try_from_to_km_parameter!(
@@ -809,37 +777,27 @@ macro_rules! implement_key_parameter_value {
implement_key_parameter_value! {
/// KeyParameterValue holds a value corresponding to one of the Tags defined in
/// the AIDL spec at hardware/interfaces/security/keymint
-#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize)]
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum KeyParameterValue {
/// Associated with Tag:INVALID
#[key_param(tag = INVALID, field = Invalid)]
Invalid,
/// Set of purposes for which the key may be used
- #[serde(deserialize_with = "deserialize_primitive")]
- #[serde(serialize_with = "serialize_primitive")]
#[key_param(tag = PURPOSE, field = KeyPurpose)]
KeyPurpose(KeyPurpose),
/// Cryptographic algorithm with which the key is used
- #[serde(deserialize_with = "deserialize_primitive")]
- #[serde(serialize_with = "serialize_primitive")]
#[key_param(tag = ALGORITHM, field = Algorithm)]
Algorithm(Algorithm),
/// Size of the key , in bits
#[key_param(tag = KEY_SIZE, field = Integer)]
KeySize(i32),
/// Block cipher mode(s) with which the key may be used
- #[serde(deserialize_with = "deserialize_primitive")]
- #[serde(serialize_with = "serialize_primitive")]
#[key_param(tag = BLOCK_MODE, field = BlockMode)]
BlockMode(BlockMode),
/// Digest algorithms that may be used with the key to perform signing and verification
- #[serde(deserialize_with = "deserialize_primitive")]
- #[serde(serialize_with = "serialize_primitive")]
#[key_param(tag = DIGEST, field = Digest)]
Digest(Digest),
/// Padding modes that may be used with the key. Relevant to RSA, AES and 3DES keys.
- #[serde(deserialize_with = "deserialize_primitive")]
- #[serde(serialize_with = "serialize_primitive")]
#[key_param(tag = PADDING, field = PaddingMode)]
PaddingMode(PaddingMode),
/// Can the caller provide a nonce for nonce-requiring operations
@@ -849,8 +807,6 @@ pub enum KeyParameterValue {
#[key_param(tag = MIN_MAC_LENGTH, field = Integer)]
MinMacLength(i32),
/// The elliptic curve
- #[serde(deserialize_with = "deserialize_primitive")]
- #[serde(serialize_with = "serialize_primitive")]
#[key_param(tag = EC_CURVE, field = EcCurve)]
EcCurve(EcCurve),
/// Value of the public exponent for an RSA key pair
@@ -900,8 +856,6 @@ pub enum KeyParameterValue {
#[key_param(tag = NO_AUTH_REQUIRED, field = BoolValue)]
NoAuthRequired,
/// The types of user authenticators that may be used to authorize this key
- #[serde(deserialize_with = "deserialize_primitive")]
- #[serde(serialize_with = "serialize_primitive")]
#[key_param(tag = USER_AUTH_TYPE, field = HardwareAuthenticatorType)]
HardwareAuthenticatorType(HardwareAuthenticatorType),
/// The time in seconds for which the key is authorized for use, after user authentication
@@ -932,8 +886,6 @@ pub enum KeyParameterValue {
#[key_param(tag = CREATION_DATETIME, field = DateTime)]
CreationDateTime(i64),
/// Specifies where the key was created, if known
- #[serde(deserialize_with = "deserialize_primitive")]
- #[serde(serialize_with = "serialize_primitive")]
#[key_param(tag = ORIGIN, field = Origin)]
KeyOrigin(KeyOrigin),
/// The key used by verified boot to validate the operating system booted
@@ -1029,11 +981,9 @@ impl From<&KmKeyParameter> for KeyParameterValue {
}
/// KeyParameter wraps the KeyParameterValue and the security level at which it is enforced.
-#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct KeyParameter {
value: KeyParameterValue,
- #[serde(deserialize_with = "deserialize_primitive")]
- #[serde(serialize_with = "serialize_primitive")]
security_level: SecurityLevel,
}
@@ -1156,18 +1106,6 @@ mod generated_key_parameter_tests {
fn key_parameter_value_field_matches_tag_type() {
check_field_matches_tag_type(&KeyParameterValue::make_field_matches_tag_type_test_vector());
}
-
- #[test]
- fn key_parameter_serialization_test() {
- let params = KeyParameterValue::make_key_parameter_defaults_vector();
- let mut out_buffer: Vec<u8> = Default::default();
- serde_cbor::to_writer(&mut out_buffer, &params)
- .expect("Failed to serialize key parameters.");
- let deserialized_params: Vec<KeyParameter> =
- serde_cbor::from_reader(&mut out_buffer.as_slice())
- .expect("Failed to deserialize key parameters.");
- assert_eq!(params, deserialized_params);
- }
}
#[cfg(test)]
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 55f5d152..cf2ba043 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -19,24 +19,19 @@ use keystore2::globals::ENFORCEMENTS;
use keystore2::maintenance::Maintenance;
use keystore2::metrics::Metrics;
use keystore2::metrics_store;
-use keystore2::remote_provisioning::{
- RemoteProvisioningService, RemotelyProvisionedKeyPoolService,
-};
+use keystore2::remote_provisioning::RemoteProvisioningService;
use keystore2::service::KeystoreService;
use keystore2::{apc::ApcManager, shared_secret_negotiation};
use keystore2::{authorization::AuthorizationManager, id_rotation::IdRotationState};
use legacykeystore::LegacyKeystore;
use log::{error, info};
-use rusqlite::trace as sqlite_trace;
-use std::{os::raw::c_int, panic, path::Path, sync::mpsc::channel};
+use std::{panic, path::Path, sync::mpsc::channel};
static KS2_SERVICE_NAME: &str = "android.system.keystore2.IKeystoreService/default";
static APC_SERVICE_NAME: &str = "android.security.apc";
static AUTHORIZATION_SERVICE_NAME: &str = "android.security.authorization";
static METRICS_SERVICE_NAME: &str = "android.security.metrics";
static REMOTE_PROVISIONING_SERVICE_NAME: &str = "android.security.remoteprovisioning";
-static REMOTELY_PROVISIONED_KEY_POOL_SERVICE_NAME: &str =
- "android.security.remoteprovisioning.IRemotelyProvisionedKeyPool";
static USER_MANAGER_SERVICE_NAME: &str = "android.security.maintenance";
static LEGACY_KEYSTORE_SERVICE_NAME: &str = "android.security.legacykeystore";
@@ -44,10 +39,7 @@ static LEGACY_KEYSTORE_SERVICE_NAME: &str = "android.security.legacykeystore";
fn main() {
// Initialize android logging.
android_logger::init_once(
- android_logger::Config::default()
- .with_tag("keystore2")
- .with_min_level(log::Level::Debug)
- .with_log_id(android_logger::LogId::System),
+ android_logger::Config::default().with_tag("keystore2").with_min_level(log::Level::Debug),
);
// Redirect panic messages to logcat.
panic::set_hook(Box::new(|panic_info| {
@@ -60,14 +52,6 @@ fn main() {
let mut args = std::env::args();
args.next().expect("That's odd. How is there not even a first argument?");
- // This must happen early before any other sqlite operations.
- log::info!("Setting up sqlite logging for keystore2");
- fn sqlite_log_handler(err: c_int, message: &str) {
- log::error!("[SQLITE3] {}: {}", err, message);
- }
- unsafe { sqlite_trace::config_log(Some(sqlite_log_handler)) }
- .expect("Error setting sqlite log callback.");
-
// Write/update keystore.crash_count system property.
metrics_store::update_keystore_crash_sysprop();
@@ -79,7 +63,7 @@ fn main() {
let db_path = Path::new(&dir);
*keystore2::globals::DB_PATH.write().expect("Could not lock DB_PATH.") =
db_path.to_path_buf();
- IdRotationState::new(db_path)
+ IdRotationState::new(&db_path)
} else {
panic!("Must specify a database directory.");
};
@@ -152,25 +136,6 @@ fn main() {
});
}
- // Even if the IRemotelyProvisionedComponent HAL is implemented, it doesn't mean that the keys
- // may be fetched via the key pool. The HAL must be a new version that exports a unique id. If
- // none of the HALs support this, then the key pool service is not published.
- match RemotelyProvisionedKeyPoolService::new_native_binder() {
- Ok(key_pool_service) => {
- binder::add_service(
- REMOTELY_PROVISIONED_KEY_POOL_SERVICE_NAME,
- key_pool_service.as_binder(),
- )
- .unwrap_or_else(|e| {
- panic!(
- "Failed to register service {} because of {:?}.",
- REMOTELY_PROVISIONED_KEY_POOL_SERVICE_NAME, e
- );
- });
- }
- Err(e) => log::info!("Not publishing IRemotelyProvisionedKeyPool service: {:?}", e),
- }
-
binder::add_service(LEGACY_KEYSTORE_SERVICE_NAME, legacykeystore.as_binder()).unwrap_or_else(
|e| {
panic!(
diff --git a/keystore2/src/km_compat.rs b/keystore2/src/km_compat.rs
deleted file mode 100644
index 788beefe..00000000
--- a/keystore2/src/km_compat.rs
+++ /dev/null
@@ -1,588 +0,0 @@
-// Copyright 2020, 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.
-
-//! Provide a wrapper around a KeyMint device that allows up-level features to
-//! be emulated on back-level devices.
-
-use crate::error::{map_binder_status, map_binder_status_code, map_or_log_err, Error, ErrorCode};
-use android_hardware_security_keymint::binder::{BinderFeatures, StatusCode, Strong};
-use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::TimeStampToken::TimeStampToken;
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- AttestationKey::AttestationKey, BeginResult::BeginResult, EcCurve::EcCurve,
- HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::BnKeyMintDevice,
- IKeyMintDevice::IKeyMintDevice, KeyCharacteristics::KeyCharacteristics,
- KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat,
- KeyMintHardwareInfo::KeyMintHardwareInfo, KeyParameter::KeyParameter,
- KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
- Tag::Tag,
-};
-use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
-use anyhow::Context;
-use keystore2_crypto::{hmac_sha256, HMAC_SHA256_LEN};
-
-/// Key data associated with key generation/import.
-#[derive(Debug, PartialEq, Eq)]
-pub enum KeyImportData<'a> {
- None,
- Pkcs8(&'a [u8]),
- Raw(&'a [u8]),
-}
-
-impl<'a> KeyImportData<'a> {
- /// Translate import parameters into a `KeyImportData` instance.
- fn new(key_format: KeyFormat, key_data: &'a [u8]) -> binder::Result<Self> {
- match key_format {
- KeyFormat::PKCS8 => Ok(KeyImportData::Pkcs8(key_data)),
- KeyFormat::RAW => Ok(KeyImportData::Raw(key_data)),
- _ => Err(binder::Status::new_service_specific_error(
- ErrorCode::UNSUPPORTED_KEY_FORMAT.0,
- None,
- )),
- }
- }
-}
-
-/// A key blob that may be software-emulated or may be directly produced by an
-/// underlying device. In either variant the inner data is the keyblob itself,
-/// as seen by the relevant device.
-#[derive(Debug, PartialEq, Eq)]
-pub enum KeyBlob<'a> {
- Raw(&'a [u8]),
- Wrapped(&'a [u8]),
-}
-
-/// Trait for detecting that software emulation of a current-version KeyMint
-/// feature is required for a back-level KeyMint implementation.
-pub trait EmulationDetector: Send + Sync {
- /// Indicate whether software emulation is required for key
- /// generation/import using the provided parameters.
- fn emulation_required(&self, params: &[KeyParameter], import_data: &KeyImportData) -> bool;
-}
-
-const KEYBLOB_PREFIX: &[u8] = b"SoftKeyMintForV1Blob";
-const KEYBLOB_HMAC_KEY: &[u8] = b"SoftKeyMintForV1HMACKey";
-
-/// Wrap the provided keyblob:
-/// - prefix it with an identifier specific to this wrapper
-/// - suffix it with an HMAC tag, using the [`KEYBLOB_HMAC_KEY`] and `keyblob`.
-fn wrap_keyblob(keyblob: &[u8]) -> anyhow::Result<Vec<u8>> {
- let mut result = Vec::with_capacity(KEYBLOB_PREFIX.len() + keyblob.len() + HMAC_SHA256_LEN);
- result.extend_from_slice(KEYBLOB_PREFIX);
- result.extend_from_slice(keyblob);
- let tag = hmac_sha256(KEYBLOB_HMAC_KEY, keyblob)
- .context("In wrap_keyblob, failed to calculate HMAC-SHA256")?;
- result.extend_from_slice(&tag);
- Ok(result)
-}
-
-/// Return an unwrapped version of the provided `keyblob`, which may or may
-/// not be associated with the software emulation.
-fn unwrap_keyblob(keyblob: &[u8]) -> KeyBlob {
- if !keyblob.starts_with(KEYBLOB_PREFIX) {
- return KeyBlob::Raw(keyblob);
- }
- let without_prefix = &keyblob[KEYBLOB_PREFIX.len()..];
- if without_prefix.len() < HMAC_SHA256_LEN {
- return KeyBlob::Raw(keyblob);
- }
- let (inner_keyblob, want_tag) = without_prefix.split_at(without_prefix.len() - HMAC_SHA256_LEN);
- let got_tag = match hmac_sha256(KEYBLOB_HMAC_KEY, inner_keyblob) {
- Ok(tag) => tag,
- Err(e) => {
- log::error!("Error calculating HMAC-SHA256 for keyblob unwrap: {:?}", e);
- return KeyBlob::Raw(keyblob);
- }
- };
- // Comparison does not need to be constant-time here.
- if want_tag == got_tag {
- KeyBlob::Wrapped(inner_keyblob)
- } else {
- KeyBlob::Raw(keyblob)
- }
-}
-
-/// Wrapper around a real device that implements a back-level version of
-/// `IKeyMintDevice`
-pub struct BacklevelKeyMintWrapper<T: EmulationDetector> {
- /// The `real` device implements some earlier version of `IKeyMintDevice`
- real: Strong<dyn IKeyMintDevice>,
- /// The `soft`ware device implements the current version of `IKeyMintDevice`
- soft: Strong<dyn IKeyMintDevice>,
- /// Detector for operations that are not supported by the earlier version of
- /// `IKeyMintDevice`. Or possibly a large flightless bird, who can tell.
- emu: T,
-}
-
-impl<T> BacklevelKeyMintWrapper<T>
-where
- T: EmulationDetector + 'static,
-{
- /// Create a wrapper around the provided back-level KeyMint device, so that
- /// software emulation can be performed for any current-version features not
- /// provided by the real device.
- pub fn wrap(
- emu: T,
- real: Strong<dyn IKeyMintDevice>,
- ) -> anyhow::Result<Strong<dyn IKeyMintDevice>> {
- // This is a no-op if it was called before.
- keystore2_km_compat::add_keymint_device_service();
-
- let keystore_compat_service: Strong<dyn IKeystoreCompatService> = map_binder_status_code(
- binder::get_interface("android.security.compat"),
- )
- .context("In BacklevelKeyMintWrapper::wrap: Trying to connect to compat service.")?;
- let soft =
- map_binder_status(keystore_compat_service.getKeyMintDevice(SecurityLevel::SOFTWARE))
- .map_err(|e| match e {
- Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
- Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
- }
- e => e,
- })
- .context("In BacklevelKeyMintWrapper::wrap: Trying to get software device.")?;
-
- Ok(BnKeyMintDevice::new_binder(
- Self { real, soft, emu },
- BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
- ))
- }
-}
-
-impl<T> binder::Interface for BacklevelKeyMintWrapper<T> where T: EmulationDetector {}
-
-impl<T> IKeyMintDevice for BacklevelKeyMintWrapper<T>
-where
- T: EmulationDetector + 'static,
-{
- // For methods that don't involve keyblobs, forward to either the real
- // device, or to both real & emulated devices.
- fn getHardwareInfo(&self) -> binder::Result<KeyMintHardwareInfo> {
- self.real.getHardwareInfo()
- }
- fn addRngEntropy(&self, data: &[u8]) -> binder::Result<()> {
- self.real.addRngEntropy(data)
- }
- fn deleteAllKeys(&self) -> binder::Result<()> {
- self.real.deleteAllKeys()
- }
- fn destroyAttestationIds(&self) -> binder::Result<()> {
- self.real.destroyAttestationIds()
- }
- fn deviceLocked(
- &self,
- password_only: bool,
- timestamp_token: Option<&TimeStampToken>,
- ) -> binder::Result<()> {
- // Propagate to both real and software devices, but only pay attention
- // to the result from the real device.
- let _ = self.soft.deviceLocked(password_only, timestamp_token);
- self.real.deviceLocked(password_only, timestamp_token)
- }
- fn earlyBootEnded(&self) -> binder::Result<()> {
- // Propagate to both real and software devices, but only pay attention
- // to the result from the real device.
- let _ = self.soft.earlyBootEnded();
- self.real.earlyBootEnded()
- }
-
- // For methods that emit keyblobs, check whether the underlying real device
- // supports the relevant parameters, and forward to the appropriate device.
- // If the emulated device is used, ensure that the created keyblob gets
- // prefixed so we can recognize it in future.
- fn generateKey(
- &self,
- key_params: &[KeyParameter],
- attestation_key: Option<&AttestationKey>,
- ) -> binder::Result<KeyCreationResult> {
- if self.emu.emulation_required(key_params, &KeyImportData::None) {
- let mut result = self.soft.generateKey(key_params, attestation_key)?;
- result.keyBlob = map_or_log_err(wrap_keyblob(&result.keyBlob), Ok)?;
- Ok(result)
- } else {
- self.real.generateKey(key_params, attestation_key)
- }
- }
- fn importKey(
- &self,
- key_params: &[KeyParameter],
- key_format: KeyFormat,
- key_data: &[u8],
- attestation_key: Option<&AttestationKey>,
- ) -> binder::Result<KeyCreationResult> {
- if self.emu.emulation_required(key_params, &KeyImportData::new(key_format, key_data)?) {
- let mut result =
- self.soft.importKey(key_params, key_format, key_data, attestation_key)?;
- result.keyBlob = map_or_log_err(wrap_keyblob(&result.keyBlob), Ok)?;
- Ok(result)
- } else {
- self.real.importKey(key_params, key_format, key_data, attestation_key)
- }
- }
- fn importWrappedKey(
- &self,
- wrapped_key_data: &[u8],
- wrapping_key_blob: &[u8],
- masking_key: &[u8],
- unwrapping_params: &[KeyParameter],
- password_sid: i64,
- biometric_sid: i64,
- ) -> binder::Result<KeyCreationResult> {
- // A wrapped key cannot be software-emulated, as the wrapping key is
- // likely hardware-bound.
- self.real.importWrappedKey(
- wrapped_key_data,
- wrapping_key_blob,
- masking_key,
- unwrapping_params,
- password_sid,
- biometric_sid,
- )
- }
-
- // For methods that use keyblobs, determine which device to forward the
- // operation to based on whether the keyblob is appropriately prefixed.
- fn upgradeKey(
- &self,
- keyblob_to_upgrade: &[u8],
- upgrade_params: &[KeyParameter],
- ) -> binder::Result<Vec<u8>> {
- match unwrap_keyblob(keyblob_to_upgrade) {
- KeyBlob::Raw(keyblob) => self.real.upgradeKey(keyblob, upgrade_params),
- KeyBlob::Wrapped(keyblob) => {
- // Re-wrap the upgraded keyblob.
- let upgraded_keyblob = self.soft.upgradeKey(keyblob, upgrade_params)?;
- map_or_log_err(wrap_keyblob(&upgraded_keyblob), Ok)
- }
- }
- }
- fn deleteKey(&self, keyblob: &[u8]) -> binder::Result<()> {
- match unwrap_keyblob(keyblob) {
- KeyBlob::Raw(keyblob) => self.real.deleteKey(keyblob),
- KeyBlob::Wrapped(keyblob) => {
- // Forward to the software implementation for completeness, but
- // this should always be a no-op.
- self.soft.deleteKey(keyblob)
- }
- }
- }
- fn begin(
- &self,
- purpose: KeyPurpose,
- keyblob: &[u8],
- params: &[KeyParameter],
- auth_token: Option<&HardwareAuthToken>,
- ) -> binder::Result<BeginResult> {
- match unwrap_keyblob(keyblob) {
- KeyBlob::Raw(keyblob) => self.real.begin(purpose, keyblob, params, auth_token),
- KeyBlob::Wrapped(keyblob) => self.soft.begin(purpose, keyblob, params, auth_token),
- }
- }
- fn getKeyCharacteristics(
- &self,
- keyblob: &[u8],
- app_id: &[u8],
- app_data: &[u8],
- ) -> binder::Result<Vec<KeyCharacteristics>> {
- match unwrap_keyblob(keyblob) {
- KeyBlob::Raw(keyblob) => self.real.getKeyCharacteristics(keyblob, app_id, app_data),
- KeyBlob::Wrapped(keyblob) => self.soft.getKeyCharacteristics(keyblob, app_id, app_data),
- }
- }
- fn getRootOfTrustChallenge(&self) -> binder::Result<[u8; 16]> {
- self.real.getRootOfTrustChallenge()
- }
- fn getRootOfTrust(&self, challenge: &[u8; 16]) -> binder::Result<Vec<u8>> {
- self.real.getRootOfTrust(challenge)
- }
- fn sendRootOfTrust(&self, root_of_trust: &[u8]) -> binder::Result<()> {
- self.real.sendRootOfTrust(root_of_trust)
- }
- fn convertStorageKeyToEphemeral(&self, storage_keyblob: &[u8]) -> binder::Result<Vec<u8>> {
- // Storage keys should never be associated with a software emulated device.
- self.real.convertStorageKeyToEphemeral(storage_keyblob)
- }
-}
-
-/// Detector for current features that are not implemented by KeyMint V1.
-#[derive(Debug)]
-pub struct KeyMintV1 {
- sec_level: SecurityLevel,
-}
-
-impl KeyMintV1 {
- pub fn new(sec_level: SecurityLevel) -> Self {
- Self { sec_level }
- }
-}
-
-impl EmulationDetector for KeyMintV1 {
- fn emulation_required(&self, params: &[KeyParameter], _import_data: &KeyImportData) -> bool {
- // No current difference from KeyMint v1 for STRONGBOX (it doesn't
- // support curve 25519).
- if self.sec_level == SecurityLevel::STRONGBOX {
- return false;
- }
-
- // KeyMint V1 does not support the use of curve 25519, so hunt for that
- // in the parameters.
- if params.iter().any(|p| {
- p.tag == Tag::EC_CURVE && p.value == KeyParameterValue::EcCurve(EcCurve::CURVE_25519)
- }) {
- return true;
- }
- // In theory, if the `import_data` is `KeyImportData::Pkcs8` we could
- // check the imported keymaterial for the Ed25519 / X25519 OIDs in the
- // PKCS8 keydata, and use that to decide to route to software. However,
- // the KeyMint spec doesn't require that so don't attempt to parse the
- // key material here.
- false
- }
-}
-
-/// Detector for current features that are not implemented by KeyMaster, via the
-/// km_compat wrapper.
-#[derive(Debug)]
-pub struct Keymaster {
- v1: KeyMintV1,
-}
-
-/// TODO(b/216434270): This could be used this to replace the emulation routing
-/// in the km_compat C++ code, and allow support for imported ECDH keys along
-/// the way. Would need to figure out what would happen to existing emulated
-/// keys though.
-#[allow(dead_code)]
-impl Keymaster {
- pub fn new(sec_level: SecurityLevel) -> Self {
- Self { v1: KeyMintV1::new(sec_level) }
- }
-}
-
-impl EmulationDetector for Keymaster {
- fn emulation_required(&self, params: &[KeyParameter], import_data: &KeyImportData) -> bool {
- // The km_compat wrapper on top of Keymaster emulates the KeyMint V1
- // interface, so any feature from > v1 needs to be emulated.
- if self.v1.emulation_required(params, import_data) {
- return true;
- }
-
- // Keymaster does not support ECDH (KeyPurpose::AGREE_KEY), so hunt for
- // that in the parameters.
- if params.iter().any(|p| {
- p.tag == Tag::PURPOSE && p.value == KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY)
- }) {
- return true;
- }
- false
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_key_import_data() {
- let data = vec![1, 2, 3];
- assert_eq!(KeyImportData::new(KeyFormat::PKCS8, &data), Ok(KeyImportData::Pkcs8(&data)));
- assert_eq!(KeyImportData::new(KeyFormat::RAW, &data), Ok(KeyImportData::Raw(&data)));
- assert!(KeyImportData::new(KeyFormat::X509, &data).is_err());
- }
-
- #[test]
- fn test_wrap_keyblob() {
- let keyblob = vec![1, 2, 3];
- let wrapped = wrap_keyblob(&keyblob).unwrap();
- assert_eq!(&wrapped[..KEYBLOB_PREFIX.len()], KEYBLOB_PREFIX);
- assert_eq!(&wrapped[KEYBLOB_PREFIX.len()..KEYBLOB_PREFIX.len() + keyblob.len()], &keyblob);
- assert_eq!(unwrap_keyblob(&keyblob), KeyBlob::Raw(&keyblob));
- assert_eq!(unwrap_keyblob(&wrapped), KeyBlob::Wrapped(&keyblob));
-
- let mut corrupt_prefix = wrapped.clone();
- corrupt_prefix[0] ^= 0x01;
- assert_eq!(unwrap_keyblob(&corrupt_prefix), KeyBlob::Raw(&corrupt_prefix));
-
- let mut corrupt_suffix = wrapped.clone();
- corrupt_suffix[wrapped.len() - 1] ^= 0x01;
- assert_eq!(unwrap_keyblob(&corrupt_suffix), KeyBlob::Raw(&corrupt_suffix));
-
- let too_short = &wrapped[..wrapped.len() - 4];
- assert_eq!(unwrap_keyblob(too_short), KeyBlob::Raw(too_short));
- }
-
- #[test]
- fn test_keymintv1_emulation_required() {
- let tests = vec![
- (SecurityLevel::TRUSTED_ENVIRONMENT, vec![], false),
- (
- SecurityLevel::TRUSTED_ENVIRONMENT,
- vec![
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
- },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KeyParameterValue::KeyPurpose(KeyPurpose::VERIFY),
- },
- ],
- false,
- ),
- (
- SecurityLevel::TRUSTED_ENVIRONMENT,
- vec![KeyParameter {
- tag: Tag::PURPOSE,
- value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
- }],
- false,
- ),
- (
- SecurityLevel::TRUSTED_ENVIRONMENT,
- vec![
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
- },
- KeyParameter {
- tag: Tag::EC_CURVE,
- value: KeyParameterValue::EcCurve(EcCurve::P_256),
- },
- ],
- false,
- ),
- (
- SecurityLevel::TRUSTED_ENVIRONMENT,
- vec![
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
- },
- KeyParameter {
- tag: Tag::EC_CURVE,
- value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
- },
- ],
- true,
- ),
- (
- SecurityLevel::STRONGBOX,
- vec![
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
- },
- KeyParameter {
- tag: Tag::EC_CURVE,
- value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
- },
- ],
- false,
- ),
- ];
- for (sec_level, params, want) in tests {
- let v1 = KeyMintV1::new(sec_level);
- let got = v1.emulation_required(&params, &KeyImportData::None);
- assert_eq!(got, want, "emulation_required({:?})={}, want {}", params, got, want);
- }
- }
-
- #[test]
- fn test_keymaster_emulation_required() {
- let tests = vec![
- (SecurityLevel::TRUSTED_ENVIRONMENT, vec![], false),
- (
- SecurityLevel::TRUSTED_ENVIRONMENT,
- vec![
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
- },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KeyParameterValue::KeyPurpose(KeyPurpose::VERIFY),
- },
- ],
- false,
- ),
- (
- SecurityLevel::TRUSTED_ENVIRONMENT,
- vec![KeyParameter {
- tag: Tag::PURPOSE,
- value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
- }],
- true,
- ),
- (
- SecurityLevel::TRUSTED_ENVIRONMENT,
- vec![
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
- },
- KeyParameter {
- tag: Tag::EC_CURVE,
- value: KeyParameterValue::EcCurve(EcCurve::P_256),
- },
- ],
- true,
- ),
- (
- SecurityLevel::TRUSTED_ENVIRONMENT,
- vec![
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
- },
- KeyParameter {
- tag: Tag::EC_CURVE,
- value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
- },
- ],
- true,
- ),
- (
- SecurityLevel::STRONGBOX,
- vec![
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
- },
- KeyParameter {
- tag: Tag::EC_CURVE,
- value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
- },
- ],
- true,
- ),
- (
- SecurityLevel::STRONGBOX,
- vec![
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
- },
- KeyParameter {
- tag: Tag::EC_CURVE,
- value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
- },
- ],
- false,
- ),
- ];
- for (sec_level, params, want) in tests {
- let v0 = Keymaster::new(sec_level);
- let got = v0.emulation_required(&params, &KeyImportData::None);
- assert_eq!(got, want, "emulation_required({:?})={}, want {}", params, got, want);
- }
- }
-}
diff --git a/keystore2/src/km_compat/Android.bp b/keystore2/src/km_compat/Android.bp
index 806f3dcf..541788e4 100644
--- a/keystore2/src/km_compat/Android.bp
+++ b/keystore2/src/km_compat/Android.bp
@@ -25,10 +25,9 @@ rust_library {
name: "libkeystore2_km_compat",
crate_name: "keystore2_km_compat",
srcs: ["lib.rs"],
- defaults: [
- "keymint_use_latest_hal_aidl_rust",
- ],
+
rustlibs: [
+ "android.hardware.security.keymint-V1-rust",
"android.security.compat-rust",
],
shared_libs: [
@@ -42,10 +41,8 @@ rust_test {
srcs: ["lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
- defaults: [
- "keymint_use_latest_hal_aidl_rust",
- ],
rustlibs: [
+ "android.hardware.security.keymint-V1-rust",
"android.security.compat-rust",
],
shared_libs: [
@@ -56,17 +53,15 @@ rust_test {
cc_library {
name: "libkm_compat",
srcs: ["km_compat.cpp"],
- defaults: [
- "keymint_use_latest_hal_aidl_ndk_shared",
- "keystore2_use_latest_aidl_ndk_shared",
- ],
shared_libs: [
"android.hardware.keymaster@3.0",
"android.hardware.keymaster@4.0",
"android.hardware.keymaster@4.1",
- "android.hardware.security.secureclock-V1-ndk",
- "android.hardware.security.sharedsecret-V1-ndk",
- "android.security.compat-ndk",
+ "android.hardware.security.keymint-V1-ndk_platform",
+ "android.hardware.security.secureclock-V1-ndk_platform",
+ "android.hardware.security.sharedsecret-V1-ndk_platform",
+ "android.security.compat-ndk_platform",
+ "android.system.keystore2-V1-ndk_platform",
"libbase",
"libbinder_ndk",
"libcrypto",
@@ -82,13 +77,11 @@ cc_library {
cc_library {
name: "libkm_compat_service",
srcs: ["km_compat_service.cpp"],
- defaults: [
- "keymint_use_latest_hal_aidl_ndk_shared",
- ],
shared_libs: [
- "android.hardware.security.secureclock-V1-ndk",
- "android.hardware.security.sharedsecret-V1-ndk",
- "android.security.compat-ndk",
+ "android.hardware.security.keymint-V1-ndk_platform",
+ "android.hardware.security.secureclock-V1-ndk_platform",
+ "android.hardware.security.sharedsecret-V1-ndk_platform",
+ "android.security.compat-ndk_platform",
"libbinder_ndk",
"libcrypto",
"libkm_compat",
@@ -110,17 +103,15 @@ cc_test {
"parameter_conversion_test.cpp",
"slot_test.cpp",
],
- defaults: [
- "keymint_use_latest_hal_aidl_ndk_shared",
- "keystore2_use_latest_aidl_ndk_shared",
- ],
shared_libs: [
"android.hardware.keymaster@3.0",
"android.hardware.keymaster@4.0",
"android.hardware.keymaster@4.1",
- "android.hardware.security.secureclock-V1-ndk",
- "android.hardware.security.sharedsecret-V1-ndk",
- "android.security.compat-ndk",
+ "android.hardware.security.keymint-V1-ndk_platform",
+ "android.hardware.security.secureclock-V1-ndk_platform",
+ "android.hardware.security.sharedsecret-V1-ndk_platform",
+ "android.security.compat-ndk_platform",
+ "android.system.keystore2-V1-ndk_platform",
"libbase",
"libbinder_ndk",
"libcrypto",
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index 6d0630b4..8d59a5a7 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -80,6 +80,7 @@ bool isAttestationParameter(const KMV1::KeyParameter& param) {
case Tag::CERTIFICATE_SUBJECT:
case Tag::CERTIFICATE_NOT_BEFORE:
case Tag::CERTIFICATE_NOT_AFTER:
+ case Tag::INCLUDE_UNIQUE_ID:
case Tag::DEVICE_UNIQUE_ATTESTATION:
return true;
default:
@@ -126,7 +127,7 @@ bool isKeyCreationParameter(const KMV1::KeyParameter& param) {
case Tag::TRUSTED_CONFIRMATION_REQUIRED:
case Tag::UNLOCKED_DEVICE_REQUIRED:
case Tag::CREATION_DATETIME:
- case Tag::INCLUDE_UNIQUE_ID:
+ case Tag::UNIQUE_ID:
case Tag::IDENTITY_CREDENTIAL_KEY:
case Tag::STORAGE_KEY:
case Tag::MAC_LENGTH:
@@ -383,39 +384,29 @@ convertSharedSecretParametersToLegacy(const std::vector<SharedSecretParameters>&
return ssps;
}
-void OperationSlotManager::setNumFreeSlots(uint8_t numFreeSlots) {
+void OperationSlots::setNumFreeSlots(uint8_t numFreeSlots) {
std::lock_guard<std::mutex> lock(mNumFreeSlotsMutex);
mNumFreeSlots = numFreeSlots;
}
-std::optional<OperationSlot>
-OperationSlotManager::claimSlot(std::shared_ptr<OperationSlotManager> operationSlots) {
- std::lock_guard<std::mutex> lock(operationSlots->mNumFreeSlotsMutex);
- if (operationSlots->mNumFreeSlots > 0) {
- operationSlots->mNumFreeSlots--;
- return OperationSlot(std::move(operationSlots), std::nullopt);
+bool OperationSlots::claimSlot() {
+ std::lock_guard<std::mutex> lock(mNumFreeSlotsMutex);
+ if (mNumFreeSlots > 0) {
+ mNumFreeSlots--;
+ return true;
}
- return std::nullopt;
-}
-
-OperationSlot
-OperationSlotManager::claimReservedSlot(std::shared_ptr<OperationSlotManager> operationSlots) {
- std::unique_lock<std::mutex> reservedGuard(operationSlots->mReservedSlotMutex);
- return OperationSlot(std::move(operationSlots), std::move(reservedGuard));
+ return false;
}
-OperationSlot::OperationSlot(std::shared_ptr<OperationSlotManager> slots,
- std::optional<std::unique_lock<std::mutex>> reservedGuard)
- : mOperationSlots(std::move(slots)), mReservedGuard(std::move(reservedGuard)) {}
-
-void OperationSlotManager::freeSlot() {
+void OperationSlots::freeSlot() {
std::lock_guard<std::mutex> lock(mNumFreeSlotsMutex);
mNumFreeSlots++;
}
-OperationSlot::~OperationSlot() {
- if (!mReservedGuard && mOperationSlots) {
+void OperationSlot::freeSlot() {
+ if (mIsActive) {
mOperationSlots->freeSlot();
+ mIsActive = false;
}
}
@@ -505,15 +496,16 @@ ScopedAStatus KeyMintDevice::importKey(const std::vector<KeyParameter>& inKeyPar
auto legacyKeyGENParams = convertKeyParametersToLegacy(extractGenerationParams(inKeyParams));
auto legacyKeyFormat = convertKeyFormatToLegacy(in_inKeyFormat);
KMV1::ErrorCode errorCode;
- auto result = mDevice->importKey(
- legacyKeyGENParams, legacyKeyFormat, in_inKeyData,
- [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
- const V4_0_KeyCharacteristics& keyCharacteristics) {
- errorCode = convert(error);
- out_creationResult->keyBlob = keyBlobPrefix(keyBlob, false);
- out_creationResult->keyCharacteristics =
- processLegacyCharacteristics(securityLevel_, inKeyParams, keyCharacteristics);
- });
+ auto result = mDevice->importKey(legacyKeyGENParams, legacyKeyFormat, in_inKeyData,
+ [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
+ const V4_0_KeyCharacteristics& keyCharacteristics) {
+ errorCode = convert(error);
+ out_creationResult->keyBlob =
+ keyBlobPrefix(keyBlob, false);
+ out_creationResult->keyCharacteristics =
+ processLegacyCharacteristics(
+ securityLevel_, inKeyParams, keyCharacteristics);
+ });
if (!result.isOk()) {
LOG(ERROR) << __func__ << " transaction failed. " << result.description();
return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
@@ -621,15 +613,9 @@ ScopedAStatus KeyMintDevice::begin(KeyPurpose in_inPurpose,
const std::vector<KeyParameter>& in_inParams,
const std::optional<HardwareAuthToken>& in_inAuthToken,
BeginResult* _aidl_return) {
- return beginInternal(in_inPurpose, prefixedKeyBlob, in_inParams, in_inAuthToken,
- false /* useReservedSlot */, _aidl_return);
-}
-
-ScopedAStatus KeyMintDevice::beginInternal(KeyPurpose in_inPurpose,
- const std::vector<uint8_t>& prefixedKeyBlob,
- const std::vector<KeyParameter>& in_inParams,
- const std::optional<HardwareAuthToken>& in_inAuthToken,
- bool useReservedSlot, BeginResult* _aidl_return) {
+ if (!mOperationSlots.claimSlot()) {
+ return convertErrorCode(V4_0_ErrorCode::TOO_MANY_OPERATIONS);
+ }
const std::vector<uint8_t>& in_inKeyBlob = prefixedKeyBlobRemovePrefix(prefixedKeyBlob);
if (prefixedKeyBlobIsSoftKeyMint(prefixedKeyBlob)) {
@@ -637,41 +623,28 @@ ScopedAStatus KeyMintDevice::beginInternal(KeyPurpose in_inPurpose,
_aidl_return);
}
- OperationSlot slot;
- // No need to claim a slot for software device.
- if (useReservedSlot) {
- // There is only one reserved slot. This function blocks until
- // the reserved slot becomes available.
- slot = OperationSlotManager::claimReservedSlot(mOperationSlots);
- } else {
- if (auto opt_slot = OperationSlotManager::claimSlot(mOperationSlots)) {
- slot = std::move(*opt_slot);
- } else {
- return convertErrorCode(V4_0_ErrorCode::TOO_MANY_OPERATIONS);
- }
- }
-
auto legacyPurpose =
static_cast<::android::hardware::keymaster::V4_0::KeyPurpose>(in_inPurpose);
auto legacyParams = convertKeyParametersToLegacy(in_inParams);
auto legacyAuthToken = convertAuthTokenToLegacy(in_inAuthToken);
KMV1::ErrorCode errorCode;
- auto result =
- mDevice->begin(legacyPurpose, in_inKeyBlob, legacyParams, legacyAuthToken,
- [&](V4_0_ErrorCode error, const hidl_vec<V4_0_KeyParameter>& outParams,
- uint64_t operationHandle) {
- errorCode = convert(error);
- if (error == V4_0_ErrorCode::OK) {
- _aidl_return->challenge = operationHandle;
- _aidl_return->params = convertKeyParametersFromLegacy(outParams);
- _aidl_return->operation = ndk::SharedRefBase::make<KeyMintOperation>(
- mDevice, operationHandle, std::move(slot));
- }
- });
+ auto result = mDevice->begin(
+ legacyPurpose, in_inKeyBlob, legacyParams, legacyAuthToken,
+ [&](V4_0_ErrorCode error, const hidl_vec<V4_0_KeyParameter>& outParams,
+ uint64_t operationHandle) {
+ errorCode = convert(error);
+ _aidl_return->challenge = operationHandle;
+ _aidl_return->params = convertKeyParametersFromLegacy(outParams);
+ _aidl_return->operation = ndk::SharedRefBase::make<KeyMintOperation>(
+ mDevice, operationHandle, &mOperationSlots, error == V4_0_ErrorCode::OK);
+ });
if (!result.isOk()) {
LOG(ERROR) << __func__ << " transaction failed. " << result.description();
errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
}
+ if (errorCode != KMV1::ErrorCode::OK) {
+ mOperationSlots.freeSlot();
+ }
return convertErrorCode(errorCode);
}
@@ -731,9 +704,8 @@ KeyMintDevice::convertStorageKeyToEphemeral(const std::vector<uint8_t>& prefixed
LOG(ERROR) << __func__ << " export_key failed: " << ret.description();
return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
}
- if (km_error != KMV1::ErrorCode::OK) {
+ if (km_error != KMV1::ErrorCode::OK)
LOG(ERROR) << __func__ << " export_key failed, code " << int32_t(km_error);
- }
return convertErrorCode(km_error);
}
@@ -769,19 +741,6 @@ ScopedAStatus KeyMintDevice::getKeyCharacteristics(
}
}
-ScopedAStatus KeyMintDevice::getRootOfTrustChallenge(std::array<uint8_t, 16>* /* challenge */) {
- return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
-}
-
-ScopedAStatus KeyMintDevice::getRootOfTrust(const std::array<uint8_t, 16>& /* challenge */,
- std::vector<uint8_t>* /* rootOfTrust */) {
- return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
-}
-
-ScopedAStatus KeyMintDevice::sendRootOfTrust(const std::vector<uint8_t>& /* rootOfTrust */) {
- return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
-}
-
ScopedAStatus KeyMintOperation::updateAad(const std::vector<uint8_t>& input,
const std::optional<HardwareAuthToken>& optAuthToken,
const std::optional<TimeStampToken>& optTimeStampToken) {
@@ -798,11 +757,7 @@ ScopedAStatus KeyMintOperation::updateAad(const std::vector<uint8_t>& input,
LOG(ERROR) << __func__ << " transaction failed. " << result.description();
errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
}
-
- // Operation slot is no longer occupied.
- if (errorCode != KMV1::ErrorCode::OK) {
- mOperationSlot = std::nullopt;
- }
+ if (errorCode != KMV1::ErrorCode::OK) mOperationSlot.freeSlot();
return convertErrorCode(errorCode);
}
@@ -860,10 +815,7 @@ ScopedAStatus KeyMintOperation::update(const std::vector<uint8_t>& input_raw,
inputPos += consumed;
}
- // Operation slot is no longer occupied.
- if (errorCode != KMV1::ErrorCode::OK) {
- mOperationSlot = std::nullopt;
- }
+ if (errorCode != KMV1::ErrorCode::OK) mOperationSlot.freeSlot();
return convertErrorCode(errorCode);
}
@@ -894,19 +846,17 @@ KeyMintOperation::finish(const std::optional<std::vector<uint8_t>>& in_input,
*out_output = output;
});
+ mOperationSlot.freeSlot();
if (!result.isOk()) {
LOG(ERROR) << __func__ << " transaction failed. " << result.description();
errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
}
-
- mOperationSlot = std::nullopt;
-
return convertErrorCode(errorCode);
}
ScopedAStatus KeyMintOperation::abort() {
auto result = mDevice->abort(mOperationHandle);
- mOperationSlot = std::nullopt;
+ mOperationSlot.freeSlot();
if (!result.isOk()) {
LOG(ERROR) << __func__ << " transaction failed. " << result.description();
return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
@@ -915,7 +865,7 @@ ScopedAStatus KeyMintOperation::abort() {
}
KeyMintOperation::~KeyMintOperation() {
- if (mOperationSlot) {
+ if (mOperationSlot.hasSlot()) {
auto error = abort();
if (!error.isOk()) {
LOG(WARNING) << "Error calling abort in ~KeyMintOperation: " << error.getMessage();
@@ -1168,8 +1118,8 @@ KeyMintDevice::signCertificate(const std::vector<KeyParameter>& keyParams,
kps.push_back(KMV1::makeKeyParameter(KMV1::TAG_PADDING, origPadding));
}
BeginResult beginResult;
- auto error = beginInternal(KeyPurpose::SIGN, prefixedKeyBlob, kps, HardwareAuthToken(),
- true /* useReservedSlot */, &beginResult);
+ auto error =
+ begin(KeyPurpose::SIGN, prefixedKeyBlob, kps, HardwareAuthToken(), &beginResult);
if (!error.isOk()) {
errorCode = toErrorCode(error);
return std::vector<uint8_t>();
@@ -1390,7 +1340,7 @@ KeymasterDevices initializeKeymasters() {
CHECK(serviceManager.get()) << "Failed to get ServiceManager";
auto result = enumerateKeymasterDevices<Keymaster4>(serviceManager.get());
auto softKeymaster = result[SecurityLevel::SOFTWARE];
- if ((!result[SecurityLevel::TRUSTED_ENVIRONMENT]) && (!result[SecurityLevel::STRONGBOX])) {
+ if (!result[SecurityLevel::TRUSTED_ENVIRONMENT]) {
result = enumerateKeymasterDevices<Keymaster3>(serviceManager.get());
}
if (softKeymaster) result[SecurityLevel::SOFTWARE] = softKeymaster;
@@ -1405,21 +1355,20 @@ KeymasterDevices initializeKeymasters() {
}
void KeyMintDevice::setNumFreeSlots(uint8_t numFreeSlots) {
- mOperationSlots->setNumFreeSlots(numFreeSlots);
+ mOperationSlots.setNumFreeSlots(numFreeSlots);
}
// Constructors and helpers.
KeyMintDevice::KeyMintDevice(sp<Keymaster> device, KeyMintSecurityLevel securityLevel)
- : mDevice(device), mOperationSlots(std::make_shared<OperationSlotManager>()),
- securityLevel_(securityLevel) {
+ : mDevice(device), securityLevel_(securityLevel) {
if (securityLevel == KeyMintSecurityLevel::STRONGBOX) {
setNumFreeSlots(3);
} else {
setNumFreeSlots(15);
}
- softKeyMintDevice_ = CreateKeyMintDevice(KeyMintSecurityLevel::SOFTWARE);
+ softKeyMintDevice_.reset(CreateKeyMintDevice(KeyMintSecurityLevel::SOFTWARE));
}
sp<Keymaster> getDevice(KeyMintSecurityLevel securityLevel) {
@@ -1442,33 +1391,14 @@ sp<Keymaster> getDevice(KeyMintSecurityLevel securityLevel) {
}
}
-std::shared_ptr<IKeyMintDevice> getSoftwareKeymintDevice() {
- static std::mutex mutex;
- static std::shared_ptr<IKeyMintDevice> swDevice;
- std::lock_guard<std::mutex> lock(mutex);
- if (!swDevice) {
- swDevice = CreateKeyMintDevice(KeyMintSecurityLevel::SOFTWARE);
- }
- return swDevice;
-}
-
std::shared_ptr<KeyMintDevice>
-KeyMintDevice::getWrappedKeymasterDevice(KeyMintSecurityLevel securityLevel) {
+KeyMintDevice::createKeyMintDevice(KeyMintSecurityLevel securityLevel) {
if (auto dev = getDevice(securityLevel)) {
return ndk::SharedRefBase::make<KeyMintDevice>(std::move(dev), securityLevel);
}
return {};
}
-std::shared_ptr<IKeyMintDevice>
-KeyMintDevice::createKeyMintDevice(KeyMintSecurityLevel securityLevel) {
- if (securityLevel == KeyMintSecurityLevel::SOFTWARE) {
- return getSoftwareKeymintDevice();
- } else {
- return getWrappedKeymasterDevice(securityLevel);
- }
-}
-
std::shared_ptr<SharedSecret> SharedSecret::createSharedSecret(KeyMintSecurityLevel securityLevel) {
auto device = getDevice(securityLevel);
if (!device) {
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
index 6654c4a6..70c7b863 100644
--- a/keystore2/src/km_compat/km_compat.h
+++ b/keystore2/src/km_compat/km_compat.h
@@ -50,55 +50,41 @@ using ::aidl::android::security::compat::BnKeystoreCompatService;
using ::android::hardware::keymaster::V4_1::support::Keymaster;
using ::ndk::ScopedAStatus;
-class OperationSlot;
-class OperationSlotManager;
-// An abstraction for a single operation slot.
-// This contains logic to ensure that we do not free the slot multiple times,
-// e.g., if we call abort twice on the same operation.
-class OperationSlot {
- friend OperationSlotManager;
-
+class OperationSlots {
private:
- std::shared_ptr<OperationSlotManager> mOperationSlots;
- std::optional<std::unique_lock<std::mutex>> mReservedGuard;
-
- protected:
- OperationSlot(std::shared_ptr<OperationSlotManager>,
- std::optional<std::unique_lock<std::mutex>> reservedGuard);
- OperationSlot(const OperationSlot&) = delete;
- OperationSlot& operator=(const OperationSlot&) = delete;
+ uint8_t mNumFreeSlots;
+ std::mutex mNumFreeSlotsMutex;
public:
- OperationSlot() : mOperationSlots(nullptr), mReservedGuard(std::nullopt) {}
- OperationSlot(OperationSlot&&) = default;
- OperationSlot& operator=(OperationSlot&&) = default;
- ~OperationSlot();
+ void setNumFreeSlots(uint8_t numFreeSlots);
+ bool claimSlot();
+ void freeSlot();
};
-class OperationSlotManager {
+// An abstraction for a single operation slot.
+// This contains logic to ensure that we do not free the slot multiple times,
+// e.g., if we call abort twice on the same operation.
+class OperationSlot {
private:
- uint8_t mNumFreeSlots;
- std::mutex mNumFreeSlotsMutex;
- std::mutex mReservedSlotMutex;
+ OperationSlots* mOperationSlots;
+ bool mIsActive;
public:
- void setNumFreeSlots(uint8_t numFreeSlots);
- static std::optional<OperationSlot>
- claimSlot(std::shared_ptr<OperationSlotManager> operationSlots);
- static OperationSlot claimReservedSlot(std::shared_ptr<OperationSlotManager> operationSlots);
+ OperationSlot(OperationSlots* slots, bool isActive)
+ : mOperationSlots(slots), mIsActive(isActive) {}
+
void freeSlot();
+ bool hasSlot() { return mIsActive; }
};
class KeyMintDevice : public aidl::android::hardware::security::keymint::BnKeyMintDevice {
private:
::android::sp<Keymaster> mDevice;
- std::shared_ptr<OperationSlotManager> mOperationSlots;
+ OperationSlots mOperationSlots;
public:
explicit KeyMintDevice(::android::sp<Keymaster>, KeyMintSecurityLevel);
- static std::shared_ptr<IKeyMintDevice> createKeyMintDevice(KeyMintSecurityLevel securityLevel);
- static std::shared_ptr<KeyMintDevice>
- getWrappedKeymasterDevice(KeyMintSecurityLevel securityLevel);
+ static std::shared_ptr<KeyMintDevice> createKeyMintDevice(KeyMintSecurityLevel securityLevel);
ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* _aidl_return) override;
ScopedAStatus addRngEntropy(const std::vector<uint8_t>& in_data) override;
@@ -121,15 +107,10 @@ class KeyMintDevice : public aidl::android::hardware::security::keymint::BnKeyMi
ScopedAStatus deleteKey(const std::vector<uint8_t>& in_inKeyBlob) override;
ScopedAStatus deleteAllKeys() override;
ScopedAStatus destroyAttestationIds() override;
-
ScopedAStatus begin(KeyPurpose in_inPurpose, const std::vector<uint8_t>& in_inKeyBlob,
const std::vector<KeyParameter>& in_inParams,
const std::optional<HardwareAuthToken>& in_inAuthToken,
BeginResult* _aidl_return) override;
- ScopedAStatus beginInternal(KeyPurpose in_inPurpose, const std::vector<uint8_t>& in_inKeyBlob,
- const std::vector<KeyParameter>& in_inParams,
- const std::optional<HardwareAuthToken>& in_inAuthToken,
- bool useReservedSlot, BeginResult* _aidl_return);
ScopedAStatus deviceLocked(bool passwordOnly,
const std::optional<TimeStampToken>& timestampToken) override;
ScopedAStatus earlyBootEnded() override;
@@ -142,11 +123,6 @@ class KeyMintDevice : public aidl::android::hardware::security::keymint::BnKeyMi
const std::vector<uint8_t>& appId, const std::vector<uint8_t>& appData,
std::vector<KeyCharacteristics>* keyCharacteristics) override;
- ScopedAStatus getRootOfTrustChallenge(std::array<uint8_t, 16>* challenge);
- ScopedAStatus getRootOfTrust(const std::array<uint8_t, 16>& challenge,
- std::vector<uint8_t>* rootOfTrust);
- ScopedAStatus sendRootOfTrust(const std::vector<uint8_t>& rootOfTrust);
-
// These are public to allow testing code to use them directly.
// This class should not be used publicly anyway.
std::variant<std::vector<Certificate>, KMV1_ErrorCode>
@@ -165,8 +141,9 @@ class KeyMintDevice : public aidl::android::hardware::security::keymint::BnKeyMi
class KeyMintOperation : public aidl::android::hardware::security::keymint::BnKeyMintOperation {
public:
- KeyMintOperation(::android::sp<Keymaster> device, uint64_t operationHandle, OperationSlot slot)
- : mDevice(device), mOperationHandle(operationHandle), mOperationSlot(std::move(slot)) {}
+ KeyMintOperation(::android::sp<Keymaster> device, uint64_t operationHandle,
+ OperationSlots* slots, bool isActive)
+ : mDevice(device), mOperationHandle(operationHandle), mOperationSlot(slots, isActive) {}
~KeyMintOperation();
ScopedAStatus updateAad(const std::vector<uint8_t>& input,
@@ -204,7 +181,7 @@ class KeyMintOperation : public aidl::android::hardware::security::keymint::BnKe
std::vector<uint8_t> mUpdateBuffer;
::android::sp<Keymaster> mDevice;
uint64_t mOperationHandle;
- std::optional<OperationSlot> mOperationSlot;
+ OperationSlot mOperationSlot;
};
class SharedSecret : public aidl::android::hardware::security::sharedsecret::BnSharedSecret {
diff --git a/keystore2/src/km_compat/km_compat_type_conversion.h b/keystore2/src/km_compat/km_compat_type_conversion.h
index 33248a40..de094775 100644
--- a/keystore2/src/km_compat/km_compat_type_conversion.h
+++ b/keystore2/src/km_compat/km_compat_type_conversion.h
@@ -16,9 +16,6 @@
#pragma once
-#include <optional>
-
-#include <aidl/android/hardware/security/keymint/EcCurve.h>
#include <aidl/android/hardware/security/keymint/ErrorCode.h>
#include <keymasterV4_1/keymaster_tags.h>
#include <keymint_support/keymint_tags.h>
@@ -281,7 +278,7 @@ static KMV1::Digest convert(V4_0::Digest d) {
}
}
-static std::optional<V4_0::EcCurve> convert(KMV1::EcCurve e) {
+static V4_0::EcCurve convert(KMV1::EcCurve e) {
switch (e) {
case KMV1::EcCurve::P_224:
return V4_0::EcCurve::P_224;
@@ -291,11 +288,7 @@ static std::optional<V4_0::EcCurve> convert(KMV1::EcCurve e) {
return V4_0::EcCurve::P_384;
case KMV1::EcCurve::P_521:
return V4_0::EcCurve::P_521;
- case KMV1::EcCurve::CURVE_25519:
- // KeyMaster did not support curve 25519
- return std::nullopt;
}
- return std::nullopt;
}
static KMV1::EcCurve convert(V4_0::EcCurve e) {
@@ -497,9 +490,7 @@ static V4_0::KeyParameter convertKeyParameterToLegacy(const KMV1::KeyParameter&
break;
case KMV1::Tag::EC_CURVE:
if (auto v = KMV1::authorizationValue(KMV1::TAG_EC_CURVE, kp)) {
- if (auto curve = convert(v->get())) {
- return V4_0::makeKeyParameter(V4_0::TAG_EC_CURVE, curve.value());
- }
+ return V4_0::makeKeyParameter(V4_0::TAG_EC_CURVE, convert(v->get()));
}
break;
case KMV1::Tag::RSA_PUBLIC_EXPONENT:
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
index 13f77607..56c35bfa 100644
--- a/keystore2/src/km_compat/lib.rs
+++ b/keystore2/src/km_compat/lib.rs
@@ -260,7 +260,7 @@ mod tests {
if let Some(mut extras) = extra_params {
kps.append(&mut extras);
}
- let result = legacy.begin(purpose, blob, &kps, None);
+ let result = legacy.begin(purpose, &blob, &kps, None);
assert!(result.is_ok(), "{:?}", result);
result.unwrap()
}
@@ -286,7 +286,7 @@ mod tests {
let operation = begin_result.operation.unwrap();
let update_aad_result = operation.updateAad(
- b"foobar".as_ref(),
+ &b"foobar".to_vec(),
None, /* authToken */
None, /* timestampToken */
);
@@ -310,7 +310,7 @@ mod tests {
let operation = begin_result.operation.unwrap();
let update_aad_result = operation.updateAad(
- b"foobar".as_ref(),
+ &b"foobar".to_vec(),
None, /* authToken */
None, /* timestampToken */
);
@@ -450,6 +450,10 @@ mod tests {
)));
assert!(sec_level_enforced.iter().any(|kp| matches!(
kp,
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KeyParameterValue::Integer(_) }
+ )));
+ assert!(sec_level_enforced.iter().any(|kp| matches!(
+ kp,
KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KeyParameterValue::Integer(_) }
)));
}
diff --git a/keystore2/src/km_compat/slot_test.cpp b/keystore2/src/km_compat/slot_test.cpp
index d7349702..43f3bc69 100644
--- a/keystore2/src/km_compat/slot_test.cpp
+++ b/keystore2/src/km_compat/slot_test.cpp
@@ -26,7 +26,6 @@ using ::aidl::android::hardware::security::keymint::Algorithm;
using ::aidl::android::hardware::security::keymint::BlockMode;
using ::aidl::android::hardware::security::keymint::Certificate;
using ::aidl::android::hardware::security::keymint::Digest;
-using ::aidl::android::hardware::security::keymint::EcCurve;
using ::aidl::android::hardware::security::keymint::ErrorCode;
using ::aidl::android::hardware::security::keymint::IKeyMintOperation;
using ::aidl::android::hardware::security::keymint::KeyCharacteristics;
@@ -54,25 +53,6 @@ static std::vector<uint8_t> generateAESKey(std::shared_ptr<KeyMintDevice> device
return creationResult.keyBlob;
}
-static bool generateECSingingKey(std::shared_ptr<KeyMintDevice> device) {
- uint64_t now_ms = (uint64_t)time(nullptr) * 1000;
-
- auto keyParams = std::vector<KeyParameter>({
- KMV1::makeKeyParameter(KMV1::TAG_ALGORITHM, Algorithm::EC),
- KMV1::makeKeyParameter(KMV1::TAG_EC_CURVE, EcCurve::P_256),
- KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, true),
- KMV1::makeKeyParameter(KMV1::TAG_DIGEST, Digest::SHA_2_256),
- KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::SIGN),
- KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::VERIFY),
- KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_BEFORE, now_ms - 60 * 60 * 1000),
- KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_AFTER, now_ms + 60 * 60 * 1000),
- });
- KeyCreationResult creationResult;
- auto status = device->generateKey(keyParams, std::nullopt /* attest_key */, &creationResult);
- EXPECT_TRUE(status.isOk()) << status.getDescription();
- return status.isOk();
-}
-
static std::variant<BeginResult, ScopedAStatus> begin(std::shared_ptr<KeyMintDevice> device,
bool valid) {
auto blob = generateAESKey(device);
@@ -89,57 +69,17 @@ static std::variant<BeginResult, ScopedAStatus> begin(std::shared_ptr<KeyMintDev
return beginResult;
}
-static std::shared_ptr<KMV1::IKeyMintOperation>
-generateAndBeginECDHKeyOperation(std::shared_ptr<KeyMintDevice> device) {
- uint64_t now_ms = (uint64_t)time(nullptr) * 1000;
-
- auto keyParams = std::vector<KeyParameter>({
- KMV1::makeKeyParameter(KMV1::TAG_ALGORITHM, Algorithm::EC),
- KMV1::makeKeyParameter(KMV1::TAG_EC_CURVE, EcCurve::P_256),
- KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, true),
- KMV1::makeKeyParameter(KMV1::TAG_DIGEST, Digest::NONE),
- KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::AGREE_KEY),
- KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_BEFORE, now_ms - 60 * 60 * 1000),
- KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_AFTER, now_ms + 60 * 60 * 1000),
- });
- KeyCreationResult creationResult;
- auto status = device->generateKey(keyParams, std::nullopt /* attest_key */, &creationResult);
- EXPECT_TRUE(status.isOk()) << status.getDescription();
- if (!status.isOk()) {
- return {};
- }
- std::vector<KeyParameter> kps;
- BeginResult beginResult;
- auto bstatus = device->begin(KeyPurpose::AGREE_KEY, creationResult.keyBlob, kps,
- HardwareAuthToken(), &beginResult);
- EXPECT_TRUE(status.isOk()) << status.getDescription();
- if (status.isOk()) {
- return beginResult.operation;
- }
- return {};
-}
-
static const int NUM_SLOTS = 2;
TEST(SlotTest, TestSlots) {
static std::shared_ptr<KeyMintDevice> device =
- KeyMintDevice::getWrappedKeymasterDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
- ASSERT_NE(device.get(), nullptr);
-
+ KeyMintDevice::createKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
device->setNumFreeSlots(NUM_SLOTS);
// A begin() that returns a failure should not use a slot.
auto result = begin(device, false);
ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
- // Software emulated operations must not leak virtual slots.
- ASSERT_TRUE(!!generateAndBeginECDHKeyOperation(device));
-
- // Software emulated operations must not impact virtual slots accounting.
- // As opposed to the previous call, the software operation is kept alive.
- auto software_op = generateAndBeginECDHKeyOperation(device);
- ASSERT_TRUE(!!software_op);
-
// Fill up all the slots.
std::vector<std::shared_ptr<IKeyMintOperation>> operations;
for (int i = 0; i < NUM_SLOTS; i++) {
@@ -154,14 +94,6 @@ TEST(SlotTest, TestSlots) {
ASSERT_EQ(std::get<ScopedAStatus>(result).getServiceSpecificError(),
static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
- // At this point all slots are in use. We should still be able to generate keys which
- // require an operation slot during generation.
- ASSERT_TRUE(generateECSingingKey(device));
-
- // Software emulated operations should work despite having all virtual operation slots
- // depleted.
- ASSERT_TRUE(generateAndBeginECDHKeyOperation(device));
-
// TODO: I'm not sure how to generate a failing update call to test that.
// Calling finish should free up a slot.
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index d75bfd2c..6b16d2e0 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -17,8 +17,8 @@
use crate::{
error::{Error as KsError, ResponseCode},
key_parameter::{KeyParameter, KeyParameterValue},
+ super_key::SuperKeyManager,
utils::uid_to_android_user,
- utils::AesGcm,
};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
SecurityLevel::SecurityLevel, Tag::Tag, TagType::TagType,
@@ -26,7 +26,6 @@ use android_hardware_security_keymint::aidl::android::hardware::security::keymin
use anyhow::{Context, Result};
use keystore2_crypto::{aes_gcm_decrypt, Password, ZVec};
use std::collections::{HashMap, HashSet};
-use std::sync::Arc;
use std::{convert::TryInto, fs::File, path::Path, path::PathBuf};
use std::{
fs,
@@ -88,14 +87,6 @@ pub enum Error {
/// an invalid alias filename encoding.
#[error("Invalid alias filename encoding.")]
BadEncoding,
- /// A component of the requested entry other than the KM key blob itself
- /// was encrypted and no super key was provided.
- #[error("Locked entry component.")]
- LockedComponent,
- /// The uids presented to move_keystore_entry belonged to different
- /// Android users.
- #[error("Cannot move keys across Android users.")]
- AndroidUserMismatch,
}
/// The blob payload, optionally with all information required to decrypt it.
@@ -105,16 +96,6 @@ pub enum BlobValue {
Generic(Vec<u8>),
/// A legacy key characteristics file. This has only a single list of Authorizations.
Characteristics(Vec<u8>),
- /// A legacy key characteristics file. This has only a single list of Authorizations.
- /// Additionally, this characteristics file was encrypted with the user's super key.
- EncryptedCharacteristics {
- /// Initialization vector.
- iv: Vec<u8>,
- /// Aead tag for integrity verification.
- tag: Vec<u8>,
- /// Ciphertext.
- data: Vec<u8>,
- },
/// A key characteristics cache has both a hardware enforced and a software enforced list
/// of authorizations.
CharacteristicsCache(Vec<u8>),
@@ -143,17 +124,6 @@ pub enum BlobValue {
/// Ciphertext.
data: Vec<u8>,
},
- /// An encrypted blob. Includes the initialization vector, the aead tag, and the
- /// ciphertext data. The key can be selected from context, i.e., the owner of the key
- /// blob. This is a special case for generic encrypted blobs as opposed to key blobs.
- EncryptedGeneric {
- /// Initialization vector.
- iv: Vec<u8>,
- /// Aead tag for integrity verification.
- tag: Vec<u8>,
- /// Ciphertext.
- data: Vec<u8>,
- },
/// Holds the plaintext key blob either after unwrapping an encrypted blob or when the
/// blob was stored in "plaintext" on disk. The "plaintext" of a key blob is not actual
/// plaintext because all KeyMint blobs are encrypted with a device bound key. The key
@@ -162,19 +132,6 @@ pub enum BlobValue {
Decrypted(ZVec),
}
-/// Keystore used two different key characteristics file formats in the past.
-/// The key characteristics cache which superseded the characteristics file.
-/// The latter stored only one list of key parameters, while the former stored
-/// a hardware enforced and a software enforced list. This Enum indicates which
-/// type was read from the file system.
-#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub enum LegacyKeyCharacteristics {
- /// A characteristics cache was read.
- Cache(Vec<KeyParameter>),
- /// A characteristics file was read.
- File(Vec<KeyParameter>),
-}
-
/// Represents a loaded legacy key blob file.
#[derive(Debug, Eq, PartialEq)]
pub struct Blob {
@@ -212,16 +169,6 @@ fn read_ne_i64(stream: &mut dyn Read) -> Result<i64> {
}
impl Blob {
- /// Creates a new blob from flags and value.
- pub fn new(flags: u8, value: BlobValue) -> Self {
- Self { flags, value }
- }
-
- /// Return the raw flags of this Blob.
- pub fn get_flags(&self) -> u8 {
- self.flags
- }
-
/// This blob was generated with a fallback software KM device.
pub fn is_fallback(&self) -> bool {
self.flags & flags::FALLBACK != 0
@@ -265,14 +212,10 @@ impl LegacyBlobLoader {
// version (1 Byte)
// blob_type (1 Byte)
// flags (1 Byte)
- // info (1 Byte) Size of an info field appended to the blob.
+ // info (1 Byte)
// initialization_vector (16 Bytes)
// integrity (MD5 digest or gcm tag) (16 Bytes)
// length (4 Bytes)
- //
- // The info field is used to store the salt for password encrypted blobs.
- // The beginning of the info field can be computed from the file length
- // and the info byte from the header: <file length> - <info> bytes.
const COMMON_HEADER_SIZE: usize = 4 + Self::IV_SIZE + Self::GCM_TAG_LENGTH + 4;
const VERSION_OFFSET: usize = 0;
@@ -398,28 +341,12 @@ impl LegacyBlobLoader {
let tag = &buffer[Self::AEAD_TAG_OFFSET..Self::AEAD_TAG_OFFSET + Self::GCM_TAG_LENGTH];
match (blob_type, is_encrypted, salt) {
- (blob_types::GENERIC, false, _) => {
+ (blob_types::GENERIC, _, _) => {
Ok(Blob { flags, value: BlobValue::Generic(value.to_vec()) })
}
- (blob_types::GENERIC, true, _) => Ok(Blob {
- flags,
- value: BlobValue::EncryptedGeneric {
- iv: iv.to_vec(),
- tag: tag.to_vec(),
- data: value.to_vec(),
- },
- }),
- (blob_types::KEY_CHARACTERISTICS, false, _) => {
+ (blob_types::KEY_CHARACTERISTICS, _, _) => {
Ok(Blob { flags, value: BlobValue::Characteristics(value.to_vec()) })
}
- (blob_types::KEY_CHARACTERISTICS, true, _) => Ok(Blob {
- flags,
- value: BlobValue::EncryptedCharacteristics {
- iv: iv.to_vec(),
- tag: tag.to_vec(),
- data: value.to_vec(),
- },
- }),
(blob_types::KEY_CHARACTERISTICS_CACHE, _, _) => {
Ok(Blob { flags, value: BlobValue::CharacteristicsCache(value.to_vec()) })
}
@@ -489,26 +416,17 @@ impl LegacyBlobLoader {
BlobValue::Encrypted { iv, tag, data } => Ok(Blob {
flags: blob.flags,
value: BlobValue::Decrypted(
- decrypt(data, iv, tag, None, None)
+ decrypt(&data, &iv, &tag, None, None)
.context("In new_from_stream_decrypt_with.")?,
),
}),
BlobValue::PwEncrypted { iv, tag, data, salt, key_size } => Ok(Blob {
flags: blob.flags,
value: BlobValue::Decrypted(
- decrypt(data, iv, tag, Some(salt), Some(*key_size))
+ decrypt(&data, &iv, &tag, Some(salt), Some(*key_size))
.context("In new_from_stream_decrypt_with.")?,
),
}),
- BlobValue::EncryptedGeneric { iv, tag, data } => Ok(Blob {
- flags: blob.flags,
- value: BlobValue::Generic(
- decrypt(data, iv, tag, None, None)
- .context("In new_from_stream_decrypt_with.")?[..]
- .to_vec(),
- ),
- }),
-
_ => Ok(blob),
}
}
@@ -628,91 +546,24 @@ impl LegacyBlobLoader {
Ok(params)
}
- /// This function takes a Blob and an optional AesGcm. Plain text blob variants are
- /// passed through as is. If a super key is given an attempt is made to decrypt the
- /// blob thereby mapping BlobValue variants as follows:
- /// BlobValue::Encrypted => BlobValue::Decrypted
- /// BlobValue::EncryptedGeneric => BlobValue::Generic
- /// BlobValue::EncryptedCharacteristics => BlobValue::Characteristics
- /// If now super key is given or BlobValue::PwEncrypted is encountered,
- /// Err(Error::LockedComponent) is returned.
- fn decrypt_if_required(super_key: &Option<Arc<dyn AesGcm>>, blob: Blob) -> Result<Blob> {
- match blob {
- Blob { value: BlobValue::Generic(_), .. }
- | Blob { value: BlobValue::Characteristics(_), .. }
- | Blob { value: BlobValue::CharacteristicsCache(_), .. }
- | Blob { value: BlobValue::Decrypted(_), .. } => Ok(blob),
- Blob { value: BlobValue::EncryptedCharacteristics { iv, tag, data }, flags }
- if super_key.is_some() =>
- {
- Ok(Blob {
- value: BlobValue::Characteristics(
- super_key.as_ref().unwrap().decrypt(&data, &iv, &tag).context(
- "In decrypt_if_required: Failed to decrypt EncryptedCharacteristics",
- )?[..]
- .to_vec(),
- ),
- flags,
- })
- }
- Blob { value: BlobValue::Encrypted { iv, tag, data }, flags }
- if super_key.is_some() =>
- {
- Ok(Blob {
- value: BlobValue::Decrypted(
- super_key
- .as_ref()
- .unwrap()
- .decrypt(&data, &iv, &tag)
- .context("In decrypt_if_required: Failed to decrypt Encrypted")?,
- ),
- flags,
- })
- }
- Blob { value: BlobValue::EncryptedGeneric { iv, tag, data }, flags }
- if super_key.is_some() =>
- {
- Ok(Blob {
- value: BlobValue::Generic(
- super_key
- .as_ref()
- .unwrap()
- .decrypt(&data, &iv, &tag)
- .context("In decrypt_if_required: Failed to decrypt Encrypted")?[..]
- .to_vec(),
- ),
- flags,
- })
- }
- // This arm catches all encrypted cases where super key is not present or cannot
- // decrypt the blob, the latter being BlobValue::PwEncrypted.
- _ => Err(Error::LockedComponent)
- .context("In decrypt_if_required: Encountered encrypted blob without super key."),
- }
- }
-
fn read_characteristics_file(
&self,
uid: u32,
prefix: &str,
alias: &str,
hw_sec_level: SecurityLevel,
- super_key: &Option<Arc<dyn AesGcm>>,
- ) -> Result<LegacyKeyCharacteristics> {
+ ) -> Result<Vec<KeyParameter>> {
let blob = Self::read_generic_blob(&self.make_chr_filename(uid, alias, prefix))
.context("In read_characteristics_file")?;
let blob = match blob {
- None => return Ok(LegacyKeyCharacteristics::Cache(Vec::new())),
+ None => return Ok(Vec::new()),
Some(blob) => blob,
};
- let blob = Self::decrypt_if_required(super_key, blob)
- .context("In read_characteristics_file: Trying to decrypt blob.")?;
-
- let (mut stream, is_cache) = match blob.value() {
- BlobValue::Characteristics(data) => (&data[..], false),
- BlobValue::CharacteristicsCache(data) => (&data[..], true),
+ let mut stream = match blob.value() {
+ BlobValue::Characteristics(data) => &data[..],
+ BlobValue::CharacteristicsCache(data) => &data[..],
_ => {
return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(concat!(
"In read_characteristics_file: ",
@@ -738,12 +589,7 @@ impl LegacyBlobLoader {
.into_iter()
.map(|value| KeyParameter::new(value, SecurityLevel::KEYSTORE));
- let params: Vec<KeyParameter> = hw_list.into_iter().flatten().chain(sw_list).collect();
- if is_cache {
- Ok(LegacyKeyCharacteristics::Cache(params))
- } else {
- Ok(LegacyKeyCharacteristics::File(params))
- }
+ Ok(hw_list.into_iter().flatten().chain(sw_list).collect())
}
// This is a list of known prefixes that the Keystore 1.0 SPI used to use.
@@ -793,40 +639,14 @@ impl LegacyBlobLoader {
Ok(Some(Self::new_from_stream(&mut file).context("In read_generic_blob.")?))
}
- fn read_generic_blob_decrypt_with<F>(path: &Path, decrypt: F) -> Result<Option<Blob>>
- where
- F: FnOnce(&[u8], &[u8], &[u8], Option<&[u8]>, Option<usize>) -> Result<ZVec>,
- {
- let mut file = match Self::with_retry_interrupted(|| File::open(path)) {
- Ok(file) => file,
- Err(e) => match e.kind() {
- ErrorKind::NotFound => return Ok(None),
- _ => return Err(e).context("In read_generic_blob_decrypt_with."),
- },
- };
-
- Ok(Some(
- Self::new_from_stream_decrypt_with(&mut file, decrypt)
- .context("In read_generic_blob_decrypt_with.")?,
- ))
- }
-
/// Read a legacy keystore entry blob.
- pub fn read_legacy_keystore_entry<F>(
- &self,
- uid: u32,
- alias: &str,
- decrypt: F,
- ) -> Result<Option<Vec<u8>>>
- where
- F: FnOnce(&[u8], &[u8], &[u8], Option<&[u8]>, Option<usize>) -> Result<ZVec>,
- {
+ pub fn read_legacy_keystore_entry(&self, uid: u32, alias: &str) -> Result<Option<Vec<u8>>> {
let path = match self.make_legacy_keystore_entry_filename(uid, alias) {
Some(path) => path,
None => return Ok(None),
};
- let blob = Self::read_generic_blob_decrypt_with(&path, decrypt)
+ let blob = Self::read_generic_blob(&path)
.context("In read_legacy_keystore_entry: Failed to read blob.")?;
Ok(blob.and_then(|blob| match blob.value {
@@ -839,23 +659,22 @@ impl LegacyBlobLoader {
}
/// Remove a legacy keystore entry by the name alias with owner uid.
- pub fn remove_legacy_keystore_entry(&self, uid: u32, alias: &str) -> Result<bool> {
+ pub fn remove_legacy_keystore_entry(&self, uid: u32, alias: &str) -> Result<()> {
let path = match self.make_legacy_keystore_entry_filename(uid, alias) {
Some(path) => path,
- None => return Ok(false),
+ None => return Ok(()),
};
if let Err(e) = Self::with_retry_interrupted(|| fs::remove_file(path.as_path())) {
match e.kind() {
- ErrorKind::NotFound => return Ok(false),
+ ErrorKind::NotFound => return Ok(()),
_ => return Err(e).context("In remove_legacy_keystore_entry."),
}
}
let user_id = uid_to_android_user(uid);
self.remove_user_dir_if_empty(user_id)
- .context("In remove_legacy_keystore_entry: Trying to remove empty user dir.")?;
- Ok(true)
+ .context("In remove_legacy_keystore_entry: Trying to remove empty user dir.")
}
/// List all entries belonging to the given uid.
@@ -1017,7 +836,7 @@ impl LegacyBlobLoader {
// in are all in the printable range that don't get mangled.
for prefix in Self::KNOWN_KEYSTORE_PREFIXES {
if let Some(alias) = encoded_alias.strip_prefix(prefix) {
- return Self::decode_alias(alias).ok();
+ return Self::decode_alias(&alias).ok();
}
}
None
@@ -1169,88 +988,6 @@ impl LegacyBlobLoader {
Ok(something_was_deleted)
}
- /// This function moves a keystore file if it exists. It constructs the source and destination
- /// file name using the make_filename function with the arguments uid, alias, and prefix.
- /// The function overwrites existing destination files silently. If the source does not exist,
- /// this function has no side effect and returns successfully.
- fn move_keystore_file_if_exists<F>(
- src_uid: u32,
- dest_uid: u32,
- src_alias: &str,
- dest_alias: &str,
- prefix: &str,
- make_filename: F,
- ) -> Result<()>
- where
- F: Fn(u32, &str, &str) -> PathBuf,
- {
- let src_path = make_filename(src_uid, src_alias, prefix);
- let dest_path = make_filename(dest_uid, dest_alias, prefix);
- match Self::with_retry_interrupted(|| fs::rename(&src_path, &dest_path)) {
- Err(e) if e.kind() == ErrorKind::NotFound => Ok(()),
- r => r.context("In move_keystore_file_if_exists: Trying to rename."),
- }
- }
-
- /// Moves a keystore entry from one uid to another. The uids must have the same android user
- /// component. Moves across android users are not permitted.
- pub fn move_keystore_entry(
- &self,
- src_uid: u32,
- dest_uid: u32,
- src_alias: &str,
- dest_alias: &str,
- ) -> Result<()> {
- if src_uid == dest_uid {
- // Nothing to do in the trivial case.
- return Ok(());
- }
-
- if uid_to_android_user(src_uid) != uid_to_android_user(dest_uid) {
- return Err(Error::AndroidUserMismatch).context("In move_keystore_entry.");
- }
-
- let prefixes = ["USRPKEY", "USRSKEY", "USRCERT", "CACERT"];
- for prefix in prefixes {
- Self::move_keystore_file_if_exists(
- src_uid,
- dest_uid,
- src_alias,
- dest_alias,
- prefix,
- |uid, alias, prefix| self.make_blob_filename(uid, alias, prefix),
- )
- .with_context(|| {
- format!(
- "In move_keystore_entry: Trying to move blob file with prefix: \"{}\"",
- prefix
- )
- })?;
- }
-
- let prefixes = ["USRPKEY", "USRSKEY"];
-
- for prefix in prefixes {
- Self::move_keystore_file_if_exists(
- src_uid,
- dest_uid,
- src_alias,
- dest_alias,
- prefix,
- |uid, alias, prefix| self.make_chr_filename(uid, alias, prefix),
- )
- .with_context(|| {
- format!(
- "In move_keystore_entry: Trying to move characteristics file with \
- prefix: \"{}\"",
- prefix
- )
- })?;
- }
-
- Ok(())
- }
-
fn remove_user_dir_if_empty(&self, user_id: u32) -> Result<()> {
if self
.is_empty_user(user_id)
@@ -1267,66 +1004,79 @@ impl LegacyBlobLoader {
&self,
uid: u32,
alias: &str,
- super_key: &Option<Arc<dyn AesGcm>>,
- ) -> Result<(Option<(Blob, LegacyKeyCharacteristics)>, Option<Vec<u8>>, Option<Vec<u8>>)> {
+ key_manager: Option<&SuperKeyManager>,
+ ) -> Result<(Option<(Blob, Vec<KeyParameter>)>, Option<Vec<u8>>, Option<Vec<u8>>)> {
let km_blob = self.read_km_blob_file(uid, alias).context("In load_by_uid_alias.")?;
let km_blob = match km_blob {
Some((km_blob, prefix)) => {
- let km_blob =
- match km_blob {
- Blob { flags: _, value: BlobValue::Decrypted(_) }
- | Blob { flags: _, value: BlobValue::Encrypted { .. } } => km_blob,
- _ => return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(
+ let km_blob = match km_blob {
+ Blob { flags: _, value: BlobValue::Decrypted(_) } => km_blob,
+ // Unwrap the key blob if required and if we have key_manager.
+ Blob { flags, value: BlobValue::Encrypted { ref iv, ref tag, ref data } } => {
+ if let Some(key_manager) = key_manager {
+ let decrypted = match key_manager
+ .get_per_boot_key_by_user_id(uid_to_android_user(uid))
+ {
+ Some(key) => key.aes_gcm_decrypt(data, iv, tag).context(
+ "In load_by_uid_alias: while trying to decrypt legacy blob.",
+ )?,
+ None => {
+ return Err(KsError::Rc(ResponseCode::LOCKED)).context(format!(
+ concat!(
+ "In load_by_uid_alias: ",
+ "User {} has not unlocked the keystore yet.",
+ ),
+ uid_to_android_user(uid)
+ ))
+ }
+ };
+ Blob { flags, value: BlobValue::Decrypted(decrypted) }
+ } else {
+ km_blob
+ }
+ }
+ _ => {
+ return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(
"In load_by_uid_alias: Found wrong blob type in legacy key blob file.",
- ),
- };
+ )
+ }
+ };
let hw_sec_level = match km_blob.is_strongbox() {
true => SecurityLevel::STRONGBOX,
false => SecurityLevel::TRUSTED_ENVIRONMENT,
};
let key_parameters = self
- .read_characteristics_file(uid, &prefix, alias, hw_sec_level, super_key)
+ .read_characteristics_file(uid, &prefix, alias, hw_sec_level)
.context("In load_by_uid_alias.")?;
Some((km_blob, key_parameters))
}
None => None,
};
- let user_cert_blob =
- Self::read_generic_blob(&self.make_blob_filename(uid, alias, "USRCERT"))
- .context("In load_by_uid_alias: While loading user cert.")?;
-
- let user_cert = if let Some(blob) = user_cert_blob {
- let blob = Self::decrypt_if_required(super_key, blob)
- .context("In load_by_uid_alias: While decrypting user cert.")?;
-
- if let Blob { value: BlobValue::Generic(data), .. } = blob {
- Some(data)
- } else {
- return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
- .context("In load_by_uid_alias: Found unexpected blob type in USRCERT file");
- }
- } else {
- None
- };
-
- let ca_cert_blob = Self::read_generic_blob(&self.make_blob_filename(uid, alias, "CACERT"))
- .context("In load_by_uid_alias: While loading ca cert.")?;
-
- let ca_cert = if let Some(blob) = ca_cert_blob {
- let blob = Self::decrypt_if_required(super_key, blob)
- .context("In load_by_uid_alias: While decrypting ca cert.")?;
+ let user_cert =
+ match Self::read_generic_blob(&self.make_blob_filename(uid, alias, "USRCERT"))
+ .context("In load_by_uid_alias: While loading user cert.")?
+ {
+ Some(Blob { value: BlobValue::Generic(data), .. }) => Some(data),
+ None => None,
+ _ => {
+ return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(
+ "In load_by_uid_alias: Found unexpected blob type in USRCERT file",
+ )
+ }
+ };
- if let Blob { value: BlobValue::Generic(data), .. } = blob {
- Some(data)
- } else {
+ let ca_cert = match Self::read_generic_blob(&self.make_blob_filename(uid, alias, "CACERT"))
+ .context("In load_by_uid_alias: While loading ca cert.")?
+ {
+ Some(Blob { value: BlobValue::Generic(data), .. }) => Some(data),
+ None => None,
+ _ => {
return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
- .context("In load_by_uid_alias: Found unexpected blob type in CACERT file");
+ .context("In load_by_uid_alias: Found unexpected blob type in CACERT file")
}
- } else {
- None
};
Ok((km_blob, user_cert, ca_cert))
@@ -1387,318 +1137,17 @@ impl LegacyBlobLoader {
}
}
-/// This module implements utility apis for creating legacy blob files.
-#[cfg(feature = "keystore2_blob_test_utils")]
-pub mod test_utils {
- #![allow(dead_code)]
-
- /// test vectors for legacy key blobs
- pub mod legacy_blob_test_vectors;
-
- use crate::legacy_blob::blob_types::{
- GENERIC, KEY_CHARACTERISTICS, KEY_CHARACTERISTICS_CACHE, KM_BLOB, SUPER_KEY,
- SUPER_KEY_AES256,
- };
- use crate::legacy_blob::*;
- use anyhow::{anyhow, Result};
- use keystore2_crypto::{aes_gcm_decrypt, aes_gcm_encrypt};
- use std::convert::TryInto;
- use std::fs::OpenOptions;
- use std::io::Write;
-
- /// This function takes a blob and synchronizes the encrypted/super encrypted flags
- /// with the blob type for the pairs Generic/EncryptedGeneric,
- /// Characteristics/EncryptedCharacteristics and Encrypted/Decrypted.
- /// E.g. if a non encrypted enum variant is encountered with flags::SUPER_ENCRYPTED
- /// or flags::ENCRYPTED is set, the payload is encrypted and the corresponding
- /// encrypted variant is returned, and vice versa. All other variants remain untouched
- /// even if flags and BlobValue variant are inconsistent.
- pub fn prepare_blob(blob: Blob, key: &[u8]) -> Result<Blob> {
- match blob {
- Blob { value: BlobValue::Generic(data), flags } if blob.is_encrypted() => {
- let (ciphertext, iv, tag) = aes_gcm_encrypt(&data, key).unwrap();
- Ok(Blob { value: BlobValue::EncryptedGeneric { data: ciphertext, iv, tag }, flags })
- }
- Blob { value: BlobValue::Characteristics(data), flags } if blob.is_encrypted() => {
- let (ciphertext, iv, tag) = aes_gcm_encrypt(&data, key).unwrap();
- Ok(Blob {
- value: BlobValue::EncryptedCharacteristics { data: ciphertext, iv, tag },
- flags,
- })
- }
- Blob { value: BlobValue::Decrypted(data), flags } if blob.is_encrypted() => {
- let (ciphertext, iv, tag) = aes_gcm_encrypt(&data, key).unwrap();
- Ok(Blob { value: BlobValue::Encrypted { data: ciphertext, iv, tag }, flags })
- }
- Blob { value: BlobValue::EncryptedGeneric { data, iv, tag }, flags }
- if !blob.is_encrypted() =>
- {
- let plaintext = aes_gcm_decrypt(&data, &iv, &tag, key).unwrap();
- Ok(Blob { value: BlobValue::Generic(plaintext[..].to_vec()), flags })
- }
- Blob { value: BlobValue::EncryptedCharacteristics { data, iv, tag }, flags }
- if !blob.is_encrypted() =>
- {
- let plaintext = aes_gcm_decrypt(&data, &iv, &tag, key).unwrap();
- Ok(Blob { value: BlobValue::Characteristics(plaintext[..].to_vec()), flags })
- }
- Blob { value: BlobValue::Encrypted { data, iv, tag }, flags }
- if !blob.is_encrypted() =>
- {
- let plaintext = aes_gcm_decrypt(&data, &iv, &tag, key).unwrap();
- Ok(Blob { value: BlobValue::Decrypted(plaintext), flags })
- }
- _ => Ok(blob),
- }
- }
-
- /// Legacy blob header structure.
- pub struct LegacyBlobHeader {
- version: u8,
- blob_type: u8,
- flags: u8,
- info: u8,
- iv: [u8; 12],
- tag: [u8; 16],
- blob_size: u32,
- }
-
- /// This function takes a Blob and writes it to out as a legacy blob file
- /// version 3. Note that the flags field and the values field may be
- /// inconsistent and could be sanitized by this function. It is intentionally
- /// not done to enable tests to construct malformed blobs.
- pub fn write_legacy_blob(out: &mut dyn Write, blob: Blob) -> Result<usize> {
- let (header, data, salt) = match blob {
- Blob { value: BlobValue::Generic(data), flags } => (
- LegacyBlobHeader {
- version: 3,
- blob_type: GENERIC,
- flags,
- info: 0,
- iv: [0u8; 12],
- tag: [0u8; 16],
- blob_size: data.len() as u32,
- },
- data,
- None,
- ),
- Blob { value: BlobValue::Characteristics(data), flags } => (
- LegacyBlobHeader {
- version: 3,
- blob_type: KEY_CHARACTERISTICS,
- flags,
- info: 0,
- iv: [0u8; 12],
- tag: [0u8; 16],
- blob_size: data.len() as u32,
- },
- data,
- None,
- ),
- Blob { value: BlobValue::CharacteristicsCache(data), flags } => (
- LegacyBlobHeader {
- version: 3,
- blob_type: KEY_CHARACTERISTICS_CACHE,
- flags,
- info: 0,
- iv: [0u8; 12],
- tag: [0u8; 16],
- blob_size: data.len() as u32,
- },
- data,
- None,
- ),
- Blob { value: BlobValue::PwEncrypted { iv, tag, data, salt, key_size }, flags } => (
- LegacyBlobHeader {
- version: 3,
- blob_type: if key_size == keystore2_crypto::AES_128_KEY_LENGTH {
- SUPER_KEY
- } else {
- SUPER_KEY_AES256
- },
- flags,
- info: 0,
- iv: iv.try_into().unwrap(),
- tag: tag[..].try_into().unwrap(),
- blob_size: data.len() as u32,
- },
- data,
- Some(salt),
- ),
- Blob { value: BlobValue::Encrypted { iv, tag, data }, flags } => (
- LegacyBlobHeader {
- version: 3,
- blob_type: KM_BLOB,
- flags,
- info: 0,
- iv: iv.try_into().unwrap(),
- tag: tag[..].try_into().unwrap(),
- blob_size: data.len() as u32,
- },
- data,
- None,
- ),
- Blob { value: BlobValue::EncryptedGeneric { iv, tag, data }, flags } => (
- LegacyBlobHeader {
- version: 3,
- blob_type: GENERIC,
- flags,
- info: 0,
- iv: iv.try_into().unwrap(),
- tag: tag[..].try_into().unwrap(),
- blob_size: data.len() as u32,
- },
- data,
- None,
- ),
- Blob { value: BlobValue::EncryptedCharacteristics { iv, tag, data }, flags } => (
- LegacyBlobHeader {
- version: 3,
- blob_type: KEY_CHARACTERISTICS,
- flags,
- info: 0,
- iv: iv.try_into().unwrap(),
- tag: tag[..].try_into().unwrap(),
- blob_size: data.len() as u32,
- },
- data,
- None,
- ),
- Blob { value: BlobValue::Decrypted(data), flags } => (
- LegacyBlobHeader {
- version: 3,
- blob_type: KM_BLOB,
- flags,
- info: 0,
- iv: [0u8; 12],
- tag: [0u8; 16],
- blob_size: data.len() as u32,
- },
- data[..].to_vec(),
- None,
- ),
- };
- write_legacy_blob_helper(out, &header, &data, salt.as_deref())
- }
-
- /// This function takes LegacyBlobHeader, blob payload and writes it to out as a legacy blob file
- /// version 3.
- pub fn write_legacy_blob_helper(
- out: &mut dyn Write,
- header: &LegacyBlobHeader,
- data: &[u8],
- info: Option<&[u8]>,
- ) -> Result<usize> {
- if 1 != out.write(&[header.version])? {
- return Err(anyhow!("Unexpected size while writing version."));
- }
- if 1 != out.write(&[header.blob_type])? {
- return Err(anyhow!("Unexpected size while writing blob_type."));
- }
- if 1 != out.write(&[header.flags])? {
- return Err(anyhow!("Unexpected size while writing flags."));
- }
- if 1 != out.write(&[header.info])? {
- return Err(anyhow!("Unexpected size while writing info."));
- }
- if 12 != out.write(&header.iv)? {
- return Err(anyhow!("Unexpected size while writing iv."));
- }
- if 4 != out.write(&[0u8; 4])? {
- return Err(anyhow!("Unexpected size while writing last 4 bytes of iv."));
- }
- if 16 != out.write(&header.tag)? {
- return Err(anyhow!("Unexpected size while writing tag."));
- }
- if 4 != out.write(&header.blob_size.to_be_bytes())? {
- return Err(anyhow!("Unexpected size while writing blob size."));
- }
- if data.len() != out.write(data)? {
- return Err(anyhow!("Unexpected size while writing blob."));
- }
- if let Some(info) = info {
- if info.len() != out.write(info)? {
- return Err(anyhow!("Unexpected size while writing inof."));
- }
- }
- Ok(40 + data.len() + info.map(|v| v.len()).unwrap_or(0))
- }
-
- /// Create encrypted characteristics file using given key.
- pub fn make_encrypted_characteristics_file<P: AsRef<Path>>(
- path: P,
- key: &[u8],
- data: &[u8],
- ) -> Result<()> {
- let mut file = OpenOptions::new().write(true).create_new(true).open(path).unwrap();
- let blob =
- Blob { value: BlobValue::Characteristics(data.to_vec()), flags: flags::ENCRYPTED };
- let blob = prepare_blob(blob, key).unwrap();
- write_legacy_blob(&mut file, blob).unwrap();
- Ok(())
- }
-
- /// Create encrypted user certificate file using given key.
- pub fn make_encrypted_usr_cert_file<P: AsRef<Path>>(
- path: P,
- key: &[u8],
- data: &[u8],
- ) -> Result<()> {
- let mut file = OpenOptions::new().write(true).create_new(true).open(path).unwrap();
- let blob = Blob { value: BlobValue::Generic(data.to_vec()), flags: flags::ENCRYPTED };
- let blob = prepare_blob(blob, key).unwrap();
- write_legacy_blob(&mut file, blob).unwrap();
- Ok(())
- }
-
- /// Create encrypted CA certificate file using given key.
- pub fn make_encrypted_ca_cert_file<P: AsRef<Path>>(
- path: P,
- key: &[u8],
- data: &[u8],
- ) -> Result<()> {
- let mut file = OpenOptions::new().write(true).create_new(true).open(path).unwrap();
- let blob = Blob { value: BlobValue::Generic(data.to_vec()), flags: flags::ENCRYPTED };
- let blob = prepare_blob(blob, key).unwrap();
- write_legacy_blob(&mut file, blob).unwrap();
- Ok(())
- }
-
- /// Create encrypted user key file using given key.
- pub fn make_encrypted_key_file<P: AsRef<Path>>(path: P, key: &[u8], data: &[u8]) -> Result<()> {
- let mut file = OpenOptions::new().write(true).create_new(true).open(path).unwrap();
- let blob = Blob {
- value: BlobValue::Decrypted(ZVec::try_from(data).unwrap()),
- flags: flags::ENCRYPTED,
- };
- let blob = prepare_blob(blob, key).unwrap();
- write_legacy_blob(&mut file, blob).unwrap();
- Ok(())
- }
-
- /// Create user or ca cert blob file.
- pub fn make_cert_blob_file<P: AsRef<Path>>(path: P, data: &[u8]) -> Result<()> {
- let mut file = OpenOptions::new().write(true).create_new(true).open(path).unwrap();
- let blob = Blob { value: BlobValue::Generic(data.to_vec()), flags: 0 };
- let blob = prepare_blob(blob, &[]).unwrap();
- write_legacy_blob(&mut file, blob).unwrap();
- Ok(())
- }
-}
-
#[cfg(test)]
mod test {
- #![allow(dead_code)]
use super::*;
- use crate::legacy_blob::test_utils::legacy_blob_test_vectors::*;
- use crate::legacy_blob::test_utils::*;
- use anyhow::{anyhow, Result};
+ use anyhow::anyhow;
use keystore2_crypto::aes_gcm_decrypt;
- use keystore2_test_utils::TempDir;
use rand::Rng;
- use std::convert::TryInto;
- use std::ops::Deref;
use std::string::FromUtf8Error;
+ mod legacy_blob_test_vectors;
+ use crate::error;
+ use crate::legacy_blob::test::legacy_blob_test_vectors::*;
+ use keystore2_test_utils::TempDir;
#[test]
fn decode_encode_alias_test() {
@@ -1754,8 +1203,7 @@ mod test {
fn read_golden_key_blob_test() -> anyhow::Result<()> {
let blob = LegacyBlobLoader::new_from_stream_decrypt_with(&mut &*BLOB, |_, _, _, _, _| {
Err(anyhow!("should not be called"))
- })
- .unwrap();
+ })?;
assert!(!blob.is_encrypted());
assert!(!blob.is_fallback());
assert!(!blob.is_strongbox());
@@ -1765,8 +1213,7 @@ mod test {
let blob = LegacyBlobLoader::new_from_stream_decrypt_with(
&mut &*REAL_LEGACY_BLOB,
|_, _, _, _, _| Err(anyhow!("should not be called")),
- )
- .unwrap();
+ )?;
assert!(!blob.is_encrypted());
assert!(!blob.is_fallback());
assert!(!blob.is_strongbox());
@@ -1854,75 +1301,62 @@ mod test {
#[test]
fn test_legacy_blobs() -> anyhow::Result<()> {
- let temp_dir = TempDir::new("legacy_blob_test").unwrap();
- std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
+ let temp_dir = TempDir::new("legacy_blob_test")?;
+ std::fs::create_dir(&*temp_dir.build().push("user_0"))?;
- std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
+ std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY)?;
std::fs::write(
&*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
USRPKEY_AUTHBOUND,
- )
- .unwrap();
+ )?;
std::fs::write(
&*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
USRPKEY_AUTHBOUND_CHR,
- )
- .unwrap();
+ )?;
std::fs::write(
&*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
USRCERT_AUTHBOUND,
- )
- .unwrap();
+ )?;
std::fs::write(
&*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
CACERT_AUTHBOUND,
- )
- .unwrap();
+ )?;
std::fs::write(
&*temp_dir.build().push("user_0").push("10223_USRPKEY_non_authbound"),
USRPKEY_NON_AUTHBOUND,
- )
- .unwrap();
+ )?;
std::fs::write(
&*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_non_authbound"),
USRPKEY_NON_AUTHBOUND_CHR,
- )
- .unwrap();
+ )?;
std::fs::write(
&*temp_dir.build().push("user_0").push("10223_USRCERT_non_authbound"),
USRCERT_NON_AUTHBOUND,
- )
- .unwrap();
+ )?;
std::fs::write(
&*temp_dir.build().push("user_0").push("10223_CACERT_non_authbound"),
CACERT_NON_AUTHBOUND,
- )
- .unwrap();
+ )?;
+ let key_manager: SuperKeyManager = Default::default();
+ let mut db = crate::database::KeystoreDB::new(temp_dir.path(), None)?;
let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
- if let (Some((Blob { flags, value }, _params)), Some(cert), Some(chain)) =
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None)?
- {
- assert_eq!(flags, 4);
- assert_eq!(
- value,
- BlobValue::Encrypted {
- data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
- iv: USRPKEY_AUTHBOUND_IV.to_vec(),
- tag: USRPKEY_AUTHBOUND_TAG.to_vec()
- }
- );
- assert_eq!(&cert[..], LOADED_CERT_AUTHBOUND);
- assert_eq!(&chain[..], LOADED_CACERT_AUTHBOUND);
- } else {
- panic!("");
- }
+ assert_eq!(
+ legacy_blob_loader
+ .load_by_uid_alias(10223, "authbound", Some(&key_manager))
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<error::Error>(),
+ Some(&error::Error::Rc(ResponseCode::LOCKED))
+ );
+
+ key_manager.unlock_user_key(&mut db, 0, &(PASSWORD.into()), &legacy_blob_loader)?;
if let (Some((Blob { flags, value: _ }, _params)), Some(cert), Some(chain)) =
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None)?
+ legacy_blob_loader.load_by_uid_alias(10223, "authbound", Some(&key_manager))?
{
assert_eq!(flags, 4);
//assert_eq!(value, BlobValue::Encrypted(..));
@@ -1932,7 +1366,7 @@ mod test {
panic!("");
}
if let (Some((Blob { flags, value }, _params)), Some(cert), Some(chain)) =
- legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", &None)?
+ legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", Some(&key_manager))?
{
assert_eq!(flags, 0);
assert_eq!(value, BlobValue::Decrypted(LOADED_USRPKEY_NON_AUTHBOUND.try_into()?));
@@ -1949,11 +1383,11 @@ mod test {
assert_eq!(
(None, None, None),
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None)?
+ legacy_blob_loader.load_by_uid_alias(10223, "authbound", Some(&key_manager))?
);
assert_eq!(
(None, None, None),
- legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", &None)?
+ legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", Some(&key_manager))?
);
// The database should not be empty due to the super key.
@@ -1972,319 +1406,9 @@ mod test {
Ok(())
}
- struct TestKey(ZVec);
-
- impl crate::utils::AesGcmKey for TestKey {
- fn key(&self) -> &[u8] {
- &self.0
- }
- }
-
- impl Deref for TestKey {
- type Target = [u8];
- fn deref(&self) -> &Self::Target {
- &self.0
- }
- }
-
- #[test]
- fn test_with_encrypted_characteristics() -> anyhow::Result<()> {
- let temp_dir = TempDir::new("test_with_encrypted_characteristics").unwrap();
- std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
-
- let pw: Password = PASSWORD.into();
- let pw_key = TestKey(pw.derive_key(Some(SUPERKEY_SALT), 32).unwrap());
- let super_key =
- Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
-
- std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
-
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
- USRPKEY_AUTHBOUND,
- )
- .unwrap();
- make_encrypted_characteristics_file(
- &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
- &super_key,
- KEY_PARAMETERS,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
- USRCERT_AUTHBOUND,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
- CACERT_AUTHBOUND,
- )
- .unwrap();
-
- let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
-
- assert_eq!(
- legacy_blob_loader
- .load_by_uid_alias(10223, "authbound", &None)
- .unwrap_err()
- .root_cause()
- .downcast_ref::<Error>(),
- Some(&Error::LockedComponent)
- );
-
- assert_eq!(
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &Some(super_key)).unwrap(),
- (
- Some((
- Blob {
- flags: 4,
- value: BlobValue::Encrypted {
- data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
- iv: USRPKEY_AUTHBOUND_IV.to_vec(),
- tag: USRPKEY_AUTHBOUND_TAG.to_vec()
- }
- },
- structured_test_params()
- )),
- Some(LOADED_CERT_AUTHBOUND.to_vec()),
- Some(LOADED_CACERT_AUTHBOUND.to_vec())
- )
- );
-
- legacy_blob_loader.remove_keystore_entry(10223, "authbound").expect("This should succeed.");
-
- assert_eq!(
- (None, None, None),
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None).unwrap()
- );
-
- // The database should not be empty due to the super key.
- assert!(!legacy_blob_loader.is_empty().unwrap());
- assert!(!legacy_blob_loader.is_empty_user(0).unwrap());
-
- // The database should be considered empty for user 1.
- assert!(legacy_blob_loader.is_empty_user(1).unwrap());
-
- legacy_blob_loader.remove_super_key(0);
-
- // Now it should be empty.
- assert!(legacy_blob_loader.is_empty_user(0).unwrap());
- assert!(legacy_blob_loader.is_empty().unwrap());
-
- Ok(())
- }
-
- #[test]
- fn test_with_encrypted_certificates() -> anyhow::Result<()> {
- let temp_dir = TempDir::new("test_with_encrypted_certificates").unwrap();
- std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
-
- let pw: Password = PASSWORD.into();
- let pw_key = TestKey(pw.derive_key(Some(SUPERKEY_SALT), 32).unwrap());
- let super_key =
- Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
-
- std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
-
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
- USRPKEY_AUTHBOUND,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
- USRPKEY_AUTHBOUND_CHR,
- )
- .unwrap();
- make_encrypted_usr_cert_file(
- &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
- &super_key,
- LOADED_CERT_AUTHBOUND,
- )
- .unwrap();
- make_encrypted_ca_cert_file(
- &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
- &super_key,
- LOADED_CACERT_AUTHBOUND,
- )
- .unwrap();
-
- let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
-
- assert_eq!(
- legacy_blob_loader
- .load_by_uid_alias(10223, "authbound", &None)
- .unwrap_err()
- .root_cause()
- .downcast_ref::<Error>(),
- Some(&Error::LockedComponent)
- );
-
- assert_eq!(
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &Some(super_key)).unwrap(),
- (
- Some((
- Blob {
- flags: 4,
- value: BlobValue::Encrypted {
- data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
- iv: USRPKEY_AUTHBOUND_IV.to_vec(),
- tag: USRPKEY_AUTHBOUND_TAG.to_vec()
- }
- },
- structured_test_params_cache()
- )),
- Some(LOADED_CERT_AUTHBOUND.to_vec()),
- Some(LOADED_CACERT_AUTHBOUND.to_vec())
- )
- );
-
- legacy_blob_loader.remove_keystore_entry(10223, "authbound").expect("This should succeed.");
-
- assert_eq!(
- (None, None, None),
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None).unwrap()
- );
-
- // The database should not be empty due to the super key.
- assert!(!legacy_blob_loader.is_empty().unwrap());
- assert!(!legacy_blob_loader.is_empty_user(0).unwrap());
-
- // The database should be considered empty for user 1.
- assert!(legacy_blob_loader.is_empty_user(1).unwrap());
-
- legacy_blob_loader.remove_super_key(0);
-
- // Now it should be empty.
- assert!(legacy_blob_loader.is_empty_user(0).unwrap());
- assert!(legacy_blob_loader.is_empty().unwrap());
-
- Ok(())
- }
-
- #[test]
- fn test_in_place_key_migration() -> anyhow::Result<()> {
- let temp_dir = TempDir::new("test_in_place_key_migration").unwrap();
- std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
-
- let pw: Password = PASSWORD.into();
- let pw_key = TestKey(pw.derive_key(Some(SUPERKEY_SALT), 32).unwrap());
- let super_key =
- Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
-
- std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
-
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
- USRPKEY_AUTHBOUND,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
- USRPKEY_AUTHBOUND_CHR,
- )
- .unwrap();
- make_encrypted_usr_cert_file(
- &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
- &super_key,
- LOADED_CERT_AUTHBOUND,
- )
- .unwrap();
- make_encrypted_ca_cert_file(
- &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
- &super_key,
- LOADED_CACERT_AUTHBOUND,
- )
- .unwrap();
-
- let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
-
- assert_eq!(
- legacy_blob_loader
- .load_by_uid_alias(10223, "authbound", &None)
- .unwrap_err()
- .root_cause()
- .downcast_ref::<Error>(),
- Some(&Error::LockedComponent)
- );
-
- let super_key: Option<Arc<dyn AesGcm>> = Some(super_key);
-
- assert_eq!(
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &super_key).unwrap(),
- (
- Some((
- Blob {
- flags: 4,
- value: BlobValue::Encrypted {
- data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
- iv: USRPKEY_AUTHBOUND_IV.to_vec(),
- tag: USRPKEY_AUTHBOUND_TAG.to_vec()
- }
- },
- structured_test_params_cache()
- )),
- Some(LOADED_CERT_AUTHBOUND.to_vec()),
- Some(LOADED_CACERT_AUTHBOUND.to_vec())
- )
- );
-
- legacy_blob_loader.move_keystore_entry(10223, 10224, "authbound", "boundauth").unwrap();
-
- assert_eq!(
- legacy_blob_loader
- .load_by_uid_alias(10224, "boundauth", &None)
- .unwrap_err()
- .root_cause()
- .downcast_ref::<Error>(),
- Some(&Error::LockedComponent)
- );
-
- assert_eq!(
- legacy_blob_loader.load_by_uid_alias(10224, "boundauth", &super_key).unwrap(),
- (
- Some((
- Blob {
- flags: 4,
- value: BlobValue::Encrypted {
- data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
- iv: USRPKEY_AUTHBOUND_IV.to_vec(),
- tag: USRPKEY_AUTHBOUND_TAG.to_vec()
- }
- },
- structured_test_params_cache()
- )),
- Some(LOADED_CERT_AUTHBOUND.to_vec()),
- Some(LOADED_CACERT_AUTHBOUND.to_vec())
- )
- );
-
- legacy_blob_loader.remove_keystore_entry(10224, "boundauth").expect("This should succeed.");
-
- assert_eq!(
- (None, None, None),
- legacy_blob_loader.load_by_uid_alias(10224, "boundauth", &None).unwrap()
- );
-
- // The database should not be empty due to the super key.
- assert!(!legacy_blob_loader.is_empty().unwrap());
- assert!(!legacy_blob_loader.is_empty_user(0).unwrap());
-
- // The database should be considered empty for user 1.
- assert!(legacy_blob_loader.is_empty_user(1).unwrap());
-
- legacy_blob_loader.remove_super_key(0);
-
- // Now it should be empty.
- assert!(legacy_blob_loader.is_empty_user(0).unwrap());
- assert!(legacy_blob_loader.is_empty().unwrap());
-
- Ok(())
- }
-
#[test]
fn list_non_existing_user() -> Result<()> {
- let temp_dir = TempDir::new("list_non_existing_user").unwrap();
+ let temp_dir = TempDir::new("list_non_existing_user")?;
let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
assert!(legacy_blob_loader.list_user(20)?.is_empty());
@@ -2294,66 +1418,11 @@ mod test {
#[test]
fn list_legacy_keystore_entries_on_non_existing_user() -> Result<()> {
- let temp_dir = TempDir::new("list_legacy_keystore_entries_on_non_existing_user").unwrap();
+ let temp_dir = TempDir::new("list_legacy_keystore_entries_on_non_existing_user")?;
let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
assert!(legacy_blob_loader.list_legacy_keystore_entries_for_user(20)?.is_empty());
Ok(())
}
-
- #[test]
- fn test_move_keystore_entry() {
- let temp_dir = TempDir::new("test_move_keystore_entry").unwrap();
- std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
-
- const SOME_CONTENT: &[u8] = b"some content";
- const ANOTHER_CONTENT: &[u8] = b"another content";
- const SOME_FILENAME: &str = "some_file";
- const ANOTHER_FILENAME: &str = "another_file";
-
- std::fs::write(&*temp_dir.build().push("user_0").push(SOME_FILENAME), SOME_CONTENT)
- .unwrap();
-
- std::fs::write(&*temp_dir.build().push("user_0").push(ANOTHER_FILENAME), ANOTHER_CONTENT)
- .unwrap();
-
- // Non existent source id silently ignored.
- assert!(LegacyBlobLoader::move_keystore_file_if_exists(
- 1,
- 2,
- "non_existent",
- ANOTHER_FILENAME,
- "ignored",
- |_, alias, _| temp_dir.build().push("user_0").push(alias).to_path_buf()
- )
- .is_ok());
-
- // Content of another_file has not changed.
- let another_content =
- std::fs::read(&*temp_dir.build().push("user_0").push(ANOTHER_FILENAME)).unwrap();
- assert_eq!(&another_content, ANOTHER_CONTENT);
-
- // Check that some_file still exists.
- assert!(temp_dir.build().push("user_0").push(SOME_FILENAME).exists());
- // Existing target files are silently overwritten.
-
- assert!(LegacyBlobLoader::move_keystore_file_if_exists(
- 1,
- 2,
- SOME_FILENAME,
- ANOTHER_FILENAME,
- "ignored",
- |_, alias, _| temp_dir.build().push("user_0").push(alias).to_path_buf()
- )
- .is_ok());
-
- // Content of another_file is now "some content".
- let another_content =
- std::fs::read(&*temp_dir.build().push("user_0").push(ANOTHER_FILENAME)).unwrap();
- assert_eq!(&another_content, SOME_CONTENT);
-
- // Check that some_file no longer exists.
- assert!(!temp_dir.build().push("user_0").push(SOME_FILENAME).exists());
- }
}
diff --git a/keystore2/src/legacy_blob/test_utils/legacy_blob_test_vectors.rs b/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs
index 3eecee08..14bd40ca 100644
--- a/keystore2/src/legacy_blob/test_utils/legacy_blob_test_vectors.rs
+++ b/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs
@@ -12,15 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use crate::key_parameter::{KeyParameter, KeyParameterValue};
-use crate::legacy_blob::LegacyKeyCharacteristics;
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve,
- HardwareAuthenticatorType::HardwareAuthenticatorType, KeyOrigin::KeyOrigin,
- KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
-};
-
-/// Holds Blob structure.
pub static BLOB: &[u8] = &[
3, // version
1, // type
@@ -31,109 +22,6 @@ pub static BLOB: &[u8] = &[
0, 0, 0, 4, // length in big endian
0xde, 0xed, 0xbe, 0xef, // payload
];
-
-/// Creates LegacyKeyCharacteristics with security level KEYSTORE.
-pub fn structured_test_params() -> LegacyKeyCharacteristics {
- LegacyKeyCharacteristics::File(vec![
- KeyParameter::new(KeyParameterValue::KeyPurpose(KeyPurpose::SIGN), SecurityLevel::KEYSTORE),
- KeyParameter::new(
- KeyParameterValue::KeyPurpose(KeyPurpose::VERIFY),
- SecurityLevel::KEYSTORE,
- ),
- KeyParameter::new(KeyParameterValue::Digest(Digest::SHA_2_256), SecurityLevel::KEYSTORE),
- KeyParameter::new(
- KeyParameterValue::UserSecureID(2100322049669824240),
- SecurityLevel::KEYSTORE,
- ),
- KeyParameter::new(KeyParameterValue::Algorithm(Algorithm::EC), SecurityLevel::KEYSTORE),
- KeyParameter::new(KeyParameterValue::KeySize(256), SecurityLevel::KEYSTORE),
- KeyParameter::new(KeyParameterValue::EcCurve(EcCurve::P_256), SecurityLevel::KEYSTORE),
- KeyParameter::new(
- KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType::FINGERPRINT),
- SecurityLevel::KEYSTORE,
- ),
- KeyParameter::new(
- KeyParameterValue::KeyOrigin(KeyOrigin::GENERATED),
- SecurityLevel::KEYSTORE,
- ),
- KeyParameter::new(KeyParameterValue::OSVersion(110000), SecurityLevel::KEYSTORE),
- KeyParameter::new(KeyParameterValue::OSPatchLevel(202101), SecurityLevel::KEYSTORE),
- KeyParameter::new(KeyParameterValue::BootPatchLevel(20210105), SecurityLevel::KEYSTORE),
- KeyParameter::new(KeyParameterValue::VendorPatchLevel(20210105), SecurityLevel::KEYSTORE),
- ])
-}
-
-/// Creates LegacyKeyCharacteristics with security level TRUSTED_ENVIRONMENT.
-pub fn structured_test_params_cache() -> LegacyKeyCharacteristics {
- LegacyKeyCharacteristics::Cache(vec![
- KeyParameter::new(
- KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::KeyPurpose(KeyPurpose::VERIFY),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::Digest(Digest::SHA_2_256),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::UserSecureID(2100322049669824240),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::Algorithm(Algorithm::EC),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::KeySize(256), SecurityLevel::TRUSTED_ENVIRONMENT),
- KeyParameter::new(
- KeyParameterValue::EcCurve(EcCurve::P_256),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType::FINGERPRINT),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::KeyOrigin(KeyOrigin::GENERATED),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::OSVersion(110000), SecurityLevel::TRUSTED_ENVIRONMENT),
- KeyParameter::new(
- KeyParameterValue::OSPatchLevel(202101),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::BootPatchLevel(20210105),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::VendorPatchLevel(20210105),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::CreationDateTime(1607149002000),
- SecurityLevel::KEYSTORE,
- ),
- KeyParameter::new(KeyParameterValue::UserID(0), SecurityLevel::KEYSTORE),
- ])
-}
-
-/// One encoded list of key parameters.
-pub static KEY_PARAMETERS: &[u8] = &[
- 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x20,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x20,
- 0x04, 0x00, 0x00, 0x00, 0xf6, 0x01, 0x00, 0xa0, 0xf0, 0x7e, 0x7d, 0xb4, 0xc6, 0xd7, 0x25, 0x1d,
- 0x02, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00,
- 0x0a, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x2d, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
- 0xf8, 0x01, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0xbe, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
- 0xc1, 0x02, 0x00, 0x30, 0xb0, 0xad, 0x01, 0x00, 0xc2, 0x02, 0x00, 0x30, 0x75, 0x15, 0x03, 0x00,
- 0xcf, 0x02, 0x00, 0x30, 0xb9, 0x61, 0x34, 0x01, 0xce, 0x02, 0x00, 0x30, 0xb9, 0x61, 0x34, 0x01,
- 0x30, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
-];
-
-/// Real legacy blob.
pub static REAL_LEGACY_BLOB: &[u8] = &[
0x03, 0x04, 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,
@@ -165,7 +53,6 @@ pub static REAL_LEGACY_BLOB: &[u8] = &[
0xda, 0x40, 0x2b, 0x75, 0xd0, 0xd2, 0x81, 0x7f, 0xe2, 0x2b, 0xef, 0x64,
];
-/// Real legacy blob payload.
pub static REAL_LEGACY_BLOB_PAYLOAD: &[u8] = &[
0x6c, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x25, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x72, 0x00, 0x00,
0x00, 0x06, 0x00, 0x80, 0x00, 0x43, 0x00, 0x20, 0x85, 0x42, 0x9e, 0xe9, 0x34, 0x85, 0x2a, 0x00,
@@ -195,13 +82,11 @@ pub static REAL_LEGACY_BLOB_PAYLOAD: &[u8] = &[
0xe2, 0x2b, 0xef, 0x64,
];
-/// AES key blob.
pub static AES_KEY: &[u8] = &[
0x48, 0xe4, 0xb5, 0xff, 0xcd, 0x9c, 0x41, 0x1e, 0x20, 0x41, 0xf2, 0x65, 0xa0, 0x4f, 0xf6, 0x57,
0xc6, 0x58, 0xca, 0xbf, 0x28, 0xa3, 0x01, 0x98, 0x01, 0x76, 0x10, 0xc0, 0x30, 0x4e, 0x35, 0x6e,
];
-/// AES-GCM encrypted blob.
pub static AES_GCM_ENCRYPTED_BLOB: &[u8] = &[
0x03, 0x04, 0x04, 0x00, 0xbd, 0xdb, 0x8d, 0x69, 0x72, 0x56, 0xf0, 0xf5, 0xa4, 0x02, 0x88, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x50, 0xd9, 0x97, 0x95, 0x37, 0x6e, 0x28, 0x6a, 0x28, 0x9d, 0x51, 0xb9,
@@ -234,7 +119,6 @@ pub static AES_GCM_ENCRYPTED_BLOB: &[u8] = &[
0x2e, 0x0c, 0xc7, 0xbf, 0x29, 0x1e, 0x31, 0xdc, 0x0e, 0x85, 0x96, 0x7b,
];
-/// Decrypted payload.
pub static DECRYPTED_PAYLOAD: &[u8] = &[
0x7c, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x25, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x72, 0x00, 0x00,
0x00, 0x06, 0x00, 0x80, 0x00, 0x43, 0x00, 0x20, 0xa4, 0xee, 0xdc, 0x1f, 0x9e, 0xba, 0x42, 0xd6,
@@ -265,7 +149,6 @@ pub static DECRYPTED_PAYLOAD: &[u8] = &[
0xf6, 0x0b, 0x81, 0x07,
];
-/// Password blob.
pub static PASSWORD: &[u8] = &[
0x42, 0x39, 0x30, 0x37, 0x44, 0x37, 0x32, 0x37, 0x39, 0x39, 0x43, 0x42, 0x39, 0x41, 0x42, 0x30,
0x34, 0x31, 0x30, 0x38, 0x46, 0x44, 0x33, 0x45, 0x39, 0x42, 0x32, 0x38, 0x36, 0x35, 0x41, 0x36,
@@ -273,7 +156,6 @@ pub static PASSWORD: &[u8] = &[
0x32, 0x45, 0x31, 0x35, 0x43, 0x43, 0x46, 0x32, 0x39, 0x36, 0x33, 0x34, 0x31, 0x32, 0x41, 0x39,
];
-/// Super key blob.
pub static SUPERKEY: &[u8] = &[
0x03, 0x07, 0x01, 0x10, 0x9a, 0x81, 0x56, 0x7d, 0xf5, 0x86, 0x7c, 0x62, 0xd7, 0xf9, 0x26, 0x06,
0x00, 0x00, 0x00, 0x00, 0xde, 0x2a, 0xcb, 0xac, 0x98, 0x57, 0x2b, 0xe5, 0x57, 0x18, 0x78, 0x57,
@@ -282,29 +164,6 @@ pub static SUPERKEY: &[u8] = &[
0x76, 0x04, 0x2a, 0x48, 0xd1, 0xa7, 0x59, 0xd1, 0x04, 0x5b, 0xb4, 0x8a, 0x09, 0x22, 0x13, 0x0c,
0x94, 0xb6, 0x67, 0x7b, 0x39, 0x85, 0x28, 0x11,
];
-
-/// Super key IV.
-pub static SUPERKEY_IV: &[u8] = &[
- 0x9a, 0x81, 0x56, 0x7d, 0xf5, 0x86, 0x7c, 0x62, 0xd7, 0xf9, 0x26, 0x06, 0x00, 0x00, 0x00, 0x00,
-];
-
-/// Super key tag.
-pub static SUPERKEY_TAG: &[u8] = &[
- 0xde, 0x2a, 0xcb, 0xac, 0x98, 0x57, 0x2b, 0xe5, 0x57, 0x18, 0x78, 0x57, 0x6e, 0x10, 0x09, 0x84,
-];
-
-/// Super key salt.
-pub static SUPERKEY_SALT: &[u8] = &[
- 0x04, 0x5b, 0xb4, 0x8a, 0x09, 0x22, 0x13, 0x0c, 0x94, 0xb6, 0x67, 0x7b, 0x39, 0x85, 0x28, 0x11,
-];
-
-/// Super key payload.
-pub static SUPERKEY_PAYLOAD: &[u8] = &[
- 0xac, 0x6d, 0x13, 0xe6, 0xad, 0x2c, 0x89, 0x53, 0x1a, 0x99, 0xa5, 0x6c, 0x88, 0xe9, 0xeb, 0x5c,
- 0xef, 0x68, 0x5e, 0x5b, 0x53, 0xa8, 0xe7, 0xa2, 0x76, 0x04, 0x2a, 0x48, 0xd1, 0xa7, 0x59, 0xd1,
-];
-
-/// user key blob.
pub static USRPKEY_AUTHBOUND: &[u8] = &[
0x03, 0x04, 0x04, 0x00, 0x1c, 0x34, 0x87, 0x6f, 0xc8, 0x35, 0x0d, 0x34, 0x88, 0x59, 0xbc, 0xf5,
0x00, 0x00, 0x00, 0x00, 0x62, 0xe3, 0x38, 0x2d, 0xd0, 0x58, 0x40, 0xc1, 0xb0, 0xf2, 0x4a, 0xdd,
@@ -344,56 +203,6 @@ pub static USRPKEY_AUTHBOUND: &[u8] = &[
0xaf, 0x17, 0x2f, 0x21, 0x07, 0xea, 0x61, 0xff, 0x73, 0x08, 0x50, 0xb2, 0x19, 0xe8, 0x23, 0x1b,
0x83, 0x42, 0xdd, 0x4e, 0x6d,
];
-
-/// Authbound IV.
-pub static USRPKEY_AUTHBOUND_IV: &[u8] = &[
- 0x1c, 0x34, 0x87, 0x6f, 0xc8, 0x35, 0x0d, 0x34, 0x88, 0x59, 0xbc, 0xf5, 0x00, 0x00, 0x00, 0x00,
-];
-
-/// Authbond IV Tag.
-pub static USRPKEY_AUTHBOUND_TAG: &[u8] = &[
- 0x62, 0xe3, 0x38, 0x2d, 0xd0, 0x58, 0x40, 0xc1, 0xb0, 0xf2, 0x4a, 0xdd, 0xf7, 0x81, 0x67, 0x0b,
-];
-
-/// Encrypted use key payload.
-pub static USRPKEY_AUTHBOUND_ENC_PAYLOAD: &[u8] = &[
- 0x05, 0xb2, 0x5a, 0x1d, 0x1b, 0x25, 0x19, 0x48, 0xbf, 0x76, 0x0b, 0x37, 0x8c, 0x60, 0x52, 0xea,
- 0x30, 0x2a, 0x2c, 0x89, 0x99, 0x95, 0x57, 0x5c, 0xec, 0x62, 0x3c, 0x08, 0x1a, 0xc6, 0x65, 0xf9,
- 0xad, 0x24, 0x99, 0xf0, 0x5c, 0x44, 0xa0, 0xea, 0x9a, 0x60, 0xa2, 0xef, 0xf5, 0x27, 0x50, 0xba,
- 0x9c, 0xef, 0xa6, 0x08, 0x88, 0x4b, 0x0f, 0xfe, 0x5d, 0x41, 0xac, 0xba, 0xef, 0x9d, 0xa4, 0xb7,
- 0x72, 0xd3, 0xc8, 0x11, 0x92, 0x06, 0xf6, 0x26, 0xdf, 0x90, 0xe2, 0x66, 0x89, 0xf3, 0x85, 0x16,
- 0x4a, 0xdf, 0x7f, 0xac, 0x94, 0x4a, 0x1c, 0xce, 0x18, 0xee, 0xf4, 0x1f, 0x8e, 0xd6, 0xaf, 0xfd,
- 0x1d, 0xe5, 0x80, 0x4a, 0x6b, 0xbf, 0x91, 0xe2, 0x36, 0x1d, 0xb3, 0x53, 0x12, 0xfd, 0xc9, 0x0b,
- 0xa6, 0x69, 0x00, 0x45, 0xcb, 0x4c, 0x40, 0x6b, 0x70, 0xcb, 0xd2, 0xa0, 0x44, 0x0b, 0x4b, 0xec,
- 0xd6, 0x4f, 0x6f, 0x64, 0x37, 0xa7, 0xc7, 0x25, 0x54, 0xf4, 0xac, 0x6b, 0x34, 0x53, 0xea, 0x4e,
- 0x56, 0x49, 0xba, 0xf4, 0x1e, 0xc6, 0x52, 0x8f, 0xf4, 0x85, 0xe7, 0xb5, 0xaf, 0x49, 0x68, 0xb3,
- 0xb8, 0x7d, 0x63, 0xfc, 0x6e, 0x83, 0xa0, 0xf3, 0x91, 0x04, 0x80, 0xfd, 0xc5, 0x54, 0x7e, 0x92,
- 0x1a, 0x87, 0x2c, 0x6e, 0xa6, 0x29, 0xb9, 0x1e, 0x3f, 0xef, 0x30, 0x12, 0x7b, 0x2f, 0xa2, 0x16,
- 0x61, 0x8a, 0xcf, 0x14, 0x2d, 0x62, 0x98, 0x15, 0xae, 0x3b, 0xe6, 0x08, 0x1e, 0xb1, 0xf1, 0x21,
- 0xb0, 0x50, 0xc0, 0x4b, 0x81, 0x71, 0x29, 0xe7, 0x86, 0xbf, 0x29, 0xe1, 0xeb, 0xfe, 0xbc, 0x11,
- 0x3c, 0xc6, 0x15, 0x47, 0x9b, 0x41, 0x84, 0x61, 0x33, 0xbf, 0xca, 0xfe, 0x24, 0x92, 0x9e, 0x70,
- 0x26, 0x36, 0x46, 0xca, 0xfe, 0xd3, 0x5a, 0x1d, 0x9e, 0x30, 0x19, 0xbd, 0x26, 0x49, 0xb4, 0x90,
- 0x0c, 0x8d, 0xa2, 0x28, 0xa6, 0x24, 0x62, 0x6b, 0xe2, 0xfa, 0xe0, 0x53, 0xaa, 0x01, 0xeb, 0xaa,
- 0x41, 0x2b, 0xcb, 0xb1, 0x08, 0x66, 0x9d, 0x21, 0x2d, 0x2a, 0x47, 0x44, 0xee, 0xd5, 0x06, 0xe3,
- 0x4a, 0xb9, 0x3f, 0xcd, 0x78, 0x67, 0x89, 0x5b, 0xf7, 0x51, 0xc0, 0xc4, 0xa9, 0x68, 0xee, 0x44,
- 0x9c, 0x47, 0xa4, 0xbd, 0x6f, 0x7b, 0xdd, 0x64, 0xa8, 0xc7, 0x1e, 0x77, 0x1d, 0x68, 0x87, 0xaa,
- 0xae, 0x3c, 0xfc, 0x58, 0xb6, 0x3c, 0xcf, 0x58, 0xd0, 0x10, 0xaa, 0xef, 0xf0, 0x98, 0x67, 0x14,
- 0x29, 0x4d, 0x40, 0x8b, 0xe5, 0xb1, 0xdf, 0x7f, 0x40, 0xb1, 0xd8, 0xea, 0x6c, 0xa8, 0xf7, 0x64,
- 0xed, 0x02, 0x8d, 0xe7, 0x93, 0xfe, 0x79, 0x9a, 0x88, 0x62, 0x4f, 0xd0, 0x8a, 0x80, 0x36, 0x42,
- 0x0a, 0xf1, 0xa2, 0x0e, 0x30, 0x39, 0xbd, 0x26, 0x1d, 0xd4, 0xf1, 0xc8, 0x6e, 0xdd, 0xc5, 0x41,
- 0x29, 0xd8, 0xc1, 0x9e, 0x24, 0xf0, 0x25, 0x07, 0x05, 0x06, 0xc5, 0x08, 0xe3, 0x02, 0x2b, 0xe1,
- 0x40, 0xc5, 0x67, 0xd2, 0x82, 0x96, 0x20, 0x80, 0xcf, 0x87, 0x3a, 0xc6, 0xb0, 0xbe, 0xcc, 0xbb,
- 0x5a, 0x01, 0xab, 0xdd, 0x00, 0xc7, 0x0e, 0x7b, 0x02, 0x35, 0x27, 0xf4, 0x70, 0xfe, 0xd1, 0x19,
- 0x6a, 0x64, 0x23, 0x9d, 0xba, 0xe9, 0x1d, 0x76, 0x90, 0xfe, 0x7f, 0xd6, 0xb5, 0xa0, 0xe7, 0xb9,
- 0xf3, 0x56, 0x82, 0x8e, 0x57, 0x35, 0xf2, 0x69, 0xce, 0x52, 0xac, 0xc2, 0xf6, 0x5e, 0xb6, 0x54,
- 0x95, 0x83, 0x3b, 0x9f, 0x48, 0xbb, 0x04, 0x06, 0xac, 0x55, 0xa9, 0xb9, 0xa3, 0xe7, 0x89, 0x6e,
- 0x5c, 0x3a, 0x08, 0x67, 0x00, 0x8f, 0x1e, 0x26, 0x1b, 0x4d, 0x8a, 0xa6, 0x17, 0xa0, 0xa6, 0x18,
- 0xe6, 0x31, 0x43, 0x15, 0xb8, 0x7f, 0x9e, 0xf5, 0x78, 0x58, 0x98, 0xb1, 0x8c, 0xf5, 0x22, 0x42,
- 0x33, 0xc0, 0x42, 0x72, 0x4f, 0xce, 0x9f, 0x31, 0xaf, 0x17, 0x2f, 0x21, 0x07, 0xea, 0x61, 0xff,
- 0x73, 0x08, 0x50, 0xb2, 0x19, 0xe8, 0x23, 0x1b, 0x83, 0x42, 0xdd, 0x4e, 0x6d,
-];
-
-/// User key characterstics blob.
pub static USRPKEY_AUTHBOUND_CHR: &[u8] = &[
0x03, 0x06, 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,
@@ -409,8 +218,6 @@ pub static USRPKEY_AUTHBOUND_CHR: &[u8] = &[
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xbd, 0x02, 0x00, 0x60,
0x10, 0x9d, 0x8b, 0x31, 0x76, 0x01, 0x00, 0x00, 0xf5, 0x01, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00,
];
-
-/// User certificate blob.
pub static USRCERT_AUTHBOUND: &[u8] = &[
0x03, 0x01, 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,
@@ -457,8 +264,6 @@ pub static USRCERT_AUTHBOUND: &[u8] = &[
0x39, 0x58, 0xe9, 0x89, 0x1a, 0x14, 0x41, 0x8d, 0xe0, 0xdc, 0x3d, 0x88, 0xf4, 0x2c, 0x7c, 0xda,
0xa1, 0x84, 0xfa, 0x7f, 0xf9, 0x07, 0x97, 0xfb, 0xb5, 0xb7, 0x28, 0x28, 0x00, 0x7c, 0xa7,
];
-
-/// CA certificate blob.
pub static CACERT_AUTHBOUND: &[u8] = &[
0x03, 0x01, 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,
@@ -647,7 +452,6 @@ pub static CACERT_AUTHBOUND: &[u8] = &[
0xab, 0xae, 0x24, 0xe2, 0x44, 0x35, 0x16, 0x8d, 0x55, 0x3c, 0xe4,
];
-/// User non-authbond-key blob.
pub static USRPKEY_NON_AUTHBOUND: &[u8] = &[
0x03, 0x04, 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,
@@ -687,8 +491,6 @@ pub static USRPKEY_NON_AUTHBOUND: &[u8] = &[
0x46, 0xf0, 0xee, 0x50, 0x73, 0x6a, 0x7b, 0xa3, 0xe9, 0xb1, 0x08, 0x81, 0x00, 0xdf, 0x0e, 0xc9,
0xc3, 0x2c, 0x13, 0x64, 0xa1,
];
-
-/// User non-authbond-key characteristics blob.
pub static USRPKEY_NON_AUTHBOUND_CHR: &[u8] = &[
0x03, 0x06, 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,
@@ -704,7 +506,6 @@ pub static USRPKEY_NON_AUTHBOUND_CHR: &[u8] = &[
0x60, 0x60, 0x60, 0x8c, 0x31, 0x76, 0x01, 0x00, 0x00, 0xf5, 0x01, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00,
];
-/// User non-authbond-key certificate blob.
pub static USRCERT_NON_AUTHBOUND: &[u8] = &[
0x03, 0x01, 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,
@@ -752,7 +553,6 @@ pub static USRCERT_NON_AUTHBOUND: &[u8] = &[
0xd8, 0xd5, 0xd1, 0x64, 0x4c, 0x05, 0xdd, 0x13, 0x0e, 0xa4, 0xf3, 0x38, 0xbf, 0x18, 0xd5,
];
-/// User non-authbond-key ca-certs blob.
pub static CACERT_NON_AUTHBOUND: &[u8] = &[
0x03, 0x01, 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,
@@ -941,7 +741,6 @@ pub static CACERT_NON_AUTHBOUND: &[u8] = &[
0xab, 0xae, 0x24, 0xe2, 0x44, 0x35, 0x16, 0x8d, 0x55, 0x3c, 0xe4,
];
-/// User decrypted authbond-key blob.
pub static _DECRYPTED_USRPKEY_AUTHBOUND: &[u8] = &[
0x44, 0x4b, 0x4d, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0xc6, 0x15, 0x3a, 0x08, 0x1e, 0x43, 0xba, 0x7a, 0x0f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
@@ -979,7 +778,6 @@ pub static _DECRYPTED_USRPKEY_AUTHBOUND: &[u8] = &[
0x60, 0x5e, 0xcd, 0xce, 0x3a, 0xd8, 0x09, 0xeb, 0x9d, 0x40, 0xdb, 0x58, 0x53,
];
-/// User loaded authbond certs blob.
pub static LOADED_CERT_AUTHBOUND: &[u8] = &[
0x30, 0x82, 0x02, 0x93, 0x30, 0x82, 0x02, 0x3A, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01,
0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x30, 0x29, 0x31, 0x19,
@@ -1024,8 +822,6 @@ pub static LOADED_CERT_AUTHBOUND: &[u8] = &[
0xE0, 0xDC, 0x3D, 0x88, 0xF4, 0x2C, 0x7C, 0xDA, 0xA1, 0x84, 0xFA, 0x7F, 0xF9, 0x07, 0x97, 0xFB,
0xB5, 0xB7, 0x28, 0x28, 0x00, 0x7C, 0xA7,
];
-
-/// User loaded authbond ca-certs blob.
pub static LOADED_CACERT_AUTHBOUND: &[u8] = &[
0x30, 0x82, 0x02, 0x26, 0x30, 0x82, 0x01, 0xAB, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0A, 0x05,
0x84, 0x20, 0x26, 0x90, 0x76, 0x23, 0x58, 0x71, 0x77, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48,
@@ -1212,7 +1008,6 @@ pub static LOADED_CACERT_AUTHBOUND: &[u8] = &[
0x55, 0x3C, 0xE4,
];
-/// User loaded non-authbond user key blob.
pub static LOADED_USRPKEY_NON_AUTHBOUND: &[u8] = &[
0x44, 0x4b, 0x4d, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x8a, 0xc1, 0x08, 0x13, 0x7c, 0x47, 0xba, 0x09, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
@@ -1250,7 +1045,6 @@ pub static LOADED_USRPKEY_NON_AUTHBOUND: &[u8] = &[
0xe9, 0xb1, 0x08, 0x81, 0x00, 0xdf, 0x0e, 0xc9, 0xc3, 0x2c, 0x13, 0x64, 0xa1,
];
-/// User loaded non-authbond certificate blob.
pub static LOADED_CERT_NON_AUTHBOUND: &[u8] = &[
0x30, 0x82, 0x02, 0x93, 0x30, 0x82, 0x02, 0x39, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01,
0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x29, 0x31, 0x19,
@@ -1296,7 +1090,6 @@ pub static LOADED_CERT_NON_AUTHBOUND: &[u8] = &[
0x0e, 0xa4, 0xf3, 0x38, 0xbf, 0x18, 0xd5,
];
-/// User loaded non-authbond ca-certificates blob.
pub static LOADED_CACERT_NON_AUTHBOUND: &[u8] = &[
0x30, 0x82, 0x02, 0x26, 0x30, 0x82, 0x01, 0xab, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0a, 0x05,
0x84, 0x20, 0x26, 0x90, 0x76, 0x23, 0x58, 0x71, 0x77, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48,
diff --git a/keystore2/src/legacy_importer.rs b/keystore2/src/legacy_migrator.rs
index 93e17358..f92fd459 100644
--- a/keystore2/src/legacy_importer.rs
+++ b/keystore2/src/legacy_migrator.rs
@@ -14,19 +14,18 @@
//! This module acts as a bridge between the legacy key database and the keystore2 database.
-use crate::database::{
- BlobInfo, BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData,
- KeyMetaEntry, KeyType, KeystoreDB, Uuid, KEYSTORE_UUID,
-};
-use crate::error::{map_km_error, Error};
-use crate::key_parameter::{KeyParameter, KeyParameterValue};
-use crate::legacy_blob::{self, Blob, BlobValue, LegacyKeyCharacteristics};
-use crate::super_key::USER_SUPER_KEY;
-use crate::utils::{
- key_characteristics_to_internal, uid_to_android_user, upgrade_keyblob_if_required_with,
- watchdog as wd, AesGcm,
-};
+use crate::key_parameter::KeyParameterValue;
+use crate::legacy_blob::BlobValue;
+use crate::utils::{uid_to_android_user, watchdog as wd};
use crate::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
+use crate::{database::KeyType, error::Error};
+use crate::{
+ database::{
+ BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData,
+ KeyMetaEntry, KeystoreDB, Uuid, KEYSTORE_UUID,
+ },
+ super_key::USER_SUPER_KEY,
+};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
@@ -39,8 +38,8 @@ use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
-/// Represents LegacyImporter.
-pub struct LegacyImporter {
+/// Represents LegacyMigrator.
+pub struct LegacyMigrator {
async_task: Arc<AsyncTask>,
initializer: Mutex<
Option<
@@ -52,19 +51,19 @@ pub struct LegacyImporter {
>,
>,
/// This atomic is used for cheap interior mutability. It is intended to prevent
- /// expensive calls into the legacy importer when the legacy database is empty.
+ /// expensive calls into the legacy migrator when the legacy database is empty.
/// When transitioning from READY to EMPTY, spurious calls may occur for a brief period
/// of time. This is tolerable in favor of the common case.
state: AtomicU8,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-struct RecentImport {
+struct RecentMigration {
uid: u32,
alias: String,
}
-impl RecentImport {
+impl RecentMigration {
fn new(uid: u32, alias: String) -> Self {
Self { uid, alias }
}
@@ -75,15 +74,15 @@ enum BulkDeleteRequest {
User(u32),
}
-struct LegacyImporterState {
- recently_imported: HashSet<RecentImport>,
- recently_imported_super_key: HashSet<u32>,
+struct LegacyMigratorState {
+ recently_migrated: HashSet<RecentMigration>,
+ recently_migrated_super_key: HashSet<u32>,
legacy_loader: Arc<LegacyBlobLoader>,
sec_level_to_km_uuid: HashMap<SecurityLevel, Uuid>,
db: KeystoreDB,
}
-impl LegacyImporter {
+impl LegacyMigrator {
const WIFI_NAMESPACE: i64 = 102;
const AID_WIFI: u32 = 1010;
@@ -91,7 +90,7 @@ impl LegacyImporter {
const STATE_READY: u8 = 1;
const STATE_EMPTY: u8 = 2;
- /// Constructs a new LegacyImporter using the given AsyncTask object as import
+ /// Constructs a new LegacyMigrator using the given AsyncTask object as migration
/// worker.
pub fn new(async_task: Arc<AsyncTask>) -> Self {
Self {
@@ -101,7 +100,7 @@ impl LegacyImporter {
}
}
- /// The legacy importer must be initialized deferred, because keystore starts very early.
+ /// The legacy migrator must be initialized deferred, because keystore starts very early.
/// At this time the data partition may not be mounted. So we cannot open database connections
/// until we get actual key load requests. This sets the function that the legacy loader
/// uses to connect to the database.
@@ -126,11 +125,11 @@ impl LegacyImporter {
Ok(())
}
- /// This function is called by the import requestor to check if it is worth
- /// making an import request. It also transitions the state from UNINITIALIZED
+ /// This function is called by the migration requestor to check if it is worth
+ /// making a migration request. It also transitions the state from UNINITIALIZED
/// to READY or EMPTY on first use. The deferred initialization is necessary, because
/// Keystore 2.0 runs early during boot, where data may not yet be mounted.
- /// Returns Ok(STATE_READY) if an import request is worth undertaking and
+ /// Returns Ok(STATE_READY) if a migration request is worth undertaking and
/// Ok(STATE_EMPTY) if the database is empty. An error is returned if the loader
/// was not initialized and cannot be initialized.
fn check_state(&self) -> Result<u8> {
@@ -158,9 +157,9 @@ impl LegacyImporter {
}
self.async_task.queue_hi(move |shelf| {
- shelf.get_or_put_with(|| LegacyImporterState {
- recently_imported: Default::default(),
- recently_imported_super_key: Default::default(),
+ shelf.get_or_put_with(|| LegacyMigratorState {
+ recently_migrated: Default::default(),
+ recently_migrated_super_key: Default::default(),
legacy_loader,
sec_level_to_km_uuid,
db,
@@ -190,14 +189,14 @@ impl LegacyImporter {
);
}
(Self::STATE_READY, _) => return Ok(Self::STATE_READY),
- (s, _) => panic!("Unknown legacy importer state. {} ", s),
+ (s, _) => panic!("Unknown legacy migrator state. {} ", s),
}
}
}
/// List all aliases for uid in the legacy database.
pub fn list_uid(&self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
- let _wp = wd::watch_millis("LegacyImporter::list_uid", 500);
+ let _wp = wd::watch_millis("LegacyMigrator::list_uid", 500);
let uid = match (domain, namespace) {
(Domain::APP, namespace) => namespace as u32,
@@ -218,44 +217,44 @@ impl LegacyImporter {
)
}
- /// Sends the given closure to the importer thread for execution after calling check_state.
+ /// Sends the given closure to the migrator thread for execution after calling check_state.
/// Returns None if the database was empty and the request was not executed.
- /// Otherwise returns Some with the result produced by the import request.
+ /// Otherwise returns Some with the result produced by the migration request.
/// The loader state may transition to STATE_EMPTY during the execution of this function.
fn do_serialized<F, T: Send + 'static>(&self, f: F) -> Option<Result<T>>
where
- F: FnOnce(&mut LegacyImporterState) -> Result<T> + Send + 'static,
+ F: FnOnce(&mut LegacyMigratorState) -> Result<T> + Send + 'static,
{
// Short circuit if the database is empty or not initialized (error case).
match self.check_state().context("In do_serialized: Checking state.") {
- Ok(LegacyImporter::STATE_EMPTY) => return None,
- Ok(LegacyImporter::STATE_READY) => {}
+ Ok(LegacyMigrator::STATE_EMPTY) => return None,
+ Ok(LegacyMigrator::STATE_READY) => {}
Err(e) => return Some(Err(e)),
- Ok(s) => panic!("Unknown legacy importer state. {} ", s),
+ Ok(s) => panic!("Unknown legacy migrator state. {} ", s),
}
// We have established that there may be a key in the legacy database.
- // Now we schedule an import request.
+ // Now we schedule a migration request.
let (sender, receiver) = channel();
self.async_task.queue_hi(move |shelf| {
- // Get the importer state from the shelf.
- // There may not be a state. This can happen if this import request was scheduled
+ // Get the migrator state from the shelf.
+ // There may not be a state. This can happen if this migration request was scheduled
// before a previous request established that the legacy database was empty
// and removed the state from the shelf. Since we know now that the database
// is empty, we can return None here.
- let (new_state, result) = if let Some(legacy_importer_state) =
- shelf.get_downcast_mut::<LegacyImporterState>()
+ let (new_state, result) = if let Some(legacy_migrator_state) =
+ shelf.get_downcast_mut::<LegacyMigratorState>()
{
- let result = f(legacy_importer_state);
- (legacy_importer_state.check_empty(), Some(result))
+ let result = f(legacy_migrator_state);
+ (legacy_migrator_state.check_empty(), Some(result))
} else {
(Self::STATE_EMPTY, None)
};
- // If the import request determined that the database is now empty, we discard
+ // If the migration request determined that the database is now empty, we discard
// the state from the shelf to free up the resources we won't need any longer.
if result.is_some() && new_state == Self::STATE_EMPTY {
- shelf.remove_downcast_ref::<LegacyImporterState>();
+ shelf.remove_downcast_ref::<LegacyMigratorState>();
}
// Send the result to the requester.
@@ -272,7 +271,7 @@ impl LegacyImporter {
};
// We can only transition to EMPTY but never back.
- // The importer never creates any legacy blobs.
+ // The migrator never creates any legacy blobs.
if new_state == Self::STATE_EMPTY {
self.state.store(Self::STATE_EMPTY, Ordering::Relaxed)
}
@@ -281,20 +280,19 @@ impl LegacyImporter {
}
/// Runs the key_accessor function and returns its result. If it returns an error and the
- /// root cause was KEY_NOT_FOUND, tries to import a key with the given parameters from
+ /// root cause was KEY_NOT_FOUND, tries to migrate a key with the given parameters from
/// the legacy database to the new database and runs the key_accessor function again if
- /// the import request was successful.
- pub fn with_try_import<F, T>(
+ /// the migration request was successful.
+ pub fn with_try_migrate<F, T>(
&self,
key: &KeyDescriptor,
caller_uid: u32,
- super_key: Option<Arc<dyn AesGcm + Send + Sync>>,
key_accessor: F,
) -> Result<T>
where
F: Fn() -> Result<T>,
{
- let _wp = wd::watch_millis("LegacyImporter::with_try_import", 500);
+ let _wp = wd::watch_millis("LegacyMigrator::with_try_migrate", 500);
// Access the key and return on success.
match key_accessor() {
@@ -306,7 +304,7 @@ impl LegacyImporter {
}
// Filter inputs. We can only load legacy app domain keys and some special rules due
- // to which we import keys transparently to an SELINUX domain.
+ // to which we migrate keys transparently to an SELINUX domain.
let uid = match key {
KeyDescriptor { domain: Domain::APP, alias: Some(_), .. } => caller_uid,
KeyDescriptor { domain: Domain::SELINUX, nspace, alias: Some(_), .. } => {
@@ -325,14 +323,12 @@ impl LegacyImporter {
};
let key_clone = key.clone();
- let result = self.do_serialized(move |importer_state| {
- let super_key = super_key.map(|sk| -> Arc<dyn AesGcm> { sk });
- importer_state.check_and_import(uid, key_clone, super_key)
- });
+ let result = self
+ .do_serialized(move |migrator_state| migrator_state.check_and_migrate(uid, key_clone));
if let Some(result) = result {
result?;
- // After successful import try again.
+ // After successful migration try again.
key_accessor()
} else {
Err(Error::Rc(ResponseCode::KEY_NOT_FOUND)).context("Legacy database is empty.")
@@ -340,8 +336,8 @@ impl LegacyImporter {
}
/// Calls key_accessor and returns the result on success. In the case of a KEY_NOT_FOUND error
- /// this function makes an import request and on success retries the key_accessor.
- pub fn with_try_import_super_key<F, T>(
+ /// this function makes a migration request and on success retries the key_accessor.
+ pub fn with_try_migrate_super_key<F, T>(
&self,
user_id: u32,
pw: &Password,
@@ -350,31 +346,31 @@ impl LegacyImporter {
where
F: FnMut() -> Result<Option<T>>,
{
- let _wp = wd::watch_millis("LegacyImporter::with_try_import_super_key", 500);
+ let _wp = wd::watch_millis("LegacyMigrator::with_try_migrate_super_key", 500);
match key_accessor() {
Ok(Some(result)) => return Ok(Some(result)),
Ok(None) => {}
Err(e) => return Err(e),
}
- let pw = pw.try_clone().context("In with_try_import_super_key: Cloning password.")?;
- let result = self.do_serialized(move |importer_state| {
- importer_state.check_and_import_super_key(user_id, &pw)
+ let pw = pw.try_clone().context("In with_try_migrate_super_key: Cloning password.")?;
+ let result = self.do_serialized(move |migrator_state| {
+ migrator_state.check_and_migrate_super_key(user_id, &pw)
});
if let Some(result) = result {
result?;
- // After successful import try again.
+ // After successful migration try again.
key_accessor()
} else {
Ok(None)
}
}
- /// Deletes all keys belonging to the given namespace, importing them into the database
+ /// Deletes all keys belonging to the given namespace, migrating them into the database
/// for subsequent garbage collection if necessary.
pub fn bulk_delete_uid(&self, domain: Domain, nspace: i64) -> Result<()> {
- let _wp = wd::watch_millis("LegacyImporter::bulk_delete_uid", 500);
+ let _wp = wd::watch_millis("LegacyMigrator::bulk_delete_uid", 500);
let uid = match (domain, nspace) {
(Domain::APP, nspace) => nspace as u32,
@@ -383,24 +379,24 @@ impl LegacyImporter {
_ => return Ok(()),
};
- let result = self.do_serialized(move |importer_state| {
- importer_state.bulk_delete(BulkDeleteRequest::Uid(uid), false)
+ let result = self.do_serialized(move |migrator_state| {
+ migrator_state.bulk_delete(BulkDeleteRequest::Uid(uid), false)
});
result.unwrap_or(Ok(()))
}
- /// Deletes all keys belonging to the given android user, importing them into the database
+ /// Deletes all keys belonging to the given android user, migrating them into the database
/// for subsequent garbage collection if necessary.
pub fn bulk_delete_user(
&self,
user_id: u32,
keep_non_super_encrypted_keys: bool,
) -> Result<()> {
- let _wp = wd::watch_millis("LegacyImporter::bulk_delete_user", 500);
+ let _wp = wd::watch_millis("LegacyMigrator::bulk_delete_user", 500);
- let result = self.do_serialized(move |importer_state| {
- importer_state
+ let result = self.do_serialized(move |migrator_state| {
+ migrator_state
.bulk_delete(BulkDeleteRequest::User(user_id), keep_non_super_encrypted_keys)
});
@@ -410,12 +406,12 @@ impl LegacyImporter {
/// Queries the legacy database for the presence of a super key for the given user.
pub fn has_super_key(&self, user_id: u32) -> Result<bool> {
let result =
- self.do_serialized(move |importer_state| importer_state.has_super_key(user_id));
+ self.do_serialized(move |migrator_state| migrator_state.has_super_key(user_id));
result.unwrap_or(Ok(false))
}
}
-impl LegacyImporterState {
+impl LegacyMigratorState {
fn get_km_uuid(&self, is_strongbox: bool) -> Result<Uuid> {
let sec_level = if is_strongbox {
SecurityLevel::STRONGBOX
@@ -434,174 +430,17 @@ impl LegacyImporterState {
.context("In list_uid: Trying to list legacy entries.")
}
- /// Checks if the key can potentially be unlocked. And deletes the key entry otherwise.
- /// If the super_key has already been imported, the super key database id is returned.
- fn get_super_key_id_check_unlockable_or_delete(
- &mut self,
- uid: u32,
- alias: &str,
- ) -> Result<i64> {
- let user_id = uid_to_android_user(uid);
-
- match self
- .db
- .load_super_key(&USER_SUPER_KEY, user_id)
- .context("In get_super_key_id_check_unlockable_or_delete: Failed to load super key")?
- {
- Some((_, entry)) => Ok(entry.id()),
- None => {
- // This might be the first time we access the super key,
- // and it may not have been imported. We cannot import
- // the legacy super_key key now, because we need to reencrypt
- // it which we cannot do if we are not unlocked, which we are
- // not because otherwise the key would have been imported.
- // We can check though if the key exists. If it does,
- // we can return Locked. Otherwise, we can delete the
- // key and return NotFound, because the key will never
- // be unlocked again.
- if self.legacy_loader.has_super_key(user_id) {
- Err(Error::Rc(ResponseCode::LOCKED)).context(
- "In get_super_key_id_check_unlockable_or_delete: \
- Cannot import super key of this key while user is locked.",
- )
- } else {
- self.legacy_loader.remove_keystore_entry(uid, alias).context(
- "In get_super_key_id_check_unlockable_or_delete: \
- Trying to remove obsolete key.",
- )?;
- Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
- .context("In get_super_key_id_check_unlockable_or_delete: Obsolete key.")
- }
- }
- }
- }
-
- fn characteristics_file_to_cache(
- &mut self,
- km_blob_params: Option<(Blob, LegacyKeyCharacteristics)>,
- super_key: &Option<Arc<dyn AesGcm>>,
- uid: u32,
- alias: &str,
- ) -> Result<(Option<(Blob, Vec<KeyParameter>)>, Option<(LegacyBlob<'static>, BlobMetaData)>)>
- {
- let (km_blob, params) = match km_blob_params {
- Some((km_blob, LegacyKeyCharacteristics::File(params))) => (km_blob, params),
- Some((km_blob, LegacyKeyCharacteristics::Cache(params))) => {
- return Ok((Some((km_blob, params)), None))
- }
- None => return Ok((None, None)),
- };
-
- let km_uuid = self
- .get_km_uuid(km_blob.is_strongbox())
- .context("In characteristics_file_to_cache: Trying to get KM UUID")?;
-
- let blob = match (&km_blob.value(), super_key.as_ref()) {
- (BlobValue::Encrypted { iv, tag, data }, Some(super_key)) => {
- let blob = super_key
- .decrypt(data, iv, tag)
- .context("In characteristics_file_to_cache: Decryption failed.")?;
- LegacyBlob::ZVec(blob)
- }
- (BlobValue::Encrypted { .. }, None) => {
- return Err(Error::Rc(ResponseCode::LOCKED)).context(
- "In characteristics_file_to_cache: Oh uh, so close. \
- This ancient key cannot be imported unless the user is unlocked.",
- );
- }
- (BlobValue::Decrypted(data), _) => LegacyBlob::Ref(data),
- _ => {
- return Err(Error::sys())
- .context("In characteristics_file_to_cache: Unexpected blob type.")
- }
- };
-
- let (km_params, upgraded_blob) = get_key_characteristics_without_app_data(&km_uuid, &*blob)
- .context(
- "In characteristics_file_to_cache: Failed to get key characteristics from device.",
- )?;
-
- let flags = km_blob.get_flags();
-
- let (current_blob, superseded_blob) = if let Some(upgraded_blob) = upgraded_blob {
- match (km_blob.take_value(), super_key.as_ref()) {
- (BlobValue::Encrypted { iv, tag, data }, Some(super_key)) => {
- let super_key_id =
- self.get_super_key_id_check_unlockable_or_delete(uid, alias).context(
- "In characteristics_file_to_cache: \
- How is there a super key but no super key id?",
- )?;
-
- let mut superseded_metadata = BlobMetaData::new();
- superseded_metadata.add(BlobMetaEntry::Iv(iv.to_vec()));
- superseded_metadata.add(BlobMetaEntry::AeadTag(tag.to_vec()));
- superseded_metadata
- .add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key_id)));
- superseded_metadata.add(BlobMetaEntry::KmUuid(km_uuid));
- let superseded_blob = (LegacyBlob::Vec(data), superseded_metadata);
-
- let (data, iv, tag) = super_key.encrypt(&upgraded_blob).context(
- "In characteristics_file_to_cache: \
- Failed to encrypt upgraded key blob.",
- )?;
- (
- Blob::new(flags, BlobValue::Encrypted { data, iv, tag }),
- Some(superseded_blob),
- )
- }
- (BlobValue::Encrypted { .. }, None) => {
- return Err(Error::sys()).context(
- "In characteristics_file_to_cache: This should not be reachable. \
- The blob could not have been decrypted above.",
- );
- }
- (BlobValue::Decrypted(data), _) => {
- let mut superseded_metadata = BlobMetaData::new();
- superseded_metadata.add(BlobMetaEntry::KmUuid(km_uuid));
- let superseded_blob = (LegacyBlob::ZVec(data), superseded_metadata);
- (
- Blob::new(
- flags,
- BlobValue::Decrypted(upgraded_blob.try_into().context(
- "In characteristics_file_to_cache: \
- Failed to convert upgraded blob to ZVec.",
- )?),
- ),
- Some(superseded_blob),
- )
- }
- _ => {
- return Err(Error::sys()).context(
- "In characteristics_file_to_cache: This should not be reachable. \
- Any other variant should have resulted in a different error.",
- )
- }
- }
- } else {
- (km_blob, None)
- };
-
- let params =
- augment_legacy_characteristics_file_with_key_characteristics(km_params, params);
- Ok((Some((current_blob, params)), superseded_blob))
- }
-
- /// This is a key import request that must run in the importer thread. This must
+ /// This is a key migration request that must run in the migrator thread. This must
/// be passed to do_serialized.
- fn check_and_import(
- &mut self,
- uid: u32,
- mut key: KeyDescriptor,
- super_key: Option<Arc<dyn AesGcm>>,
- ) -> Result<()> {
+ fn check_and_migrate(&mut self, uid: u32, mut key: KeyDescriptor) -> Result<()> {
let alias = key.alias.clone().ok_or_else(|| {
- anyhow::anyhow!(Error::sys()).context(
- "In check_and_import: Must be Some because \
- our caller must not have called us otherwise.",
- )
+ anyhow::anyhow!(Error::sys()).context(concat!(
+ "In check_and_migrate: Must be Some because ",
+ "our caller must not have called us otherwise."
+ ))
})?;
- if self.recently_imported.contains(&RecentImport::new(uid, alias.clone())) {
+ if self.recently_migrated.contains(&RecentMigration::new(uid, alias.clone())) {
return Ok(());
}
@@ -612,42 +451,49 @@ impl LegacyImporterState {
// If the key is not found in the cache, try to load from the legacy database.
let (km_blob_params, user_cert, ca_cert) = self
.legacy_loader
- .load_by_uid_alias(uid, &alias, &super_key)
- .map_err(|e| {
- if e.root_cause().downcast_ref::<legacy_blob::Error>()
- == Some(&legacy_blob::Error::LockedComponent)
- {
- // There is no chance to succeed at this point. We just check if there is
- // a super key so that this entry might be unlockable in the future.
- // If not the entry will be deleted and KEY_NOT_FOUND is returned.
- // If a super key id was returned we still have to return LOCKED but the key
- // may be imported when the user unlocks the device.
- self.get_super_key_id_check_unlockable_or_delete(uid, &alias)
- .and_then::<i64, _>(|_| {
- Err(Error::Rc(ResponseCode::LOCKED))
- .context("Super key present but locked.")
- })
- .unwrap_err()
- } else {
- e
- }
- })
- .context("In check_and_import: Trying to load legacy blob.")?;
-
- let (km_blob_params, superseded_blob) = self
- .characteristics_file_to_cache(km_blob_params, &super_key, uid, &alias)
- .context("In check_and_import: Trying to update legacy charateristics.")?;
-
+ .load_by_uid_alias(uid, &alias, None)
+ .context("In check_and_migrate: Trying to load legacy blob.")?;
let result = match km_blob_params {
Some((km_blob, params)) => {
let is_strongbox = km_blob.is_strongbox();
-
let (blob, mut blob_metadata) = match km_blob.take_value() {
BlobValue::Encrypted { iv, tag, data } => {
// Get super key id for user id.
- let super_key_id = self
- .get_super_key_id_check_unlockable_or_delete(uid, &alias)
- .context("In check_and_import: Failed to get super key id.")?;
+ let user_id = uid_to_android_user(uid as u32);
+
+ let super_key_id = match self
+ .db
+ .load_super_key(&USER_SUPER_KEY, user_id)
+ .context("In check_and_migrate: Failed to load super key")?
+ {
+ Some((_, entry)) => entry.id(),
+ None => {
+ // This might be the first time we access the super key,
+ // and it may not have been migrated. We cannot import
+ // the legacy super_key key now, because we need to reencrypt
+ // it which we cannot do if we are not unlocked, which we are
+ // not because otherwise the key would have been migrated.
+ // We can check though if the key exists. If it does,
+ // we can return Locked. Otherwise, we can delete the
+ // key and return NotFound, because the key will never
+ // be unlocked again.
+ if self.legacy_loader.has_super_key(user_id) {
+ return Err(Error::Rc(ResponseCode::LOCKED)).context(concat!(
+ "In check_and_migrate: Cannot migrate super key of this ",
+ "key while user is locked."
+ ));
+ } else {
+ self.legacy_loader.remove_keystore_entry(uid, &alias).context(
+ concat!(
+ "In check_and_migrate: ",
+ "Trying to remove obsolete key."
+ ),
+ )?;
+ return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+ .context("In check_and_migrate: Obsolete key.");
+ }
+ }
+ };
let mut blob_metadata = BlobMetaData::new();
blob_metadata.add(BlobMetaEntry::Iv(iv.to_vec()));
@@ -659,79 +505,74 @@ impl LegacyImporterState {
BlobValue::Decrypted(data) => (LegacyBlob::ZVec(data), BlobMetaData::new()),
_ => {
return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
- .context("In check_and_import: Legacy key has unexpected type.")
+ .context("In check_and_migrate: Legacy key has unexpected type.")
}
};
let km_uuid = self
.get_km_uuid(is_strongbox)
- .context("In check_and_import: Trying to get KM UUID")?;
+ .context("In check_and_migrate: Trying to get KM UUID")?;
blob_metadata.add(BlobMetaEntry::KmUuid(km_uuid));
let mut metadata = KeyMetaData::new();
let creation_date = DateTime::now()
- .context("In check_and_import: Trying to make creation time.")?;
+ .context("In check_and_migrate: Trying to make creation time.")?;
metadata.add(KeyMetaEntry::CreationDate(creation_date));
- let blob_info = BlobInfo::new_with_superseded(
- &blob,
- &blob_metadata,
- superseded_blob.as_ref().map(|(b, m)| (&**b, m)),
- );
// Store legacy key in the database.
self.db
.store_new_key(
&key,
KeyType::Client,
&params,
- &blob_info,
+ &(&blob, &blob_metadata),
&CertificateInfo::new(user_cert, ca_cert),
&metadata,
&km_uuid,
)
- .context("In check_and_import.")?;
+ .context("In check_and_migrate.")?;
Ok(())
}
None => {
if let Some(ca_cert) = ca_cert {
self.db
.store_new_certificate(&key, KeyType::Client, &ca_cert, &KEYSTORE_UUID)
- .context("In check_and_import: Failed to insert new certificate.")?;
+ .context("In check_and_migrate: Failed to insert new certificate.")?;
Ok(())
} else {
Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
- .context("In check_and_import: Legacy key not found.")
+ .context("In check_and_migrate: Legacy key not found.")
}
}
};
match result {
Ok(()) => {
- // Add the key to the imported_keys list.
- self.recently_imported.insert(RecentImport::new(uid, alias.clone()));
+ // Add the key to the migrated_keys list.
+ self.recently_migrated.insert(RecentMigration::new(uid, alias.clone()));
// Delete legacy key from the file system
self.legacy_loader
.remove_keystore_entry(uid, &alias)
- .context("In check_and_import: Trying to remove imported key.")?;
+ .context("In check_and_migrate: Trying to remove migrated key.")?;
Ok(())
}
Err(e) => Err(e),
}
}
- fn check_and_import_super_key(&mut self, user_id: u32, pw: &Password) -> Result<()> {
- if self.recently_imported_super_key.contains(&user_id) {
+ fn check_and_migrate_super_key(&mut self, user_id: u32, pw: &Password) -> Result<()> {
+ if self.recently_migrated_super_key.contains(&user_id) {
return Ok(());
}
if let Some(super_key) = self
.legacy_loader
- .load_super_key(user_id, pw)
- .context("In check_and_import_super_key: Trying to load legacy super key.")?
+ .load_super_key(user_id, &pw)
+ .context("In check_and_migrate_super_key: Trying to load legacy super key.")?
{
let (blob, blob_metadata) =
crate::super_key::SuperKeyManager::encrypt_with_password(&super_key, pw)
- .context("In check_and_import_super_key: Trying to encrypt super key.")?;
+ .context("In check_and_migrate_super_key: Trying to encrypt super key.")?;
self.db
.store_super_key(
@@ -742,20 +583,20 @@ impl LegacyImporterState {
&KeyMetaData::new(),
)
.context(concat!(
- "In check_and_import_super_key: ",
+ "In check_and_migrate_super_key: ",
"Trying to insert legacy super_key into the database."
))?;
self.legacy_loader.remove_super_key(user_id);
- self.recently_imported_super_key.insert(user_id);
+ self.recently_migrated_super_key.insert(user_id);
Ok(())
} else {
Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
- .context("In check_and_import_super_key: No key found do import.")
+ .context("In check_and_migrate_super_key: No key found do migrate.")
}
}
- /// Key importer request to be run by do_serialized.
- /// See LegacyImporter::bulk_delete_uid and LegacyImporter::bulk_delete_user.
+ /// Key migrator request to be run by do_serialized.
+ /// See LegacyMigrator::bulk_delete_uid and LegacyMigrator::bulk_delete_user.
fn bulk_delete(
&mut self,
bulk_delete_request: BulkDeleteRequest,
@@ -789,21 +630,18 @@ impl LegacyImporterState {
for (uid, alias) in aliases
.into_iter()
- .flat_map(|(uid, aliases)| aliases.into_iter().map(move |alias| (uid, alias)))
+ .map(|(uid, aliases)| aliases.into_iter().map(move |alias| (uid, alias)))
+ .flatten()
{
let (km_blob_params, _, _) = self
.legacy_loader
- .load_by_uid_alias(uid, &alias, &None)
+ .load_by_uid_alias(uid, &alias, None)
.context("In bulk_delete: Trying to load legacy blob.")?;
// Determine if the key needs special handling to be deleted.
let (need_gc, is_super_encrypted) = km_blob_params
.as_ref()
.map(|(blob, params)| {
- let params = match params {
- LegacyKeyCharacteristics::Cache(params)
- | LegacyKeyCharacteristics::File(params) => params,
- };
(
params.iter().any(|kp| {
KeyParameterValue::RollbackResistance == *kp.key_parameter_value()
@@ -857,87 +695,37 @@ impl LegacyImporterState {
self.legacy_loader
.remove_keystore_entry(uid, &alias)
- .context("In bulk_delete: Trying to remove imported key.")?;
+ .context("In bulk_delete: Trying to remove migrated key.")?;
}
Ok(())
}
fn has_super_key(&mut self, user_id: u32) -> Result<bool> {
- Ok(self.recently_imported_super_key.contains(&user_id)
+ Ok(self.recently_migrated_super_key.contains(&user_id)
|| self.legacy_loader.has_super_key(user_id))
}
fn check_empty(&self) -> u8 {
if self.legacy_loader.is_empty().unwrap_or(false) {
- LegacyImporter::STATE_EMPTY
+ LegacyMigrator::STATE_EMPTY
} else {
- LegacyImporter::STATE_READY
+ LegacyMigrator::STATE_READY
}
}
}
-enum LegacyBlob<'a> {
+enum LegacyBlob {
Vec(Vec<u8>),
ZVec(ZVec),
- Ref(&'a [u8]),
}
-impl Deref for LegacyBlob<'_> {
+impl Deref for LegacyBlob {
type Target = [u8];
fn deref(&self) -> &Self::Target {
match self {
- Self::Vec(v) => v,
- Self::ZVec(v) => v,
- Self::Ref(v) => v,
- }
- }
-}
-
-/// This function takes two KeyParameter lists. The first is assumed to have been retrieved from the
-/// KM back end using km_dev.getKeyCharacteristics. The second is assumed to have been retrieved
-/// from a legacy key characteristics file (not cache) as used in Android P and older. The function
-/// augments the former with entries from the latter only if no equivalent entry is present ignoring.
-/// the security level of enforcement. All entries in the latter are assumed to have security level
-/// KEYSTORE.
-fn augment_legacy_characteristics_file_with_key_characteristics<T>(
- mut from_km: Vec<KeyParameter>,
- legacy: T,
-) -> Vec<KeyParameter>
-where
- T: IntoIterator<Item = KeyParameter>,
-{
- for legacy_kp in legacy.into_iter() {
- if !from_km
- .iter()
- .any(|km_kp| km_kp.key_parameter_value() == legacy_kp.key_parameter_value())
- {
- from_km.push(legacy_kp);
+ Self::Vec(v) => &v,
+ Self::ZVec(v) => &v,
}
}
- from_km
-}
-
-/// Attempts to retrieve the key characteristics for the given blob from the KM back end with the
-/// given UUID. It may upgrade the key blob in the process. In that case the upgraded blob is
-/// returned as the second tuple member.
-fn get_key_characteristics_without_app_data(
- uuid: &Uuid,
- blob: &[u8],
-) -> Result<(Vec<KeyParameter>, Option<Vec<u8>>)> {
- let (km_dev, _) = crate::globals::get_keymint_dev_by_uuid(uuid)
- .with_context(|| format!("In foo: Trying to get km device for id {:?}", uuid))?;
-
- let (characteristics, upgraded_blob) = upgrade_keyblob_if_required_with(
- &*km_dev,
- blob,
- &[],
- |blob| {
- let _wd = wd::watch_millis("In foo: Calling GetKeyCharacteristics.", 500);
- map_km_error(km_dev.getKeyCharacteristics(blob, &[], &[]))
- },
- |_| Ok(()),
- )
- .context("In foo.")?;
- Ok((key_characteristics_to_internal(characteristics), upgraded_blob))
}
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 4a23843f..8b629b10 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -29,7 +29,7 @@ pub mod id_rotation;
/// Internal Representation of Key Parameter and convenience functions.
pub mod key_parameter;
pub mod legacy_blob;
-pub mod legacy_importer;
+pub mod legacy_migrator;
pub mod maintenance;
pub mod metrics;
pub mod metrics_store;
@@ -40,12 +40,12 @@ pub mod remote_provisioning;
pub mod security_level;
pub mod service;
pub mod shared_secret_negotiation;
+pub mod try_insert;
pub mod utils;
mod attestation_key_utils;
mod audit_log;
mod gc;
-mod km_compat;
mod super_key;
#[cfg(feature = "watchdog")]
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 1fca5d96..3180e5df 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -19,15 +19,12 @@ use crate::error::map_km_error;
use crate::error::map_or_log_err;
use crate::error::Error;
use crate::globals::get_keymint_device;
-use crate::globals::{DB, LEGACY_IMPORTER, SUPER_KEY};
+use crate::globals::{DB, LEGACY_MIGRATOR, SUPER_KEY};
use crate::permission::{KeyPerm, KeystorePerm};
-use crate::super_key::{SuperKeyManager, UserState};
-use crate::utils::{
- check_key_permission, check_keystore_permission, uid_to_android_user, watchdog as wd,
-};
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- IKeyMintDevice::IKeyMintDevice, SecurityLevel::SecurityLevel,
-};
+use crate::super_key::UserState;
+use crate::utils::{check_key_permission, check_keystore_permission, watchdog as wd};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::IKeyMintDevice::IKeyMintDevice;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
use android_security_maintenance::aidl::android::security::maintenance::{
IKeystoreMaintenance::{BnKeystoreMaintenance, IKeystoreMaintenance},
UserState::UserState as AidlUserState,
@@ -69,25 +66,24 @@ impl Maintenance {
}
fn on_user_password_changed(user_id: i32, password: Option<Password>) -> Result<()> {
- // Check permission. Function should return if this failed. Therefore having '?' at the end
- // is very important.
- check_keystore_permission(KeystorePerm::ChangePassword)
+ //Check permission. Function should return if this failed. Therefore having '?' at the end
+ //is very important.
+ check_keystore_permission(KeystorePerm::change_password())
.context("In on_user_password_changed.")?;
- let mut skm = SUPER_KEY.write().unwrap();
-
if let Some(pw) = password.as_ref() {
DB.with(|db| {
- skm.unlock_screen_lock_bound_key(&mut db.borrow_mut(), user_id as u32, pw)
+ SUPER_KEY.unlock_screen_lock_bound_key(&mut db.borrow_mut(), user_id as u32, pw)
})
.context("In on_user_password_changed: unlock_screen_lock_bound_key failed")?;
}
match DB
.with(|db| {
- skm.reset_or_init_user_and_get_user_state(
+ UserState::get_with_password_changed(
&mut db.borrow_mut(),
- &LEGACY_IMPORTER,
+ &LEGACY_MIGRATOR,
+ &SUPER_KEY,
user_id as u32,
password.as_ref(),
)
@@ -109,12 +105,12 @@ impl Maintenance {
fn add_or_remove_user(&self, user_id: i32) -> Result<()> {
// Check permission. Function should return if this failed. Therefore having '?' at the end
// is very important.
- check_keystore_permission(KeystorePerm::ChangeUser).context("In add_or_remove_user.")?;
-
+ check_keystore_permission(KeystorePerm::change_user()).context("In add_or_remove_user.")?;
DB.with(|db| {
- SUPER_KEY.write().unwrap().reset_user(
+ UserState::reset_user(
&mut db.borrow_mut(),
- &LEGACY_IMPORTER,
+ &SUPER_KEY,
+ &LEGACY_MIGRATOR,
user_id as u32,
false,
)
@@ -127,9 +123,9 @@ impl Maintenance {
fn clear_namespace(&self, domain: Domain, nspace: i64) -> Result<()> {
// Permission check. Must return on error. Do not touch the '?'.
- check_keystore_permission(KeystorePerm::ClearUID).context("In clear_namespace.")?;
+ check_keystore_permission(KeystorePerm::clear_uid()).context("In clear_namespace.")?;
- LEGACY_IMPORTER
+ LEGACY_MIGRATOR
.bulk_delete_uid(domain, nspace)
.context("In clear_namespace: Trying to delete legacy keys.")?;
DB.with(|db| db.borrow_mut().unbind_keys_for_namespace(domain, nspace))
@@ -142,14 +138,10 @@ impl Maintenance {
fn get_state(user_id: i32) -> Result<AidlUserState> {
// Check permission. Function should return if this failed. Therefore having '?' at the end
// is very important.
- check_keystore_permission(KeystorePerm::GetState).context("In get_state.")?;
+ check_keystore_permission(KeystorePerm::get_state()).context("In get_state.")?;
let state = DB
.with(|db| {
- SUPER_KEY.read().unwrap().get_user_state(
- &mut db.borrow_mut(),
- &LEGACY_IMPORTER,
- user_id as u32,
- )
+ UserState::get(&mut db.borrow_mut(), &LEGACY_MIGRATOR, &SUPER_KEY, user_id as u32)
})
.context("In get_state. Trying to get UserState.")?;
@@ -162,10 +154,13 @@ impl Maintenance {
fn call_with_watchdog<F>(sec_level: SecurityLevel, name: &'static str, op: &F) -> Result<()>
where
- F: Fn(Strong<dyn IKeyMintDevice>) -> binder::Result<()>,
+ F: Fn(Strong<dyn IKeyMintDevice>) -> binder::public_api::Result<()>,
{
- let (km_dev, _, _) = get_keymint_device(&sec_level)
+ let (dev, _, _) = get_keymint_device(&sec_level)
.context("In call_with_watchdog: getting keymint device")?;
+ let km_dev: Strong<dyn IKeyMintDevice> = dev
+ .get_interface()
+ .context("In call_with_watchdog: getting keymint device interface")?;
let _wp = wd::watch_millis_with("In call_with_watchdog", 500, move || {
format!("Seclevel: {:?} Op: {}", sec_level, name)
@@ -176,7 +171,7 @@ impl Maintenance {
fn call_on_all_security_levels<F>(name: &'static str, op: F) -> Result<()>
where
- F: Fn(Strong<dyn IKeyMintDevice>) -> binder::Result<()>,
+ F: Fn(Strong<dyn IKeyMintDevice>) -> binder::public_api::Result<()>,
{
let sec_levels = [
(SecurityLevel::TRUSTED_ENVIRONMENT, "TRUSTED_ENVIRONMENT"),
@@ -202,13 +197,11 @@ impl Maintenance {
}
fn early_boot_ended() -> Result<()> {
- check_keystore_permission(KeystorePerm::EarlyBootEnded)
+ check_keystore_permission(KeystorePerm::early_boot_ended())
.context("In early_boot_ended. Checking permission")?;
log::info!("In early_boot_ended.");
- if let Err(e) =
- DB.with(|db| SuperKeyManager::set_up_boot_level_cache(&SUPER_KEY, &mut db.borrow_mut()))
- {
+ if let Err(e) = DB.with(|db| SUPER_KEY.set_up_boot_level_cache(&mut db.borrow_mut())) {
log::error!("SUPER_KEY.set_up_boot_level_cache failed:\n{:?}\n:(", e);
}
Maintenance::call_on_all_security_levels("earlyBootEnded", |dev| dev.earlyBootEnded())
@@ -216,66 +209,53 @@ impl Maintenance {
fn on_device_off_body() -> Result<()> {
// Security critical permission check. This statement must return on fail.
- check_keystore_permission(KeystorePerm::ReportOffBody).context("In on_device_off_body.")?;
+ check_keystore_permission(KeystorePerm::report_off_body())
+ .context("In on_device_off_body.")?;
DB.with(|db| db.borrow_mut().update_last_off_body(MonotonicRawTime::now()));
Ok(())
}
fn migrate_key_namespace(source: &KeyDescriptor, destination: &KeyDescriptor) -> Result<()> {
- let calling_uid = ThreadState::get_calling_uid();
-
- match source.domain {
- Domain::SELINUX | Domain::KEY_ID | Domain::APP => (),
- _ => {
- return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
- "In migrate_key_namespace: \
- Source domain must be one of APP, SELINUX, or KEY_ID.",
- )
- }
- };
-
- match destination.domain {
- Domain::SELINUX | Domain::APP => (),
- _ => {
- return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
- "In migrate_key_namespace: \
- Destination domain must be one of APP or SELINUX.",
- )
- }
- };
-
- let user_id = uid_to_android_user(calling_uid);
-
- let super_key = SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(user_id);
+ let caller_uid = ThreadState::get_calling_uid();
DB.with(|db| {
- let (key_id_guard, _) = LEGACY_IMPORTER
- .with_try_import(source, calling_uid, super_key, || {
- db.borrow_mut().load_key_entry(
- source,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- calling_uid,
- |k, av| {
- check_key_permission(KeyPerm::Use, k, &av)?;
- check_key_permission(KeyPerm::Delete, k, &av)?;
- check_key_permission(KeyPerm::Grant, k, &av)
- },
- )
- })
- .context("In migrate_key_namespace: Failed to load key blob.")?;
- {
- db.borrow_mut().migrate_key_namespace(key_id_guard, destination, calling_uid, |k| {
- check_key_permission(KeyPerm::Rebind, k, &None)
- })
- }
+ let key_id_guard = match source.domain {
+ Domain::APP | Domain::SELINUX | Domain::KEY_ID => {
+ let (key_id_guard, _) = LEGACY_MIGRATOR
+ .with_try_migrate(&source, caller_uid, || {
+ db.borrow_mut().load_key_entry(
+ &source,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ caller_uid,
+ |k, av| {
+ check_key_permission(KeyPerm::use_(), k, &av)?;
+ check_key_permission(KeyPerm::delete(), k, &av)?;
+ check_key_permission(KeyPerm::grant(), k, &av)
+ },
+ )
+ })
+ .context("In migrate_key_namespace: Failed to load key blob.")?;
+ key_id_guard
+ }
+ _ => {
+ return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(concat!(
+ "In migrate_key_namespace: ",
+ "Source domain must be one of APP, SELINUX, or KEY_ID."
+ ))
+ }
+ };
+
+ db.borrow_mut().migrate_key_namespace(key_id_guard, destination, caller_uid, |k| {
+ check_key_permission(KeyPerm::rebind(), k, &None)
+ })
})
}
fn delete_all_keys() -> Result<()> {
// Security critical permission check. This statement must return on fail.
- check_keystore_permission(KeystorePerm::DeleteAllKeys)
+ check_keystore_permission(KeystorePerm::delete_all_keys())
.context("In delete_all_keys. Checking permission")?;
log::info!("In delete_all_keys.");
diff --git a/keystore2/src/metrics.rs b/keystore2/src/metrics.rs
index 3d8d6d3f..42295b7c 100644
--- a/keystore2/src/metrics.rs
+++ b/keystore2/src/metrics.rs
@@ -41,7 +41,7 @@ impl Metrics {
fn pull_metrics(&self, atom_id: AtomID) -> Result<Vec<KeystoreAtom>> {
// Check permission. Function should return if this failed. Therefore having '?' at the end
// is very important.
- check_keystore_permission(KeystorePerm::PullMetrics).context("In pull_metrics.")?;
+ check_keystore_permission(KeystorePerm::pull_metrics()).context("In pull_metrics.")?;
METRICS_STORE.get_atoms(atom_id)
}
}
diff --git a/keystore2/src/metrics_store.rs b/keystore2/src/metrics_store.rs
index 62a7d135..32067b95 100644
--- a/keystore2/src/metrics_store.rs
+++ b/keystore2/src/metrics_store.rs
@@ -17,7 +17,7 @@
//! stores them in an in-memory store.
//! 2. Returns the collected metrics when requested by the statsd proxy.
-use crate::error::{get_error_code, Error};
+use crate::error::get_error_code;
use crate::globals::DB;
use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
use crate::operation::Outcome;
@@ -44,10 +44,9 @@ use android_security_metrics::aidl::android::security::metrics::{
RkpPoolStats::RkpPoolStats, SecurityLevel::SecurityLevel as MetricsSecurityLevel,
Storage::Storage as MetricsStorage,
};
-use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
use anyhow::{Context, Result};
+use keystore2_system_property::{write, PropertyWatcher, PropertyWatcherError};
use lazy_static::lazy_static;
-use rustutils::system_properties::PropertyWatcherError;
use std::collections::HashMap;
use std::sync::Mutex;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
@@ -288,7 +287,6 @@ fn process_key_creation_event_stats<U>(
EcCurve::P_256 => MetricsEcCurve::P_256,
EcCurve::P_384 => MetricsEcCurve::P_384,
EcCurve::P_521 => MetricsEcCurve::P_521,
- EcCurve::CURVE_25519 => MetricsEcCurve::CURVE_25519,
_ => MetricsEcCurve::EC_CURVE_UNSPECIFIED,
}
}
@@ -562,14 +560,10 @@ fn pull_storage_stats() -> Result<Vec<KeystoreAtom>> {
fn pull_attestation_pool_stats() -> Result<Vec<KeystoreAtom>> {
let mut atoms = Vec::<KeystoreAtom>::new();
for sec_level in &[SecurityLevel::TRUSTED_ENVIRONMENT, SecurityLevel::STRONGBOX] {
- // set the expired_by date to be three days from now
let expired_by = SystemTime::now()
- .checked_add(Duration::from_secs(60 * 60 * 24 * 3))
- .ok_or(Error::Rc(ResponseCode::SYSTEM_ERROR))
- .context("In pull_attestation_pool_stats: Failed to compute expired by system time.")?
.duration_since(UNIX_EPOCH)
- .context("In pull_attestation_pool_stats: Failed to compute expired by duration.")?
- .as_millis() as i64;
+ .unwrap_or_else(|_| Duration::new(0, 0))
+ .as_secs() as i64;
let result = get_pool_status(expired_by, *sec_level);
@@ -599,9 +593,8 @@ fn pull_attestation_pool_stats() -> Result<Vec<KeystoreAtom>> {
}
/// Log error events related to Remote Key Provisioning (RKP).
-pub fn log_rkp_error_stats(rkp_error: MetricsRkpError, sec_level: &SecurityLevel) {
- let rkp_error_stats = KeystoreAtomPayload::RkpErrorStats(
- RkpErrorStats { rkpError: rkp_error, security_level: process_security_level(*sec_level) });
+pub fn log_rkp_error_stats(rkp_error: MetricsRkpError) {
+ let rkp_error_stats = KeystoreAtomPayload::RkpErrorStats(RkpErrorStats { rkpError: rkp_error });
METRICS_STORE.insert_atom(AtomID::RKP_ERROR_STATS, rkp_error_stats);
}
@@ -633,9 +626,7 @@ pub fn update_keystore_crash_sysprop() {
}
};
- if let Err(e) =
- rustutils::system_properties::write(KEYSTORE_CRASH_COUNT_PROPERTY, &new_count.to_string())
- {
+ if let Err(e) = write(KEYSTORE_CRASH_COUNT_PROPERTY, &new_count.to_string()) {
log::error!(
concat!(
"In update_keystore_crash_sysprop:: ",
@@ -648,11 +639,12 @@ pub fn update_keystore_crash_sysprop() {
/// Read the system property: keystore.crash_count.
pub fn read_keystore_crash_count() -> Result<i32> {
- rustutils::system_properties::read("keystore.crash_count")
- .context("In read_keystore_crash_count: Failed read property.")?
- .context("In read_keystore_crash_count: Property not set.")?
- .parse::<i32>()
- .map_err(std::convert::Into::into)
+ let mut prop_reader = PropertyWatcher::new("keystore.crash_count").context(concat!(
+ "In read_keystore_crash_count: Failed to create reader a PropertyWatcher."
+ ))?;
+ prop_reader
+ .read(|_n, v| v.parse::<i32>().map_err(std::convert::Into::into))
+ .context("In read_keystore_crash_count: Failed to read the existing system property.")
}
/// Enum defining the bit position for each padding mode. Since padding mode can be repeatable, it
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index 5da3b326..71718642 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -128,12 +128,12 @@
use crate::enforcements::AuthInfo;
use crate::error::{map_err_with, map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
use crate::metrics_store::log_key_operation_event_stats;
-use crate::utils::watchdog as wd;
+use crate::utils::{watchdog as wd, Asp};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
IKeyMintOperation::IKeyMintOperation, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
SecurityLevel::SecurityLevel,
};
-use android_hardware_security_keymint::binder::{BinderFeatures, Strong};
+use android_hardware_security_keymint::binder::BinderFeatures;
use android_system_keystore2::aidl::android::system::keystore2::{
IKeystoreOperation::BnKeystoreOperation, IKeystoreOperation::IKeystoreOperation,
};
@@ -170,7 +170,7 @@ pub enum Outcome {
pub struct Operation {
// The index of this operation in the OperationDb.
index: usize,
- km_op: Strong<dyn IKeyMintOperation>,
+ km_op: Asp,
last_usage: Mutex<Instant>,
outcome: Mutex<Outcome>,
owner: u32, // Uid of the operation's owner.
@@ -222,7 +222,7 @@ impl Operation {
) -> Self {
Self {
index,
- km_op,
+ km_op: Asp::new(km_op.as_binder()),
last_usage: Mutex::new(Instant::now()),
outcome: Mutex::new(Outcome::Unknown),
owner,
@@ -282,10 +282,19 @@ impl Operation {
}
*locked_outcome = Outcome::Pruned;
+ let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
+ match self.km_op.get_interface() {
+ Ok(km_op) => km_op,
+ Err(e) => {
+ log::error!("In prune: Failed to get KeyMintOperation interface.\n {:?}", e);
+ return Err(Error::sys());
+ }
+ };
+
let _wp = wd::watch_millis("In Operation::prune: calling abort()", 500);
// We abort the operation. If there was an error we log it but ignore it.
- if let Err(e) = map_km_error(self.km_op.abort()) {
+ if let Err(e) = map_km_error(km_op.abort()) {
log::error!("In prune: KeyMint::abort failed with {:?}.", e);
}
@@ -353,6 +362,9 @@ impl Operation {
Self::check_input_length(aad_input).context("In update_aad")?;
self.touch();
+ let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
+ self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
+
let (hat, tst) = self
.auth_info
.lock()
@@ -362,7 +374,7 @@ impl Operation {
self.update_outcome(&mut *outcome, {
let _wp = wd::watch_millis("Operation::update_aad: calling updateAad", 500);
- map_km_error(self.km_op.updateAad(aad_input, hat.as_ref(), tst.as_ref()))
+ map_km_error(km_op.updateAad(aad_input, hat.as_ref(), tst.as_ref()))
})
.context("In update_aad: KeyMint::update failed.")?;
@@ -376,6 +388,9 @@ impl Operation {
Self::check_input_length(input).context("In update")?;
self.touch();
+ let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
+ self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
+
let (hat, tst) = self
.auth_info
.lock()
@@ -386,7 +401,7 @@ impl Operation {
let output = self
.update_outcome(&mut *outcome, {
let _wp = wd::watch_millis("Operation::update: calling update", 500);
- map_km_error(self.km_op.update(input, hat.as_ref(), tst.as_ref()))
+ map_km_error(km_op.update(input, hat.as_ref(), tst.as_ref()))
})
.context("In update: KeyMint::update failed.")?;
@@ -406,6 +421,9 @@ impl Operation {
}
self.touch();
+ let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
+ self.km_op.get_interface().context("In finish: Failed to get KeyMintOperation.")?;
+
let (hat, tst, confirmation_token) = self
.auth_info
.lock()
@@ -416,7 +434,7 @@ impl Operation {
let output = self
.update_outcome(&mut *outcome, {
let _wp = wd::watch_millis("Operation::finish: calling finish", 500);
- map_km_error(self.km_op.finish(
+ map_km_error(km_op.finish(
input,
signature,
hat.as_ref(),
@@ -444,10 +462,12 @@ impl Operation {
fn abort(&self, outcome: Outcome) -> Result<()> {
let mut locked_outcome = self.check_active().context("In abort")?;
*locked_outcome = outcome;
+ let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
+ self.km_op.get_interface().context("In abort: Failed to get KeyMintOperation.")?;
{
let _wp = wd::watch_millis("Operation::abort: calling abort", 500);
- map_km_error(self.km_op.abort()).context("In abort: KeyMint::abort failed.")
+ map_km_error(km_op.abort()).context("In abort: KeyMint::abort failed.")
}
}
}
@@ -493,7 +513,7 @@ impl OperationDb {
/// owner uid and returns a new Operation wrapped in a `std::sync::Arc`.
pub fn create_operation(
&self,
- km_op: binder::Strong<dyn IKeyMintOperation>,
+ km_op: binder::public_api::Strong<dyn IKeyMintOperation>,
owner: u32,
auth_info: AuthInfo,
forced: bool,
@@ -771,7 +791,9 @@ impl KeystoreOperation {
/// BnKeystoreOperation proxy object. It also enables
/// `BinderFeatures::set_requesting_sid` on the new interface, because
/// we need it for checking Keystore permissions.
- pub fn new_native_binder(operation: Arc<Operation>) -> binder::Strong<dyn IKeystoreOperation> {
+ pub fn new_native_binder(
+ operation: Arc<Operation>,
+ ) -> binder::public_api::Strong<dyn IKeystoreOperation> {
BnKeystoreOperation::new_binder(
Self { operation: Mutex::new(Some(operation)) },
BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
@@ -819,7 +841,7 @@ impl KeystoreOperation {
impl binder::Interface for KeystoreOperation {}
impl IKeystoreOperation for KeystoreOperation {
- fn updateAad(&self, aad_input: &[u8]) -> binder::Result<()> {
+ fn updateAad(&self, aad_input: &[u8]) -> binder::public_api::Result<()> {
let _wp = wd::watch_millis("IKeystoreOperation::updateAad", 500);
map_or_log_err(
self.with_locked_operation(
@@ -830,7 +852,7 @@ impl IKeystoreOperation for KeystoreOperation {
)
}
- fn update(&self, input: &[u8]) -> binder::Result<Option<Vec<u8>>> {
+ fn update(&self, input: &[u8]) -> binder::public_api::Result<Option<Vec<u8>>> {
let _wp = wd::watch_millis("IKeystoreOperation::update", 500);
map_or_log_err(
self.with_locked_operation(
@@ -844,7 +866,7 @@ impl IKeystoreOperation for KeystoreOperation {
&self,
input: Option<&[u8]>,
signature: Option<&[u8]>,
- ) -> binder::Result<Option<Vec<u8>>> {
+ ) -> binder::public_api::Result<Option<Vec<u8>>> {
let _wp = wd::watch_millis("IKeystoreOperation::finish", 500);
map_or_log_err(
self.with_locked_operation(
@@ -855,7 +877,7 @@ impl IKeystoreOperation for KeystoreOperation {
)
}
- fn abort(&self) -> binder::Result<()> {
+ fn abort(&self) -> binder::public_api::Result<()> {
let _wp = wd::watch_millis("IKeystoreOperation::abort", 500);
map_err_with(
self.with_locked_operation(
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index 22509c46..4add8992 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -18,18 +18,23 @@
//! It also provides KeystorePerm and KeyPerm as convenience wrappers for the SELinux permission
//! defined by keystore2 and keystore2_key respectively.
-use crate::error::Error as KsError;
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
};
-use anyhow::Context as AnyhowContext;
-use keystore2_selinux as selinux;
-use lazy_static::lazy_static;
-use selinux::{implement_class, Backend, ClassPermission};
+
use std::cmp::PartialEq;
use std::convert::From;
use std::ffi::CStr;
+use crate::error::Error as KsError;
+use keystore2_selinux as selinux;
+
+use anyhow::Context as AnyhowContext;
+
+use selinux::Backend;
+
+use lazy_static::lazy_static;
+
// Replace getcon with a mock in the test situation
#[cfg(not(test))]
use selinux::getcon;
@@ -47,107 +52,273 @@ fn lookup_keystore2_key_context(namespace: i64) -> anyhow::Result<selinux::Conte
KEYSTORE2_KEY_LABEL_BACKEND.lookup(&namespace.to_string())
}
-implement_class!(
+/// ## Background
+///
+/// AIDL enums are represented as constants of the form:
+/// ```
+/// mod EnumName {
+/// pub type EnumName = i32;
+/// pub const Variant1: EnumName = <value1>;
+/// pub const Variant2: EnumName = <value2>;
+/// ...
+/// }
+///```
+/// This macro wraps the enum in a new type, e.g., `MyPerm` and maps each variant to an SELinux
+/// permission while providing the following interface:
+/// * From<EnumName> and Into<EnumName> are implemented. Where the implementation of From maps
+/// any variant not specified to the default.
+/// * Every variant has a constructor with a name corresponding to its lower case SELinux string
+/// representation.
+/// * `MyPerm.to_selinux(&self)` returns the SELinux string representation of the
+/// represented permission.
+///
+/// ## Special behavior
+/// If the keyword `use` appears as an selinux name `use_` is used as identifier for the
+/// constructor function (e.g. `MePerm::use_()`) but the string returned by `to_selinux` will
+/// still be `"use"`.
+///
+/// ## Example
+/// ```
+///
+/// implement_permission!(
+/// /// MyPerm documentation.
+/// #[derive(Clone, Copy, Debug, PartialEq)]
+/// MyPerm from EnumName with default (None, none) {}
+/// Variant1, selinux name: variant1;
+/// Variant2, selinux name: variant1;
+/// }
+/// );
+/// ```
+macro_rules! implement_permission_aidl {
+ // This rule provides the public interface of the macro. And starts the preprocessing
+ // recursion (see below).
+ ($(#[$m:meta])* $name:ident from $aidl_name:ident with default ($($def:tt)*)
+ { $($element:tt)* })
+ => {
+ implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*), [],
+ $($element)*);
+ };
+
+ // The following three rules recurse through the elements of the form
+ // `<enum variant>, selinux name: <selinux_name>;`
+ // preprocessing the input.
+
+ // The first rule terminates the recursion and passes the processed arguments to the final
+ // rule that spills out the implementation.
+ (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*], ) => {
+ implement_permission_aidl!(@end $($m)*, $name, $aidl_name, ($($def)*) { $($out)* } );
+ };
+
+ // The second rule is triggered if the selinux name of an element is literally `use`.
+ // It produces the tuple `<enum variant>, use_, use;`
+ // and appends it to the out list.
+ (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*],
+ $e_name:ident, selinux name: use; $($element:tt)*)
+ => {
+ implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*),
+ [$($out)* $e_name, use_, use;], $($element)*);
+ };
+
+ // The third rule is the default rule which replaces every input tuple with
+ // `<enum variant>, <selinux_name>, <selinux_name>;`
+ // and appends the result to the out list.
+ (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*],
+ $e_name:ident, selinux name: $e_str:ident; $($element:tt)*)
+ => {
+ implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*),
+ [$($out)* $e_name, $e_str, $e_str;], $($element)*);
+ };
+
+ (@end $($m:meta)*, $name:ident, $aidl_name:ident,
+ ($def_name:ident, $def_selinux_name:ident) {
+ $($element_name:ident, $element_identifier:ident,
+ $selinux_name:ident;)*
+ })
+ =>
+ {
+ $(#[$m])*
+ pub struct $name(pub $aidl_name);
+
+ impl From<$aidl_name> for $name {
+ fn from (p: $aidl_name) -> Self {
+ match p {
+ $aidl_name::$def_name => Self($aidl_name::$def_name),
+ $($aidl_name::$element_name => Self($aidl_name::$element_name),)*
+ _ => Self($aidl_name::$def_name),
+ }
+ }
+ }
+
+ impl From<$name> for $aidl_name {
+ fn from(p: $name) -> $aidl_name {
+ p.0
+ }
+ }
+
+ impl $name {
+ /// Returns a string representation of the permission as required by
+ /// `selinux::check_access`.
+ pub fn to_selinux(&self) -> &'static str {
+ match self {
+ Self($aidl_name::$def_name) => stringify!($def_selinux_name),
+ $(Self($aidl_name::$element_name) => stringify!($selinux_name),)*
+ _ => stringify!($def_selinux_name),
+ }
+ }
+
+ /// Creates an instance representing a permission with the same name.
+ pub const fn $def_selinux_name() -> Self { Self($aidl_name::$def_name) }
+ $(
+ /// Creates an instance representing a permission with the same name.
+ pub const fn $element_identifier() -> Self { Self($aidl_name::$element_name) }
+ )*
+ }
+ };
+}
+
+implement_permission_aidl!(
/// KeyPerm provides a convenient abstraction from the SELinux class `keystore2_key`.
/// At the same time it maps `KeyPermissions` from the Keystore 2.0 AIDL Grant interface to
- /// the SELinux permissions.
- #[repr(i32)]
- #[selinux(class_name = keystore2_key)]
- #[derive(Clone, Copy, Debug, PartialEq)]
- pub enum KeyPerm {
- /// Checked when convert_storage_key_to_ephemeral is called.
- #[selinux(name = convert_storage_key_to_ephemeral)]
- ConvertStorageKeyToEphemeral = KeyPermission::CONVERT_STORAGE_KEY_TO_EPHEMERAL.0,
- /// Checked when the caller tries do delete a key.
- #[selinux(name = delete)]
- Delete = KeyPermission::DELETE.0,
- /// Checked when the caller tries to use a unique id.
- #[selinux(name = gen_unique_id)]
- GenUniqueId = KeyPermission::GEN_UNIQUE_ID.0,
- /// Checked when the caller tries to load a key.
- #[selinux(name = get_info)]
- GetInfo = KeyPermission::GET_INFO.0,
- /// Checked when the caller attempts to grant a key to another uid.
- /// Also used for gating key migration attempts.
- #[selinux(name = grant)]
- Grant = KeyPermission::GRANT.0,
- /// Checked when the caller attempts to use Domain::BLOB.
- #[selinux(name = manage_blob)]
- ManageBlob = KeyPermission::MANAGE_BLOB.0,
- /// Checked when the caller tries to create a key which implies rebinding
- /// an alias to the new key.
- #[selinux(name = rebind)]
- Rebind = KeyPermission::REBIND.0,
- /// Checked when the caller attempts to create a forced operation.
- #[selinux(name = req_forced_op)]
- ReqForcedOp = KeyPermission::REQ_FORCED_OP.0,
- /// Checked when the caller attempts to update public key artifacts.
- #[selinux(name = update)]
- Update = KeyPermission::UPDATE.0,
- /// Checked when the caller attempts to use a private or public key.
- #[selinux(name = use)]
- Use = KeyPermission::USE.0,
- /// Checked when the caller attempts to use device ids for attestation.
- #[selinux(name = use_dev_id)]
- UseDevId = KeyPermission::USE_DEV_ID.0,
+ /// the SELinux permissions. With the implement_permission macro, we conveniently
+ /// provide mappings between the wire type bit field values, the rust enum and the SELinux
+ /// string representation.
+ ///
+ /// ## Example
+ ///
+ /// In this access check `KeyPerm::get_info().to_selinux()` would return the SELinux representation
+ /// "info".
+ /// ```
+ /// selinux::check_access(source_context, target_context, "keystore2_key",
+ /// KeyPerm::get_info().to_selinux());
+ /// ```
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ KeyPerm from KeyPermission with default (NONE, none) {
+ CONVERT_STORAGE_KEY_TO_EPHEMERAL, selinux name: convert_storage_key_to_ephemeral;
+ DELETE, selinux name: delete;
+ GEN_UNIQUE_ID, selinux name: gen_unique_id;
+ GET_INFO, selinux name: get_info;
+ GRANT, selinux name: grant;
+ MANAGE_BLOB, selinux name: manage_blob;
+ REBIND, selinux name: rebind;
+ REQ_FORCED_OP, selinux name: req_forced_op;
+ UPDATE, selinux name: update;
+ USE, selinux name: use;
+ USE_DEV_ID, selinux name: use_dev_id;
}
);
-implement_class!(
+/// This macro implements an enum with values mapped to SELinux permission names.
+/// The below example wraps the enum MyPermission in the tuple struct `MyPerm` and implements
+/// * From<i32> and Into<i32> are implemented. Where the implementation of From maps
+/// any variant not specified to the default.
+/// * Every variant has a constructor with a name corresponding to its lower case SELinux string
+/// representation.
+/// * `MyPerm.to_selinux(&self)` returns the SELinux string representation of the
+/// represented permission.
+///
+/// ## Example
+/// ```
+/// implement_permission!(
+/// /// MyPerm documentation.
+/// #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+/// MyPerm with default (None = 0, none) {
+/// Foo = 1, selinux name: foo;
+/// Bar = 2, selinux name: bar;
+/// }
+/// );
+/// ```
+macro_rules! implement_permission {
+ // This rule provides the public interface of the macro. And starts the preprocessing
+ // recursion (see below).
+ ($(#[$m:meta])* $name:ident with default
+ ($def_name:ident = $def_val:expr, $def_selinux_name:ident)
+ {
+ $($(#[$element_meta:meta])*
+ $element_name:ident = $element_val:expr, selinux name: $selinux_name:ident;)*
+ })
+ => {
+ $(#[$m])*
+ pub enum $name {
+ /// The default variant of an enum.
+ $def_name = $def_val,
+ $(
+ $(#[$element_meta])*
+ $element_name = $element_val,
+ )*
+ }
+
+ impl From<i32> for $name {
+ fn from (p: i32) -> Self {
+ match p {
+ $def_val => Self::$def_name,
+ $($element_val => Self::$element_name,)*
+ _ => Self::$def_name,
+ }
+ }
+ }
+
+ impl From<$name> for i32 {
+ fn from(p: $name) -> i32 {
+ p as i32
+ }
+ }
+
+ impl $name {
+ /// Returns a string representation of the permission as required by
+ /// `selinux::check_access`.
+ pub fn to_selinux(&self) -> &'static str {
+ match self {
+ Self::$def_name => stringify!($def_selinux_name),
+ $(Self::$element_name => stringify!($selinux_name),)*
+ }
+ }
+
+ /// Creates an instance representing a permission with the same name.
+ pub const fn $def_selinux_name() -> Self { Self::$def_name }
+ $(
+ /// Creates an instance representing a permission with the same name.
+ pub const fn $selinux_name() -> Self { Self::$element_name }
+ )*
+ }
+ };
+}
+
+implement_permission!(
/// KeystorePerm provides a convenient abstraction from the SELinux class `keystore2`.
/// Using the implement_permission macro we get the same features as `KeyPerm`.
- #[selinux(class_name = keystore2)]
#[derive(Clone, Copy, Debug, PartialEq)]
- pub enum KeystorePerm {
+ KeystorePerm with default (None = 0, none) {
/// Checked when a new auth token is installed.
- #[selinux(name = add_auth)]
- AddAuth,
+ AddAuth = 1, selinux name: add_auth;
/// Checked when an app is uninstalled or wiped.
- #[selinux(name = clear_ns)]
- ClearNs,
+ ClearNs = 2, selinux name: clear_ns;
/// Checked when the user state is queried from Keystore 2.0.
- #[selinux(name = get_state)]
- GetState,
+ GetState = 4, selinux name: get_state;
/// Checked when Keystore 2.0 is asked to list a namespace that the caller
/// does not have the get_info permission for.
- #[selinux(name = list)]
- List,
+ List = 8, selinux name: list;
/// Checked when Keystore 2.0 gets locked.
- #[selinux(name = lock)]
- Lock,
+ Lock = 0x10, selinux name: lock;
/// Checked when Keystore 2.0 shall be reset.
- #[selinux(name = reset)]
- Reset,
+ Reset = 0x20, selinux name: reset;
/// Checked when Keystore 2.0 shall be unlocked.
- #[selinux(name = unlock)]
- Unlock,
+ Unlock = 0x40, selinux name: unlock;
/// Checked when user is added or removed.
- #[selinux(name = change_user)]
- ChangeUser,
+ ChangeUser = 0x80, selinux name: change_user;
/// Checked when password of the user is changed.
- #[selinux(name = change_password)]
- ChangePassword,
+ ChangePassword = 0x100, selinux name: change_password;
/// Checked when a UID is cleared.
- #[selinux(name = clear_uid)]
- ClearUID,
+ ClearUID = 0x200, selinux name: clear_uid;
/// Checked when Credstore calls IKeystoreAuthorization to obtain auth tokens.
- #[selinux(name = get_auth_token)]
- GetAuthToken,
+ GetAuthToken = 0x400, selinux name: get_auth_token;
/// Checked when earlyBootEnded() is called.
- #[selinux(name = early_boot_ended)]
- EarlyBootEnded,
+ EarlyBootEnded = 0x800, selinux name: early_boot_ended;
/// Checked when IKeystoreMaintenance::onDeviceOffBody is called.
- #[selinux(name = report_off_body)]
- ReportOffBody,
- /// Checked when IkeystoreMetrics::pullMetrics is called.
- #[selinux(name = pull_metrics)]
- PullMetrics,
+ ReportOffBody = 0x1000, selinux name: report_off_body;
+ /// Checked when IkeystoreMetrics::pullMetris is called.
+ PullMetrics = 0x2000, selinux name: pull_metrics;
/// Checked when IKeystoreMaintenance::deleteAllKeys is called.
- #[selinux(name = delete_all_keys)]
- DeleteAllKeys,
- /// Checked on calls to IRemotelyProvisionedKeyPool::getAttestationKey
- #[selinux(name = get_attestation_key)]
- GetAttestationKey,
+ DeleteAllKeys = 0x4000, selinux name: delete_all_keys;
}
);
@@ -161,17 +332,17 @@ implement_class!(
///
/// ## Example
/// ```
-/// let perms1 = key_perm_set![KeyPerm::Use, KeyPerm::ManageBlob, KeyPerm::Grant];
-/// let perms2 = key_perm_set![KeyPerm::Use, KeyPerm::ManageBlob];
+/// let perms1 = key_perm_set![KeyPerm::use_(), KeyPerm::manage_blob(), KeyPerm::grant()];
+/// let perms2 = key_perm_set![KeyPerm::use_(), KeyPerm::manage_blob()];
///
/// assert!(perms1.includes(perms2))
/// assert!(!perms2.includes(perms1))
///
/// let i = perms1.into_iter();
/// // iteration in ascending order of the permission's numeric representation.
-/// assert_eq(Some(KeyPerm::ManageBlob), i.next());
-/// assert_eq(Some(KeyPerm::Grant), i.next());
-/// assert_eq(Some(KeyPerm::Use), i.next());
+/// assert_eq(Some(KeyPerm::manage_blob()), i.next());
+/// assert_eq(Some(KeyPerm::grant()), i.next());
+/// assert_eq(Some(KeyPerm::use_()), i.next());
/// assert_eq(None, i.next());
/// ```
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
@@ -202,7 +373,7 @@ mod perm {
let p = self.vec.0 & (1 << self.pos);
self.pos += 1;
if p != 0 {
- return Some(KeyPerm::from(p));
+ return Some(KeyPerm::from(KeyPermission(p)));
}
}
}
@@ -211,7 +382,7 @@ mod perm {
impl From<KeyPerm> for KeyPermSet {
fn from(p: KeyPerm) -> Self {
- Self(p as i32)
+ Self((p.0).0 as i32)
}
}
@@ -246,7 +417,7 @@ impl KeyPermSet {
macro_rules! key_perm_set {
() => { KeyPermSet(0) };
($head:expr $(, $tail:expr)* $(,)?) => {
- KeyPermSet($head as i32 $(| $tail as i32)*)
+ KeyPermSet(($head.0).0 $(| ($tail.0).0)*)
};
}
@@ -259,14 +430,14 @@ impl IntoIterator for KeyPermSet {
}
}
-/// Uses `selinux::check_permission` to check if the given caller context `caller_cxt` may access
+/// Uses `selinux::check_access` to check if the given caller context `caller_cxt` may access
/// the given permision `perm` of the `keystore2` security class.
pub fn check_keystore_permission(caller_ctx: &CStr, perm: KeystorePerm) -> anyhow::Result<()> {
let target_context = getcon().context("check_keystore_permission: getcon failed.")?;
- selinux::check_permission(caller_ctx, &target_context, perm)
+ selinux::check_access(caller_ctx, &target_context, "keystore2", perm.to_selinux())
}
-/// Uses `selinux::check_permission` to check if the given caller context `caller_cxt` has
+/// Uses `selinux::check_access` to check if the given caller context `caller_cxt` has
/// all the permissions indicated in `access_vec` for the target domain indicated by the key
/// descriptor `key` in the security class `keystore2_key`.
///
@@ -291,24 +462,27 @@ pub fn check_grant_permission(
_ => return Err(KsError::sys()).context(format!("Cannot grant {:?}.", key.domain)),
};
- selinux::check_permission(caller_ctx, &target_context, KeyPerm::Grant)
+ selinux::check_access(caller_ctx, &target_context, "keystore2_key", "grant")
.context("Grant permission is required when granting.")?;
- if access_vec.includes(KeyPerm::Grant) {
+ if access_vec.includes(KeyPerm::grant()) {
return Err(selinux::Error::perm()).context("Grant permission cannot be granted.");
}
for p in access_vec.into_iter() {
- selinux::check_permission(caller_ctx, &target_context, p).context(format!(
- "check_grant_permission: check_permission failed. \
- The caller may have tried to grant a permission that they don't possess. {:?}",
- p
- ))?
+ selinux::check_access(caller_ctx, &target_context, "keystore2_key", p.to_selinux())
+ .context(format!(
+ concat!(
+ "check_grant_permission: check_access failed. ",
+ "The caller may have tried to grant a permission that they don't possess. {:?}"
+ ),
+ p
+ ))?
}
Ok(())
}
-/// Uses `selinux::check_permission` to check if the given caller context `caller_cxt`
+/// Uses `selinux::check_access` to check if the given caller context `caller_cxt`
/// has the permissions indicated by `perm` for the target domain indicated by the key
/// descriptor `key` in the security class `keystore2_key`.
///
@@ -318,7 +492,7 @@ pub fn check_grant_permission(
/// backend, and the result is used as target context.
/// * `Domain::BLOB` Same as SELinux but the "manage_blob" permission is always checked additionally
/// to the one supplied in `perm`.
-/// * `Domain::GRANT` Does not use selinux::check_permission. Instead the `access_vector`
+/// * `Domain::GRANT` Does not use selinux::check_access. Instead the `access_vector`
/// parameter is queried for permission, which must be supplied in this case.
///
/// ## Return values.
@@ -362,7 +536,7 @@ pub fn check_key_permission(
match access_vector {
Some(_) => {
return Err(selinux::Error::perm())
- .context(format!("\"{}\" not granted", perm.name()));
+ .context(format!("\"{}\" not granted", perm.to_selinux()));
}
None => {
// If DOMAIN_GRANT was selected an access vector must be supplied.
@@ -383,7 +557,12 @@ pub fn check_key_permission(
.context("Domain::BLOB: Failed to lookup namespace.")?;
// If DOMAIN_KEY_BLOB was specified, we check for the "manage_blob"
// permission in addition to the requested permission.
- selinux::check_permission(caller_ctx, &tctx, KeyPerm::ManageBlob)?;
+ selinux::check_access(
+ caller_ctx,
+ &tctx,
+ "keystore2_key",
+ KeyPerm::manage_blob().to_selinux(),
+ )?;
tctx
}
@@ -393,7 +572,7 @@ pub fn check_key_permission(
}
};
- selinux::check_permission(caller_ctx, &target_context, perm)
+ selinux::check_access(caller_ctx, &target_context, "keystore2_key", perm.to_selinux())
}
#[cfg(test)]
@@ -404,49 +583,49 @@ mod tests {
use keystore2_selinux::*;
const ALL_PERMS: KeyPermSet = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- KeyPerm::Grant,
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- KeyPerm::ConvertStorageKeyToEphemeral,
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::use_dev_id(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ KeyPerm::grant(),
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ KeyPerm::convert_storage_key_to_ephemeral(),
];
const SYSTEM_SERVER_PERMISSIONS_NO_GRANT: KeyPermSet = key_perm_set![
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- // No KeyPerm::Grant
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
+ KeyPerm::delete(),
+ KeyPerm::use_dev_id(),
+ // No KeyPerm::grant()
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
];
const NOT_GRANT_PERMS: KeyPermSet = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- // No KeyPerm::Grant
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- KeyPerm::ConvertStorageKeyToEphemeral,
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::use_dev_id(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ // No KeyPerm::grant()
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ KeyPerm::convert_storage_key_to_ephemeral(),
];
const UNPRIV_PERMS: KeyPermSet = key_perm_set![
- KeyPerm::Delete,
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
+ KeyPerm::delete(),
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
];
/// The su_key namespace as defined in su.te and keystore_key_contexts of the
@@ -493,26 +672,28 @@ mod tests {
#[test]
fn check_keystore_permission_test() -> Result<()> {
let system_server_ctx = Context::new("u:r:system_server:s0")?;
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::AddAuth).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearNs).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::GetState).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Lock).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Reset).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Unlock).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangeUser).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangePassword).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearUID).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::add_auth()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::clear_ns()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::get_state()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::lock()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::reset()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::unlock()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::change_user()).is_ok());
+ assert!(
+ check_keystore_permission(&system_server_ctx, KeystorePerm::change_password()).is_ok()
+ );
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::clear_uid()).is_ok());
let shell_ctx = Context::new("u:r:shell:s0")?;
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::AddAuth));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearNs));
- assert!(check_keystore_permission(&shell_ctx, KeystorePerm::GetState).is_ok());
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::List));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Lock));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Reset));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Unlock));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangeUser));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangePassword));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearUID));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::add_auth()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::clear_ns()));
+ assert!(check_keystore_permission(&shell_ctx, KeystorePerm::get_state()).is_ok());
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::list()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::lock()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::reset()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::unlock()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::change_user()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::change_password()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::clear_uid()));
Ok(())
}
@@ -527,7 +708,7 @@ mod tests {
// attempts to grant the grant permission must always fail even when privileged.
assert_perm_failed!(check_grant_permission(
&system_server_ctx,
- KeyPerm::Grant.into(),
+ KeyPerm::grant().into(),
&key
));
// unprivileged grant attempts always fail. shell does not have the grant permission.
@@ -547,7 +728,7 @@ mod tests {
if is_su {
assert!(check_grant_permission(&sctx, NOT_GRANT_PERMS, &key).is_ok());
// attempts to grant the grant permission must always fail even when privileged.
- assert_perm_failed!(check_grant_permission(&sctx, KeyPerm::Grant.into(), &key));
+ assert_perm_failed!(check_grant_permission(&sctx, KeyPerm::grant().into(), &key));
} else {
// unprivileged grant attempts always fail. shell does not have the grant permission.
assert_perm_failed!(check_grant_permission(&sctx, UNPRIV_PERMS, &key));
@@ -562,7 +743,7 @@ mod tests {
assert_perm_failed!(check_key_permission(
0,
&selinux::Context::new("ignored").unwrap(),
- KeyPerm::Grant,
+ KeyPerm::grant(),
&key,
&Some(UNPRIV_PERMS)
));
@@ -570,7 +751,7 @@ mod tests {
check_key_permission(
0,
&selinux::Context::new("ignored").unwrap(),
- KeyPerm::Use,
+ KeyPerm::use_(),
&key,
&Some(ALL_PERMS),
)
@@ -584,31 +765,61 @@ mod tests {
let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Use, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Delete, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Rebind, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Update, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Grant, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::UseDevId, &key, &None).is_ok());
- assert!(check_key_permission(0, &gmscore_app, KeyPerm::GenUniqueId, &key, &None).is_ok());
-
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::Use, &key, &None).is_ok());
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::Delete, &key, &None).is_ok());
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::Rebind, &key, &None).is_ok());
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::Update, &key, &None).is_ok());
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::Grant, &key, &None));
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ReqForcedOp, &key, &None));
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ManageBlob, &key, &None));
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::UseDevId, &key, &None));
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::GenUniqueId, &key, &None));
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::use_(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::delete(), &key, &None).is_ok());
+ assert!(
+ check_key_permission(0, &system_server_ctx, KeyPerm::get_info(), &key, &None).is_ok()
+ );
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::rebind(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::update(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::grant(), &key, &None).is_ok());
+ assert!(
+ check_key_permission(0, &system_server_ctx, KeyPerm::use_dev_id(), &key, &None).is_ok()
+ );
+ assert!(
+ check_key_permission(0, &gmscore_app, KeyPerm::gen_unique_id(), &key, &None).is_ok()
+ );
+
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::use_(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::delete(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::get_info(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::rebind(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::update(), &key, &None).is_ok());
+ assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::grant(), &key, &None));
+ assert_perm_failed!(check_key_permission(
+ 0,
+ &shell_ctx,
+ KeyPerm::req_forced_op(),
+ &key,
+ &None
+ ));
+ assert_perm_failed!(check_key_permission(
+ 0,
+ &shell_ctx,
+ KeyPerm::manage_blob(),
+ &key,
+ &None
+ ));
+ assert_perm_failed!(check_key_permission(
+ 0,
+ &shell_ctx,
+ KeyPerm::use_dev_id(),
+ &key,
+ &None
+ ));
+ assert_perm_failed!(check_key_permission(
+ 0,
+ &shell_ctx,
+ KeyPerm::gen_unique_id(),
+ &key,
+ &None
+ ));
// Also make sure that the permission fails if the caller is not the owner.
assert_perm_failed!(check_key_permission(
1, // the owner is 0
&system_server_ctx,
- KeyPerm::Use,
+ KeyPerm::use_(),
&key,
&None
));
@@ -616,18 +827,18 @@ mod tests {
assert!(check_key_permission(
1,
&system_server_ctx,
- KeyPerm::Use,
+ KeyPerm::use_(),
&key,
- &Some(key_perm_set![KeyPerm::Use])
+ &Some(key_perm_set![KeyPerm::use_()])
)
.is_ok());
// But fail if the grant did not cover the requested permission.
assert_perm_failed!(check_key_permission(
1,
&system_server_ctx,
- KeyPerm::Use,
+ KeyPerm::use_(),
&key,
- &Some(key_perm_set![KeyPerm::GetInfo])
+ &Some(key_perm_set![KeyPerm::get_info()])
));
Ok(())
@@ -643,24 +854,46 @@ mod tests {
blob: None,
};
- assert!(check_key_permission(0, &sctx, KeyPerm::Use, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::Delete, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::GetInfo, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::Rebind, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::Update, &key, &None).is_ok());
-
if is_su {
- assert!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::delete(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::get_info(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::rebind(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::update(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::grant(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::manage_blob(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::use_dev_id(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::gen_unique_id(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::req_forced_op(), &key, &None).is_ok());
} else {
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None));
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None));
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None));
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None));
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &key, &None));
+ assert!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::delete(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::get_info(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::rebind(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::update(), &key, &None).is_ok());
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::grant(), &key, &None));
+ assert_perm_failed!(check_key_permission(
+ 0,
+ &sctx,
+ KeyPerm::req_forced_op(),
+ &key,
+ &None
+ ));
+ assert_perm_failed!(check_key_permission(
+ 0,
+ &sctx,
+ KeyPerm::manage_blob(),
+ &key,
+ &None
+ ));
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::use_dev_id(), &key, &None));
+ assert_perm_failed!(check_key_permission(
+ 0,
+ &sctx,
+ KeyPerm::gen_unique_id(),
+ &key,
+ &None
+ ));
}
Ok(())
}
@@ -676,9 +909,9 @@ mod tests {
};
if is_su {
- check_key_permission(0, &sctx, KeyPerm::Use, &key, &None)
+ check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None)
} else {
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::Use, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None));
Ok(())
}
}
@@ -692,7 +925,7 @@ mod tests {
check_key_permission(
0,
&selinux::Context::new("ignored").unwrap(),
- KeyPerm::Use,
+ KeyPerm::use_(),
&key,
&None
)
@@ -707,45 +940,45 @@ mod tests {
#[test]
fn key_perm_set_all_test() {
let v = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- KeyPerm::Grant,
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use // Test if the macro accepts missing comma at the end of the list.
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::use_dev_id(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ KeyPerm::grant(),
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_() // Test if the macro accepts missing comma at the end of the list.
];
let mut i = v.into_iter();
- assert_eq!(i.next().unwrap().name(), "delete");
- assert_eq!(i.next().unwrap().name(), "gen_unique_id");
- assert_eq!(i.next().unwrap().name(), "get_info");
- assert_eq!(i.next().unwrap().name(), "grant");
- assert_eq!(i.next().unwrap().name(), "manage_blob");
- assert_eq!(i.next().unwrap().name(), "rebind");
- assert_eq!(i.next().unwrap().name(), "req_forced_op");
- assert_eq!(i.next().unwrap().name(), "update");
- assert_eq!(i.next().unwrap().name(), "use");
- assert_eq!(i.next().unwrap().name(), "use_dev_id");
+ assert_eq!(i.next().unwrap().to_selinux(), "delete");
+ assert_eq!(i.next().unwrap().to_selinux(), "gen_unique_id");
+ assert_eq!(i.next().unwrap().to_selinux(), "get_info");
+ assert_eq!(i.next().unwrap().to_selinux(), "grant");
+ assert_eq!(i.next().unwrap().to_selinux(), "manage_blob");
+ assert_eq!(i.next().unwrap().to_selinux(), "rebind");
+ assert_eq!(i.next().unwrap().to_selinux(), "req_forced_op");
+ assert_eq!(i.next().unwrap().to_selinux(), "update");
+ assert_eq!(i.next().unwrap().to_selinux(), "use");
+ assert_eq!(i.next().unwrap().to_selinux(), "use_dev_id");
assert_eq!(None, i.next());
}
#[test]
fn key_perm_set_sparse_test() {
let v = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- KeyPerm::Update,
- KeyPerm::Use, // Test if macro accepts the comma at the end of the list.
+ KeyPerm::manage_blob(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ KeyPerm::update(),
+ KeyPerm::use_(), // Test if macro accepts the comma at the end of the list.
];
let mut i = v.into_iter();
- assert_eq!(i.next().unwrap().name(), "gen_unique_id");
- assert_eq!(i.next().unwrap().name(), "manage_blob");
- assert_eq!(i.next().unwrap().name(), "req_forced_op");
- assert_eq!(i.next().unwrap().name(), "update");
- assert_eq!(i.next().unwrap().name(), "use");
+ assert_eq!(i.next().unwrap().to_selinux(), "gen_unique_id");
+ assert_eq!(i.next().unwrap().to_selinux(), "manage_blob");
+ assert_eq!(i.next().unwrap().to_selinux(), "req_forced_op");
+ assert_eq!(i.next().unwrap().to_selinux(), "update");
+ assert_eq!(i.next().unwrap().to_selinux(), "use");
assert_eq!(None, i.next());
}
#[test]
@@ -757,23 +990,23 @@ mod tests {
#[test]
fn key_perm_set_include_subset_test() {
let v1 = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- KeyPerm::Grant,
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::use_dev_id(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ KeyPerm::grant(),
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
];
let v2 = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
];
assert!(v1.includes(v2));
assert!(!v2.includes(v1));
@@ -781,18 +1014,18 @@ mod tests {
#[test]
fn key_perm_set_include_equal_test() {
let v1 = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
];
let v2 = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
];
assert!(v1.includes(v2));
assert!(v2.includes(v1));
@@ -800,29 +1033,33 @@ mod tests {
#[test]
fn key_perm_set_include_overlap_test() {
let v1 = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::Grant, // only in v1
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::grant(), // only in v1
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
];
let v2 = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::ReqForcedOp, // only in v2
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::req_forced_op(), // only in v2
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
];
assert!(!v1.includes(v2));
assert!(!v2.includes(v1));
}
#[test]
fn key_perm_set_include_no_overlap_test() {
- let v1 = key_perm_set![KeyPerm::ManageBlob, KeyPerm::Delete, KeyPerm::Grant,];
- let v2 =
- key_perm_set![KeyPerm::ReqForcedOp, KeyPerm::Rebind, KeyPerm::Update, KeyPerm::Use,];
+ let v1 = key_perm_set![KeyPerm::manage_blob(), KeyPerm::delete(), KeyPerm::grant(),];
+ let v2 = key_perm_set![
+ KeyPerm::req_forced_op(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
assert!(!v1.includes(v2));
assert!(!v2.includes(v1));
}
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
index 4ce9dceb..cd549151 100644
--- a/keystore2/src/raw_device.rs
+++ b/keystore2/src/raw_device.rs
@@ -16,9 +16,8 @@
use crate::{
database::{
- BlobInfo, BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, KeyEntry,
- KeyEntryLoadBits, KeyIdGuard, KeyMetaData, KeyMetaEntry, KeyType, KeystoreDB,
- SubComponentType, Uuid,
+ BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, KeyEntry, KeyEntryLoadBits,
+ KeyIdGuard, KeyMetaData, KeyMetaEntry, KeyType, KeystoreDB, SubComponentType, Uuid,
},
error::{map_km_error, Error, ErrorCode},
globals::get_keymint_device,
@@ -60,16 +59,14 @@ impl KeyMintDevice {
pub const KEY_MASTER_V4_1: i32 = 41;
/// Version number of KeyMintDevice@V1
pub const KEY_MINT_V1: i32 = 100;
- /// Version number of KeyMintDevice@V2
- pub const KEY_MINT_V2: i32 = 200;
/// Get a [`KeyMintDevice`] for the given [`SecurityLevel`]
pub fn get(security_level: SecurityLevel) -> Result<KeyMintDevice> {
- let (km_dev, hw_info, km_uuid) = get_keymint_device(&security_level)
+ let (asp, hw_info, km_uuid) = get_keymint_device(&security_level)
.context("In KeyMintDevice::get: get_keymint_device failed")?;
Ok(KeyMintDevice {
- km_dev,
+ km_dev: asp.get_interface()?,
km_uuid,
version: hw_info.versionNumber,
security_level: hw_info.securityLevel,
@@ -123,10 +120,10 @@ impl KeyMintDevice {
blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
db.store_new_key(
- key_desc,
+ &key_desc,
key_type,
&key_parameters,
- &BlobInfo::new(&creation_result.keyBlob, &blob_metadata),
+ &(&creation_result.keyBlob, &blob_metadata),
&CertificateInfo::new(None, None),
&key_metadata,
&self.km_uuid,
@@ -151,7 +148,7 @@ impl KeyMintDevice {
key_desc: &KeyDescriptor,
key_type: KeyType,
) -> Result<(KeyIdGuard, KeyEntry)> {
- db.load_key_entry(key_desc, key_type, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| Ok(()))
+ db.load_key_entry(&key_desc, key_type, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| Ok(()))
.context("In lookup_from_desc: load_key_entry failed.")
}
@@ -231,8 +228,8 @@ impl KeyMintDevice {
};
}
- self.create_and_store_key(db, key_desc, key_type, |km_dev| {
- km_dev.generateKey(params, None)
+ self.create_and_store_key(db, &key_desc, key_type, |km_dev| {
+ km_dev.generateKey(&params, None)
})
.context("In lookup_or_generate_key: generate_and_store_key failed")?;
Self::lookup_from_desc(db, key_desc, key_type)
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index ea2698f0..212bf399 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -30,27 +30,21 @@ use android_hardware_security_keymint::aidl::android::hardware::security::keymin
};
use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
AttestationPoolStatus::AttestationPoolStatus, IRemoteProvisioning::BnRemoteProvisioning,
- IRemoteProvisioning::IRemoteProvisioning,
- IRemotelyProvisionedKeyPool::BnRemotelyProvisionedKeyPool,
- IRemotelyProvisionedKeyPool::IRemotelyProvisionedKeyPool, ImplInfo::ImplInfo,
- RemotelyProvisionedKey::RemotelyProvisionedKey,
+ IRemoteProvisioning::IRemoteProvisioning, ImplInfo::ImplInfo,
};
use android_security_remoteprovisioning::binder::{BinderFeatures, Strong};
use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+ Domain::Domain, KeyDescriptor::KeyDescriptor,
};
use anyhow::{Context, Result};
use keystore2_crypto::parse_subject_from_certificate;
-use serde_cbor::Value;
-use std::collections::BTreeMap;
use std::sync::atomic::{AtomicBool, Ordering};
-use crate::database::{CertificateChain, KeyIdGuard, KeystoreDB, Uuid};
+use crate::database::{CertificateChain, KeystoreDB, Uuid};
use crate::error::{self, map_or_log_err, map_rem_prov_error, Error};
use crate::globals::{get_keymint_device, get_remotely_provisioned_component, DB};
use crate::metrics_store::log_rkp_error_stats;
-use crate::permission::KeystorePerm;
-use crate::utils::{check_keystore_permission, watchdog as wd};
+use crate::utils::{watchdog as wd, Asp};
use android_security_metrics::aidl::android::security::metrics::RkpError::RkpError as MetricsRkpError;
/// Contains helper functions to check if remote provisioning is enabled on the system and, if so,
@@ -62,43 +56,17 @@ pub struct RemProvState {
is_hal_present: AtomicBool,
}
-static COSE_KEY_XCOORD: Value = Value::Integer(-2);
-static COSE_KEY_YCOORD: Value = Value::Integer(-3);
-static COSE_MAC0_LEN: usize = 4;
-static COSE_MAC0_PAYLOAD: usize = 2;
-
impl RemProvState {
/// Creates a RemProvState struct.
pub fn new(security_level: SecurityLevel, km_uuid: Uuid) -> Self {
Self { security_level, km_uuid, is_hal_present: AtomicBool::new(true) }
}
- /// Returns the uuid for the KM instance attached to this RemProvState struct.
- pub fn get_uuid(&self) -> Uuid {
- self.km_uuid
- }
-
- fn is_rkp_only(&self) -> bool {
- let default_value = false;
-
- let property_name = match self.security_level {
- SecurityLevel::STRONGBOX => "remote_provisioning.strongbox.rkp_only",
- SecurityLevel::TRUSTED_ENVIRONMENT => "remote_provisioning.tee.rkp_only",
- _ => return default_value,
- };
-
- rustutils::system_properties::read_bool(property_name, default_value)
- .unwrap_or(default_value)
- }
-
/// Checks if remote provisioning is enabled and partially caches the result. On a hybrid system
/// remote provisioning can flip from being disabled to enabled depending on responses from the
/// server, so unfortunately caching the presence or absence of the HAL is not enough to fully
/// make decisions about the state of remote provisioning during runtime.
fn check_rem_prov_enabled(&self, db: &mut KeystoreDB) -> Result<bool> {
- if self.is_rkp_only() {
- return Ok(true);
- }
if !self.is_hal_present.load(Ordering::Relaxed)
|| get_remotely_provisioned_component(&self.security_level).is_err()
{
@@ -115,6 +83,70 @@ impl RemProvState {
Ok(pool_status.total != 0)
}
+ /// 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`
+ fn get_rem_prov_attest_key(
+ &self,
+ key: &KeyDescriptor,
+ caller_uid: u32,
+ db: &mut KeystoreDB,
+ ) -> Result<Option<CertificateChain>> {
+ match key.domain {
+ Domain::APP => {
+ // Attempt to get an Attestation Key once. If it fails, then the app doesn't
+ // have a valid chain assigned to it. The helper function will return None after
+ // attempting to assign a key. An error will be thrown if the pool is simply out
+ // of usable keys. Then another attempt to fetch the just-assigned key will be
+ // made. If this fails too, something is very wrong.
+ self.get_rem_prov_attest_key_helper(key, caller_uid, db)
+ .context("In get_rem_prov_attest_key: Failed to get a key")?
+ .map_or_else(
+ || self.get_rem_prov_attest_key_helper(key, caller_uid, db),
+ |v| Ok(Some(v)),
+ )
+ .context(concat!(
+ "In get_rem_prov_attest_key: Failed to get a key after",
+ "attempting to assign one."
+ ))?
+ .map_or_else(
+ || {
+ Err(Error::sys()).context(concat!(
+ "In get_rem_prov_attest_key: Attempted to assign a ",
+ "key and failed silently. Something is very wrong."
+ ))
+ },
+ |cert_chain| Ok(Some(cert_chain)),
+ )
+ }
+ _ => Ok(None),
+ }
+ }
+
+ /// Returns None if an AttestationKey fails to be assigned. Errors if no keys are available.
+ fn get_rem_prov_attest_key_helper(
+ &self,
+ key: &KeyDescriptor,
+ caller_uid: u32,
+ db: &mut KeystoreDB,
+ ) -> Result<Option<CertificateChain>> {
+ let cert_chain = db
+ .retrieve_attestation_key_and_cert_chain(key.domain, caller_uid as i64, &self.km_uuid)
+ .context("In get_rem_prov_attest_key_helper: Failed to retrieve a key + cert chain")?;
+ match cert_chain {
+ Some(cert_chain) => Ok(Some(cert_chain)),
+ // Either this app needs to be assigned a key, or the pool is empty. An error will
+ // be thrown if there is no key available to assign. This will indicate that the app
+ // should be nudged to provision more keys so keystore can retry.
+ None => {
+ db.assign_attestation_key(key.domain, caller_uid as i64, &self.km_uuid)
+ .context("In get_rem_prov_attest_key_helper: Failed to assign a key")?;
+ Ok(None)
+ }
+ }
+ }
+
fn is_asymmetric_key(&self, params: &[KeyParameter]) -> bool {
params.iter().any(|kp| {
matches!(
@@ -142,7 +174,7 @@ impl RemProvState {
caller_uid: u32,
params: &[KeyParameter],
db: &mut KeystoreDB,
- ) -> Result<Option<(KeyIdGuard, AttestationKey, Certificate)>> {
+ ) -> Result<Option<(AttestationKey, Certificate)>> {
if !self.is_asymmetric_key(params) || !self.check_rem_prov_enabled(db)? {
// There is no remote provisioning component for this security level on the
// device. Return None so the underlying KM instance knows to use its
@@ -150,22 +182,20 @@ impl RemProvState {
// and therefore will not be attested.
Ok(None)
} else {
- match get_rem_prov_attest_key(key.domain, caller_uid, db, &self.km_uuid) {
+ match self.get_rem_prov_attest_key(&key, caller_uid, db) {
Err(e) => {
log::error!(
- "In get_remote_provisioning_key_and_certs: Error occurred: {:?}",
+ concat!(
+ "In get_remote_provisioning_key_and_certs: Failed to get ",
+ "attestation key. {:?}"
+ ),
e
);
- if self.is_rkp_only() {
- return Err(e);
- }
- log_rkp_error_stats(MetricsRkpError::FALL_BACK_DURING_HYBRID,
- &self.security_level);
+ log_rkp_error_stats(MetricsRkpError::FALL_BACK_DURING_HYBRID);
Ok(None)
}
Ok(v) => match v {
- Some((guard, cert_chain)) => Ok(Some((
- guard,
+ Some(cert_chain) => Ok(Some((
AttestationKey {
keyBlob: cert_chain.private_key.to_vec(),
attestKeyParams: vec![],
@@ -188,7 +218,7 @@ impl RemProvState {
/// Implementation of the IRemoteProvisioning service.
#[derive(Default)]
pub struct RemoteProvisioningService {
- device_by_sec_level: HashMap<SecurityLevel, Strong<dyn IRemotelyProvisionedComponent>>,
+ device_by_sec_level: HashMap<SecurityLevel, Asp>,
curve_by_sec_level: HashMap<SecurityLevel, i32>,
}
@@ -196,9 +226,9 @@ impl RemoteProvisioningService {
fn get_dev_by_sec_level(
&self,
sec_level: &SecurityLevel,
- ) -> Result<&dyn IRemotelyProvisionedComponent> {
+ ) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
if let Some(dev) = self.device_by_sec_level.get(sec_level) {
- Ok(dev.as_ref())
+ dev.get_interface().context("In get_dev_by_sec_level.")
} else {
Err(error::Error::sys()).context(concat!(
"In get_dev_by_sec_level: Remote instance for requested security level",
@@ -212,17 +242,21 @@ impl RemoteProvisioningService {
let mut result: Self = Default::default();
let dev = get_remotely_provisioned_component(&SecurityLevel::TRUSTED_ENVIRONMENT)
.context("In new_native_binder: Failed to get TEE Remote Provisioner instance.")?;
+ let rkp_tee_dev: Strong<dyn IRemotelyProvisionedComponent> = dev.get_interface()?;
result.curve_by_sec_level.insert(
SecurityLevel::TRUSTED_ENVIRONMENT,
- dev.getHardwareInfo()
+ rkp_tee_dev
+ .getHardwareInfo()
.context("In new_native_binder: Failed to get hardware info for the TEE.")?
.supportedEekCurve,
);
result.device_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, dev);
if let Ok(dev) = get_remotely_provisioned_component(&SecurityLevel::STRONGBOX) {
+ let rkp_sb_dev: Strong<dyn IRemotelyProvisionedComponent> = dev.get_interface()?;
result.curve_by_sec_level.insert(
SecurityLevel::STRONGBOX,
- dev.getHardwareInfo()
+ rkp_sb_dev
+ .getHardwareInfo()
.context("In new_native_binder: Failed to get hardware info for StrongBox.")?
.supportedEekCurve,
);
@@ -231,27 +265,6 @@ impl RemoteProvisioningService {
Ok(BnRemoteProvisioning::new_binder(result, BinderFeatures::default()))
}
- fn extract_payload_from_cose_mac(data: &[u8]) -> Result<Value> {
- let cose_mac0: Vec<Value> = serde_cbor::from_slice(data).context(
- "In extract_payload_from_cose_mac: COSE_Mac0 returned from IRPC cannot be parsed",
- )?;
- if cose_mac0.len() != COSE_MAC0_LEN {
- return Err(error::Error::sys()).context(format!(
- "In extract_payload_from_cose_mac: COSE_Mac0 has improper length. \
- Expected: {}, Actual: {}",
- COSE_MAC0_LEN,
- cose_mac0.len(),
- ));
- }
- match &cose_mac0[COSE_MAC0_PAYLOAD] {
- Value::Bytes(key) => Ok(serde_cbor::from_slice(key)
- .context("In extract_payload_from_cose_mac: COSE_Mac0 payload is malformed.")?),
- _ => Err(error::Error::sys()).context(
- "In extract_payload_from_cose_mac: COSE_Mac0 payload is the wrong type.",
- )?,
- }
- }
-
/// Generates a CBOR blob which will be assembled by the calling code into a larger
/// CBOR blob intended for delivery to a provisioning serever. This blob will contain
/// `num_csr` certificate signing requests for attestation keys generated in the TEE,
@@ -281,7 +294,7 @@ impl RemoteProvisioningService {
.map(|key| MacedPublicKey { macedKey: key.to_vec() })
.collect())
})?;
- let mac = map_rem_prov_error(dev.generateCertificateRequest(
+ let mut mac = map_rem_prov_error(dev.generateCertificateRequest(
test_mode,
&keys_to_sign,
eek,
@@ -290,16 +303,30 @@ impl RemoteProvisioningService {
protected_data,
))
.context("In generate_csr: Failed to generate csr")?;
- let mut mac_and_keys: Vec<Value> = vec![Value::from(mac)];
+ // TODO(b/180392379): Replace this manual CBOR generation with the cbor-serde crate as well.
+ // This generates an array consisting of the mac and the public key Maps.
+ // Just generate the actual MacedPublicKeys structure when the crate is
+ // available.
+ let mut cose_mac_0: Vec<u8> = vec![
+ (0b100_00000 | (keys_to_sign.len() + 1)) as u8,
+ 0b010_11000, // mac
+ (mac.len() as u8),
+ ];
+ cose_mac_0.append(&mut mac);
+ // If this is a test mode key, there is an extra 6 bytes added as an additional entry in
+ // the COSE_Key struct to denote that.
+ let test_mode_entry_shift = if test_mode { 0 } else { 6 };
+ let byte_dist_mac0_payload = 8;
+ let cose_key_size = 83 - test_mode_entry_shift;
for maced_public_key in keys_to_sign {
- mac_and_keys.push(
- Self::extract_payload_from_cose_mac(&maced_public_key.macedKey)
- .context("In generate_csr: Failed to get the payload from the COSE_Mac0")?,
- )
+ if maced_public_key.macedKey.len() > cose_key_size + byte_dist_mac0_payload {
+ cose_mac_0.extend_from_slice(
+ &maced_public_key.macedKey
+ [byte_dist_mac0_payload..cose_key_size + byte_dist_mac0_payload],
+ );
+ }
}
- let cbor_array: Value = Value::Array(mac_and_keys);
- serde_cbor::to_vec(&cbor_array)
- .context("In generate_csr: Failed to serialize the mac and keys array")
+ Ok(cose_mac_0)
}
/// Provisions a certificate chain for a key whose CSR was included in generate_csr. The
@@ -309,106 +336,52 @@ impl RemoteProvisioningService {
/// here.
pub fn provision_cert_chain(
&self,
- db: &mut KeystoreDB,
public_key: &[u8],
batch_cert: &[u8],
certs: &[u8],
expiration_date: i64,
sec_level: SecurityLevel,
) -> Result<()> {
- let (_, _, uuid) = get_keymint_device(&sec_level)?;
- db.store_signed_attestation_certificate_chain(
- public_key,
- batch_cert,
- certs, /* DER encoded certificate chain */
- expiration_date,
- &uuid,
- )
- }
-
- fn parse_cose_mac0_for_coords(data: &[u8]) -> Result<Vec<u8>> {
- let cose_mac0: Vec<Value> = serde_cbor::from_slice(data).context(
- "In parse_cose_mac0_for_coords: COSE_Mac0 returned from IRPC cannot be parsed",
- )?;
- if cose_mac0.len() != COSE_MAC0_LEN {
- return Err(error::Error::sys()).context(format!(
- "In parse_cose_mac0_for_coords: COSE_Mac0 has improper length. \
- Expected: {}, Actual: {}",
- COSE_MAC0_LEN,
- cose_mac0.len(),
- ));
- }
- let cose_key: BTreeMap<Value, Value> = match &cose_mac0[COSE_MAC0_PAYLOAD] {
- Value::Bytes(key) => serde_cbor::from_slice(key)
- .context("In parse_cose_mac0_for_coords: COSE_Key is malformed.")?,
- _ => Err(error::Error::sys())
- .context("In parse_cose_mac0_for_coords: COSE_Mac0 payload is the wrong type.")?,
- };
- if !cose_key.contains_key(&COSE_KEY_XCOORD) || !cose_key.contains_key(&COSE_KEY_YCOORD) {
- return Err(error::Error::sys()).context(
- "In parse_cose_mac0_for_coords: \
- COSE_Key returned from IRPC is lacking required fields",
- );
- }
- let mut raw_key: Vec<u8> = vec![0; 64];
- match &cose_key[&COSE_KEY_XCOORD] {
- Value::Bytes(x_coord) if x_coord.len() == 32 => {
- raw_key[0..32].clone_from_slice(x_coord)
- }
- Value::Bytes(x_coord) => {
- return Err(error::Error::sys()).context(format!(
- "In parse_cose_mac0_for_coords: COSE_Key X-coordinate is not the right length. \
- Expected: 32; Actual: {}",
- x_coord.len()
- ))
- }
- _ => {
- return Err(error::Error::sys())
- .context("In parse_cose_mac0_for_coords: COSE_Key X-coordinate is not a bstr")
- }
- }
- match &cose_key[&COSE_KEY_YCOORD] {
- Value::Bytes(y_coord) if y_coord.len() == 32 => {
- raw_key[32..64].clone_from_slice(y_coord)
- }
- Value::Bytes(y_coord) => {
- return Err(error::Error::sys()).context(format!(
- "In parse_cose_mac0_for_coords: COSE_Key Y-coordinate is not the right length. \
- Expected: 32; Actual: {}",
- y_coord.len()
- ))
- }
- _ => {
- return Err(error::Error::sys())
- .context("In parse_cose_mac0_for_coords: COSE_Key Y-coordinate is not a bstr")
- }
- }
- Ok(raw_key)
+ DB.with::<_, Result<()>>(|db| {
+ let mut db = db.borrow_mut();
+ let (_, _, uuid) = get_keymint_device(&sec_level)?;
+ db.store_signed_attestation_certificate_chain(
+ public_key,
+ batch_cert,
+ certs, /* DER encoded certificate chain */
+ expiration_date,
+ &uuid,
+ )
+ })
}
/// Submits a request to the Remote Provisioner HAL to generate a signing key pair.
/// `is_test_mode` indicates whether or not the returned public key should be marked as being
/// for testing in order to differentiate them from private keys. If the call is successful,
/// the key pair is then added to the database.
- pub fn generate_key_pair(
- &self,
- db: &mut KeystoreDB,
- is_test_mode: bool,
- sec_level: SecurityLevel,
- ) -> Result<()> {
+ pub fn generate_key_pair(&self, is_test_mode: bool, sec_level: SecurityLevel) -> Result<()> {
let (_, _, uuid) = get_keymint_device(&sec_level)?;
- let dev = self.get_dev_by_sec_level(&sec_level).context(format!(
- "In generate_key_pair: Failed to get device for security level {:?}",
- sec_level
- ))?;
+ let dev = self.get_dev_by_sec_level(&sec_level)?;
let mut maced_key = MacedPublicKey { macedKey: Vec::new() };
let priv_key =
map_rem_prov_error(dev.generateEcdsaP256KeyPair(is_test_mode, &mut maced_key))
.context("In generate_key_pair: Failed to generated ECDSA keypair.")?;
- let raw_key = Self::parse_cose_mac0_for_coords(&maced_key.macedKey)
- .context("In generate_key_pair: Failed to parse raw key")?;
- db.create_attestation_key_entry(&maced_key.macedKey, &raw_key, &priv_key, &uuid)
- .context("In generate_key_pair: Failed to insert attestation key entry")
+ // TODO(b/180392379): This is a brittle hack that relies on the consistent formatting of
+ // the returned CBOR blob in order to extract the public key.
+ let data = &maced_key.macedKey;
+ if data.len() < 85 {
+ return Err(error::Error::sys()).context(concat!(
+ "In generate_key_pair: CBOR blob returned from",
+ "RemotelyProvisionedComponent is definitely malformatted or empty."
+ ));
+ }
+ let mut raw_key: Vec<u8> = vec![0; 64];
+ raw_key[0..32].clone_from_slice(&data[18..18 + 32]);
+ raw_key[32..64].clone_from_slice(&data[53..53 + 32]);
+ DB.with::<_, Result<()>>(|db| {
+ let mut db = db.borrow_mut();
+ db.create_attestation_key_entry(&maced_key.macedKey, &raw_key, &priv_key, &uuid)
+ })
}
/// Checks the security level of each available IRemotelyProvisionedComponent hal and returns
@@ -447,70 +420,6 @@ pub fn get_pool_status(expired_by: i64, sec_level: SecurityLevel) -> Result<Atte
})
}
-/// 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`
-fn get_rem_prov_attest_key(
- domain: Domain,
- caller_uid: u32,
- db: &mut KeystoreDB,
- km_uuid: &Uuid,
-) -> Result<Option<(KeyIdGuard, CertificateChain)>> {
- match domain {
- Domain::APP => {
- // Attempt to get an Attestation Key once. If it fails, then the app doesn't
- // have a valid chain assigned to it. The helper function will return None after
- // attempting to assign a key. An error will be thrown if the pool is simply out
- // of usable keys. Then another attempt to fetch the just-assigned key will be
- // made. If this fails too, something is very wrong.
- get_rem_prov_attest_key_helper(domain, caller_uid, db, km_uuid)
- .context("In get_rem_prov_attest_key: Failed to get a key")?
- .map_or_else(
- || get_rem_prov_attest_key_helper(domain, caller_uid, db, km_uuid),
- |v| Ok(Some(v)),
- )
- .context(concat!(
- "In get_rem_prov_attest_key: Failed to get a key after",
- "attempting to assign one."
- ))?
- .map_or_else(
- || {
- Err(Error::sys()).context(concat!(
- "In get_rem_prov_attest_key: Attempted to assign a ",
- "key and failed silently. Something is very wrong."
- ))
- },
- |(guard, cert_chain)| Ok(Some((guard, cert_chain))),
- )
- }
- _ => Ok(None),
- }
-}
-
-/// Returns None if an AttestationKey fails to be assigned. Errors if no keys are available.
-fn get_rem_prov_attest_key_helper(
- domain: Domain,
- caller_uid: u32,
- db: &mut KeystoreDB,
- km_uuid: &Uuid,
-) -> Result<Option<(KeyIdGuard, CertificateChain)>> {
- let guard_and_chain = db
- .retrieve_attestation_key_and_cert_chain(domain, caller_uid as i64, km_uuid)
- .context("In get_rem_prov_attest_key_helper: Failed to retrieve a key + cert chain")?;
- match guard_and_chain {
- Some((guard, cert_chain)) => Ok(Some((guard, cert_chain))),
- // Either this app needs to be assigned a key, or the pool is empty. An error will
- // be thrown if there is no key available to assign. This will indicate that the app
- // should be nudged to provision more keys so keystore can retry.
- None => {
- db.assign_attestation_key(domain, caller_uid as i64, km_uuid)
- .context("In get_rem_prov_attest_key_helper: Failed to assign a key")?;
- Ok(None)
- }
- }
-}
-
impl binder::Interface for RemoteProvisioningService {}
// Implementation of IRemoteProvisioning. See AIDL spec at
@@ -520,7 +429,7 @@ impl IRemoteProvisioning for RemoteProvisioningService {
&self,
expired_by: i64,
sec_level: SecurityLevel,
- ) -> binder::Result<AttestationPoolStatus> {
+ ) -> binder::public_api::Result<AttestationPoolStatus> {
let _wp = wd::watch_millis("IRemoteProvisioning::getPoolStatus", 500);
map_or_log_err(get_pool_status(expired_by, sec_level), Ok)
}
@@ -534,7 +443,7 @@ impl IRemoteProvisioning for RemoteProvisioningService {
sec_level: SecurityLevel,
protected_data: &mut ProtectedData,
device_info: &mut DeviceInfo,
- ) -> binder::Result<Vec<u8>> {
+ ) -> binder::public_api::Result<Vec<u8>> {
let _wp = wd::watch_millis("IRemoteProvisioning::generateCsr", 500);
map_or_log_err(
self.generate_csr(
@@ -557,516 +466,30 @@ impl IRemoteProvisioning for RemoteProvisioningService {
certs: &[u8],
expiration_date: i64,
sec_level: SecurityLevel,
- ) -> binder::Result<()> {
+ ) -> binder::public_api::Result<()> {
let _wp = wd::watch_millis("IRemoteProvisioning::provisionCertChain", 500);
- DB.with::<_, binder::Result<()>>(|db| {
- map_or_log_err(
- self.provision_cert_chain(
- &mut db.borrow_mut(),
- public_key,
- batch_cert,
- certs,
- expiration_date,
- sec_level,
- ),
- Ok,
- )
- })
+ map_or_log_err(
+ self.provision_cert_chain(public_key, batch_cert, certs, expiration_date, sec_level),
+ Ok,
+ )
}
- fn generateKeyPair(&self, is_test_mode: bool, sec_level: SecurityLevel) -> binder::Result<()> {
+ fn generateKeyPair(
+ &self,
+ is_test_mode: bool,
+ sec_level: SecurityLevel,
+ ) -> binder::public_api::Result<()> {
let _wp = wd::watch_millis("IRemoteProvisioning::generateKeyPair", 500);
- DB.with::<_, binder::Result<()>>(|db| {
- map_or_log_err(
- self.generate_key_pair(&mut db.borrow_mut(), is_test_mode, sec_level),
- Ok,
- )
- })
+ map_or_log_err(self.generate_key_pair(is_test_mode, sec_level), Ok)
}
- fn getImplementationInfo(&self) -> binder::Result<Vec<ImplInfo>> {
+ fn getImplementationInfo(&self) -> binder::public_api::Result<Vec<ImplInfo>> {
let _wp = wd::watch_millis("IRemoteProvisioning::getSecurityLevels", 500);
map_or_log_err(self.get_implementation_info(), Ok)
}
- fn deleteAllKeys(&self) -> binder::Result<i64> {
+ fn deleteAllKeys(&self) -> binder::public_api::Result<i64> {
let _wp = wd::watch_millis("IRemoteProvisioning::deleteAllKeys", 500);
map_or_log_err(self.delete_all_keys(), Ok)
}
}
-
-/// Implementation of the IRemotelyProvisionedKeyPool service.
-#[derive(Default)]
-pub struct RemotelyProvisionedKeyPoolService {
- unique_id_to_sec_level: HashMap<String, SecurityLevel>,
-}
-
-impl RemotelyProvisionedKeyPoolService {
- /// 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.
- fn get_attestation_key(
- &self,
- db: &mut KeystoreDB,
- caller_uid: i32,
- irpc_id: &str,
- ) -> Result<RemotelyProvisionedKey> {
- log::info!("get_attestation_key(self, {}, {}", caller_uid, irpc_id);
-
- let sec_level = self
- .unique_id_to_sec_level
- .get(irpc_id)
- .ok_or(Error::Rc(ResponseCode::INVALID_ARGUMENT))
- .context(format!("In get_attestation_key: unknown irpc id '{}'", irpc_id))?;
- let (_, _, km_uuid) = get_keymint_device(sec_level)?;
-
- let guard_and_cert_chain =
- get_rem_prov_attest_key(Domain::APP, caller_uid as u32, db, &km_uuid)
- .context("In get_attestation_key")?;
- match guard_and_cert_chain {
- Some((_, chain)) => Ok(RemotelyProvisionedKey {
- keyBlob: chain.private_key.to_vec(),
- encodedCertChain: chain.cert_chain,
- }),
- // 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))
- .context("In get_attestation_key: No available attestation keys"),
- }
- }
-
- /// Creates a new instance of the remotely provisioned key pool service, used for fetching
- /// remotely provisioned attestation keys.
- pub fn new_native_binder() -> Result<Strong<dyn IRemotelyProvisionedKeyPool>> {
- let mut result: Self = Default::default();
-
- let dev = get_remotely_provisioned_component(&SecurityLevel::TRUSTED_ENVIRONMENT)
- .context("In new_native_binder: Failed to get TEE Remote Provisioner instance.")?;
- if let Some(id) = dev.getHardwareInfo()?.uniqueId {
- result.unique_id_to_sec_level.insert(id, SecurityLevel::TRUSTED_ENVIRONMENT);
- }
-
- if let Ok(dev) = get_remotely_provisioned_component(&SecurityLevel::STRONGBOX) {
- if let Some(id) = dev.getHardwareInfo()?.uniqueId {
- if result.unique_id_to_sec_level.contains_key(&id) {
- anyhow::bail!("In new_native_binder: duplicate irpc id found: '{}'", id)
- }
- result.unique_id_to_sec_level.insert(id, SecurityLevel::STRONGBOX);
- }
- }
-
- // If none of the remotely provisioned components have unique ids, then we shouldn't
- // bother publishing the service, as it's impossible to match keys with their backends.
- if result.unique_id_to_sec_level.is_empty() {
- anyhow::bail!(
- "In new_native_binder: No remotely provisioned components have unique ids"
- )
- }
-
- Ok(BnRemotelyProvisionedKeyPool::new_binder(
- result,
- BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
- ))
- }
-}
-
-impl binder::Interface for RemotelyProvisionedKeyPoolService {}
-
-// Implementation of IRemotelyProvisionedKeyPool. See AIDL spec at
-// :aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl
-impl IRemotelyProvisionedKeyPool for RemotelyProvisionedKeyPoolService {
- fn getAttestationKey(
- &self,
- caller_uid: i32,
- irpc_id: &str,
- ) -> binder::Result<RemotelyProvisionedKey> {
- let _wp = wd::watch_millis("IRemotelyProvisionedKeyPool::getAttestationKey", 500);
- map_or_log_err(check_keystore_permission(KeystorePerm::GetAttestationKey), Ok)?;
- DB.with::<_, binder::Result<RemotelyProvisionedKey>>(|db| {
- map_or_log_err(self.get_attestation_key(&mut db.borrow_mut(), caller_uid, irpc_id), Ok)
- })
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use serde_cbor::Value;
- use std::collections::BTreeMap;
- use std::sync::{Arc, Mutex};
- use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- RpcHardwareInfo::RpcHardwareInfo,
- };
-
- #[derive(Default)]
- struct MockRemotelyProvisionedComponentValues {
- hw_info: RpcHardwareInfo,
- private_key: Vec<u8>,
- maced_public_key: Vec<u8>,
- }
-
- // binder::Interface requires the Send trait, so we have to use a Mutex even though the test
- // is single threaded.
- #[derive(Default)]
- struct MockRemotelyProvisionedComponent(Arc<Mutex<MockRemotelyProvisionedComponentValues>>);
-
- impl binder::Interface for MockRemotelyProvisionedComponent {}
-
- impl IRemotelyProvisionedComponent for MockRemotelyProvisionedComponent {
- fn getHardwareInfo(&self) -> binder::Result<RpcHardwareInfo> {
- Ok(self.0.lock().unwrap().hw_info.clone())
- }
-
- fn generateEcdsaP256KeyPair(
- &self,
- test_mode: bool,
- maced_public_key: &mut MacedPublicKey,
- ) -> binder::Result<Vec<u8>> {
- assert!(test_mode);
- maced_public_key.macedKey = self.0.lock().unwrap().maced_public_key.clone();
- Ok(self.0.lock().unwrap().private_key.clone())
- }
-
- fn generateCertificateRequest(
- &self,
- _test_mode: bool,
- _keys_to_sign: &[MacedPublicKey],
- _eek: &[u8],
- _challenge: &[u8],
- _device_info: &mut DeviceInfo,
- _protected_data: &mut ProtectedData,
- ) -> binder::Result<Vec<u8>> {
- Err(binder::StatusCode::INVALID_OPERATION.into())
- }
- }
-
- // Hard coded cert that can be parsed -- the content doesn't matter for testing, only that it's valid.
- fn get_fake_cert() -> Vec<u8> {
- vec![
- 0x30, 0x82, 0x01, 0xbb, 0x30, 0x82, 0x01, 0x61, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02,
- 0x14, 0x3a, 0xd5, 0x67, 0xce, 0xfe, 0x93, 0xe1, 0xea, 0xb7, 0xe4, 0xbf, 0x64, 0x19,
- 0xa4, 0x11, 0xe1, 0x87, 0x40, 0x20, 0x37, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48,
- 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x33, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
- 0x04, 0x06, 0x13, 0x02, 0x55, 0x54, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
- 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
- 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67,
- 0x6c, 0x65, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x32, 0x31, 0x30, 0x32, 0x32,
- 0x30, 0x38, 0x35, 0x32, 0x5a, 0x17, 0x0d, 0x34, 0x39, 0x30, 0x34, 0x32, 0x36, 0x32,
- 0x32, 0x30, 0x38, 0x35, 0x32, 0x5a, 0x30, 0x33, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
- 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x54, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
- 0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
- 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f,
- 0x67, 0x6c, 0x65, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d,
- 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42,
- 0x00, 0x04, 0x1e, 0xac, 0x0c, 0xe0, 0x0d, 0xc5, 0x25, 0x84, 0x1b, 0xd2, 0x77, 0x2d,
- 0xe7, 0xba, 0xf1, 0xde, 0xa7, 0xf6, 0x39, 0x7f, 0x38, 0x91, 0xbf, 0xa4, 0x58, 0xf5,
- 0x62, 0x6b, 0xce, 0x06, 0xcf, 0xb9, 0x73, 0x91, 0x0d, 0x8a, 0x60, 0xa0, 0xc6, 0xa2,
- 0x22, 0xe6, 0x51, 0x2e, 0x58, 0xd6, 0x43, 0x02, 0x80, 0x43, 0x44, 0x29, 0x38, 0x9a,
- 0x99, 0xf3, 0xa4, 0xdd, 0xd0, 0xb4, 0x6f, 0x8b, 0x44, 0x2d, 0xa3, 0x53, 0x30, 0x51,
- 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xdb, 0x13, 0x68,
- 0xe0, 0x0e, 0x47, 0x10, 0xf8, 0xcb, 0x88, 0x83, 0xfe, 0x42, 0x3c, 0xd9, 0x3f, 0x1a,
- 0x33, 0xe9, 0xaa, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
- 0x80, 0x14, 0xdb, 0x13, 0x68, 0xe0, 0x0e, 0x47, 0x10, 0xf8, 0xcb, 0x88, 0x83, 0xfe,
- 0x42, 0x3c, 0xd9, 0x3f, 0x1a, 0x33, 0xe9, 0xaa, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d,
- 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0a, 0x06,
- 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45,
- 0x02, 0x20, 0x10, 0xdf, 0x40, 0xc3, 0x20, 0x54, 0x36, 0xb5, 0xc9, 0x3c, 0x70, 0xe3,
- 0x55, 0x37, 0xd2, 0x04, 0x51, 0xeb, 0x0f, 0x18, 0x83, 0xd0, 0x58, 0xa1, 0x08, 0x77,
- 0x8d, 0x4d, 0xa4, 0x20, 0xee, 0x33, 0x02, 0x21, 0x00, 0x8d, 0xe3, 0xa6, 0x6c, 0x0d,
- 0x86, 0x25, 0xdc, 0x59, 0x0d, 0x21, 0x43, 0x22, 0x3a, 0xb9, 0xa1, 0x73, 0x28, 0xc9,
- 0x16, 0x9e, 0x91, 0x15, 0xc4, 0xc3, 0xd7, 0xeb, 0xe5, 0xce, 0xdc, 0x1c, 0x1b,
- ]
- }
-
- // Generate a fake COSE_Mac0 with a key that's just `byte` repeated
- fn generate_maced_pubkey(byte: u8) -> Vec<u8> {
- vec![
- 0x84, 0x43, 0xA1, 0x01, 0x05, 0xA0, 0x58, 0x4D, 0xA5, 0x01, 0x02, 0x03, 0x26, 0x20,
- 0x01, 0x21, 0x58, 0x20, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte,
- byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte,
- byte, byte, byte, byte, byte, byte, byte, byte, 0x22, 0x58, 0x20, byte, byte, byte,
- byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte,
- byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte,
- byte, 0x58, 0x20, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte,
- byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte, byte,
- byte, byte, byte, byte, byte, byte, byte,
- ]
- }
-
- #[test]
- fn test_parse_cose_mac0_for_coords_raw_bytes() -> Result<()> {
- let cose_mac0: Vec<u8> = vec![
- 0x84, 0x01, 0x02, 0x58, 0x4D, 0xA5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58,
- 0x20, 0x1A, 0xFB, 0xB2, 0xD9, 0x9D, 0xF6, 0x2D, 0xF0, 0xC3, 0xA8, 0xFC, 0x7E, 0xC9,
- 0x21, 0x26, 0xED, 0xB5, 0x4A, 0x98, 0x9B, 0xF3, 0x0D, 0x91, 0x3F, 0xC6, 0x42, 0x5C,
- 0x43, 0x22, 0xC8, 0xEE, 0x03, 0x22, 0x58, 0x20, 0x40, 0xB3, 0x9B, 0xFC, 0x47, 0x95,
- 0x90, 0xA7, 0x5C, 0x5A, 0x16, 0x31, 0x34, 0xAF, 0x0C, 0x5B, 0xF2, 0xB2, 0xD8, 0x2A,
- 0xA3, 0xB3, 0x1A, 0xB4, 0x4C, 0xA6, 0x3B, 0xE7, 0x22, 0xEC, 0x41, 0xDC, 0x03,
- ];
- let raw_key = RemoteProvisioningService::parse_cose_mac0_for_coords(&cose_mac0)?;
- assert_eq!(
- raw_key,
- vec![
- 0x1A, 0xFB, 0xB2, 0xD9, 0x9D, 0xF6, 0x2D, 0xF0, 0xC3, 0xA8, 0xFC, 0x7E, 0xC9, 0x21,
- 0x26, 0xED, 0xB5, 0x4A, 0x98, 0x9B, 0xF3, 0x0D, 0x91, 0x3F, 0xC6, 0x42, 0x5C, 0x43,
- 0x22, 0xC8, 0xEE, 0x03, 0x40, 0xB3, 0x9B, 0xFC, 0x47, 0x95, 0x90, 0xA7, 0x5C, 0x5A,
- 0x16, 0x31, 0x34, 0xAF, 0x0C, 0x5B, 0xF2, 0xB2, 0xD8, 0x2A, 0xA3, 0xB3, 0x1A, 0xB4,
- 0x4C, 0xA6, 0x3B, 0xE7, 0x22, 0xEC, 0x41, 0xDC,
- ]
- );
- Ok(())
- }
-
- #[test]
- fn test_parse_cose_mac0_for_coords_constructed_mac() -> Result<()> {
- let x_coord: Vec<u8> = vec![0; 32];
- let y_coord: Vec<u8> = vec![1; 32];
- let mut expected_key: Vec<u8> = Vec::new();
- expected_key.extend(&x_coord);
- expected_key.extend(&y_coord);
- let key_map: BTreeMap<Value, Value> = BTreeMap::from([
- (Value::Integer(1), Value::Integer(2)),
- (Value::Integer(3), Value::Integer(-7)),
- (Value::Integer(-1), Value::Integer(1)),
- (Value::Integer(-2), Value::Bytes(x_coord)),
- (Value::Integer(-3), Value::Bytes(y_coord)),
- ]);
- let cose_mac0: Vec<Value> = vec![
- Value::Integer(0),
- Value::Integer(1),
- Value::from(serde_cbor::to_vec(&key_map)?),
- Value::Integer(2),
- ];
- let raw_key = RemoteProvisioningService::parse_cose_mac0_for_coords(&serde_cbor::to_vec(
- &Value::from(cose_mac0),
- )?)?;
- assert_eq!(expected_key, raw_key);
- Ok(())
- }
-
- #[test]
- fn test_extract_payload_from_cose_mac() -> Result<()> {
- let key_map = Value::Map(BTreeMap::from([(Value::Integer(1), Value::Integer(2))]));
- let payload = Value::Bytes(serde_cbor::to_vec(&key_map)?);
- let cose_mac0 =
- Value::Array(vec![Value::Integer(0), Value::Integer(1), payload, Value::Integer(3)]);
- let extracted_map = RemoteProvisioningService::extract_payload_from_cose_mac(
- &serde_cbor::to_vec(&cose_mac0)?,
- )?;
- assert_eq!(key_map, extracted_map);
- Ok(())
- }
-
- #[test]
- fn test_extract_payload_from_cose_mac_fails_malformed_payload() -> Result<()> {
- let payload = Value::Bytes(vec![5; 10]);
- let cose_mac0 =
- Value::Array(vec![Value::Integer(0), Value::Integer(1), payload, Value::Integer(3)]);
- let extracted_payload = RemoteProvisioningService::extract_payload_from_cose_mac(
- &serde_cbor::to_vec(&cose_mac0)?,
- );
- assert!(extracted_payload.is_err());
- Ok(())
- }
-
- #[test]
- fn test_extract_payload_from_cose_mac_fails_type() -> Result<()> {
- let payload = Value::Integer(1);
- let cose_mac0 =
- Value::Array(vec![Value::Integer(0), Value::Integer(1), payload, Value::Integer(3)]);
- let extracted_payload = RemoteProvisioningService::extract_payload_from_cose_mac(
- &serde_cbor::to_vec(&cose_mac0)?,
- );
- assert!(extracted_payload.is_err());
- Ok(())
- }
-
- #[test]
- fn test_extract_payload_from_cose_mac_fails_length() -> Result<()> {
- let cose_mac0 = Value::Array(vec![Value::Integer(0), Value::Integer(1)]);
- let extracted_payload = RemoteProvisioningService::extract_payload_from_cose_mac(
- &serde_cbor::to_vec(&cose_mac0)?,
- );
- assert!(extracted_payload.is_err());
- Ok(())
- }
-
- #[test]
- #[ignore] // b/215746308
- fn test_get_attestation_key_no_keys_provisioned() {
- let mut db = crate::database::tests::new_test_db().unwrap();
- let mock_rpc = Box::<MockRemotelyProvisionedComponent>::default();
- mock_rpc.0.lock().unwrap().hw_info.uniqueId = Some(String::from("mallory"));
-
- let mut service: RemotelyProvisionedKeyPoolService = Default::default();
- service
- .unique_id_to_sec_level
- .insert(String::from("mallory"), SecurityLevel::TRUSTED_ENVIRONMENT);
-
- assert_eq!(
- service
- .get_attestation_key(&mut db, 0, "mallory")
- .unwrap_err()
- .downcast::<error::Error>()
- .unwrap(),
- error::Error::Rc(ResponseCode::OUT_OF_KEYS)
- );
- }
-
- #[test]
- #[ignore] // b/215746308
- fn test_get_attestation_key() {
- let mut db = crate::database::tests::new_test_db().unwrap();
- let sec_level = SecurityLevel::TRUSTED_ENVIRONMENT;
- let irpc_id = "paul";
- let caller_uid = 0;
-
- let mock_rpc = Box::<MockRemotelyProvisionedComponent>::default();
- let mock_values = mock_rpc.0.clone();
- let mut remote_provisioning: RemoteProvisioningService = Default::default();
- remote_provisioning.device_by_sec_level.insert(sec_level, Strong::new(mock_rpc));
- let mut key_pool: RemotelyProvisionedKeyPoolService = Default::default();
- key_pool.unique_id_to_sec_level.insert(String::from(irpc_id), sec_level);
-
- mock_values.lock().unwrap().hw_info.uniqueId = Some(String::from(irpc_id));
- mock_values.lock().unwrap().private_key = vec![8, 6, 7, 5, 3, 0, 9];
- mock_values.lock().unwrap().maced_public_key = generate_maced_pubkey(0x11);
- remote_provisioning.generate_key_pair(&mut db, true, sec_level).unwrap();
-
- let public_key = RemoteProvisioningService::parse_cose_mac0_for_coords(
- mock_values.lock().unwrap().maced_public_key.as_slice(),
- )
- .unwrap();
- let batch_cert = get_fake_cert();
- let certs = &[5, 6, 7, 8];
- assert!(remote_provisioning
- .provision_cert_chain(
- &mut db,
- public_key.as_slice(),
- batch_cert.as_slice(),
- certs,
- 0,
- sec_level
- )
- .is_ok());
-
- // ensure we got the key we expected
- let first_key = key_pool
- .get_attestation_key(&mut db, caller_uid, irpc_id)
- .context("get first key")
- .unwrap();
- assert_eq!(first_key.keyBlob, mock_values.lock().unwrap().private_key);
- assert_eq!(first_key.encodedCertChain, certs);
-
- // ensure that multiple calls get the same key
- assert_eq!(
- first_key,
- key_pool
- .get_attestation_key(&mut db, caller_uid, irpc_id)
- .context("get second key")
- .unwrap()
- );
-
- // no more keys for new clients
- assert_eq!(
- key_pool
- .get_attestation_key(&mut db, caller_uid + 1, irpc_id)
- .unwrap_err()
- .downcast::<error::Error>()
- .unwrap(),
- error::Error::Rc(ResponseCode::OUT_OF_KEYS)
- );
- }
-
- #[test]
- #[ignore] // b/215746308
- fn test_get_attestation_key_gets_different_key_for_different_client() {
- let mut db = crate::database::tests::new_test_db().unwrap();
- let sec_level = SecurityLevel::TRUSTED_ENVIRONMENT;
- let irpc_id = "ringo";
- let first_caller = 0;
- let second_caller = first_caller + 1;
-
- let mock_rpc = Box::<MockRemotelyProvisionedComponent>::default();
- let mock_values = mock_rpc.0.clone();
- let mut remote_provisioning: RemoteProvisioningService = Default::default();
- remote_provisioning.device_by_sec_level.insert(sec_level, Strong::new(mock_rpc));
- let mut key_pool: RemotelyProvisionedKeyPoolService = Default::default();
- key_pool.unique_id_to_sec_level.insert(String::from(irpc_id), sec_level);
-
- // generate two distinct keys and provision them with certs
- mock_values.lock().unwrap().hw_info.uniqueId = Some(String::from(irpc_id));
- mock_values.lock().unwrap().private_key = vec![3, 1, 4, 1, 5];
- mock_values.lock().unwrap().maced_public_key = generate_maced_pubkey(0x11);
- assert!(remote_provisioning.generate_key_pair(&mut db, true, sec_level).is_ok());
- let public_key = RemoteProvisioningService::parse_cose_mac0_for_coords(
- mock_values.lock().unwrap().maced_public_key.as_slice(),
- )
- .unwrap();
- assert!(remote_provisioning
- .provision_cert_chain(
- &mut db,
- public_key.as_slice(),
- get_fake_cert().as_slice(),
- &[1],
- 0,
- sec_level
- )
- .is_ok());
-
- mock_values.lock().unwrap().hw_info.uniqueId = Some(String::from(irpc_id));
- mock_values.lock().unwrap().private_key = vec![9, 0, 2, 1, 0];
- mock_values.lock().unwrap().maced_public_key = generate_maced_pubkey(0x22);
- assert!(remote_provisioning.generate_key_pair(&mut db, true, sec_level).is_ok());
- let public_key = RemoteProvisioningService::parse_cose_mac0_for_coords(
- mock_values.lock().unwrap().maced_public_key.as_slice(),
- )
- .unwrap();
- assert!(remote_provisioning
- .provision_cert_chain(
- &mut db,
- public_key.as_slice(),
- get_fake_cert().as_slice(),
- &[2],
- 0,
- sec_level
- )
- .is_ok());
-
- // make sure each caller gets a distinct key
- assert_ne!(
- key_pool
- .get_attestation_key(&mut db, first_caller, irpc_id)
- .context("get first key")
- .unwrap(),
- key_pool
- .get_attestation_key(&mut db, second_caller, irpc_id)
- .context("get second key")
- .unwrap()
- );
-
- // repeated calls should return the same key for a given caller
- assert_eq!(
- key_pool
- .get_attestation_key(&mut db, first_caller, irpc_id)
- .context("first caller a")
- .unwrap(),
- key_pool
- .get_attestation_key(&mut db, first_caller, irpc_id)
- .context("first caller b")
- .unwrap(),
- );
-
- assert_eq!(
- key_pool
- .get_attestation_key(&mut db, second_caller, irpc_id)
- .context("second caller a")
- .unwrap(),
- key_pool
- .get_attestation_key(&mut db, second_caller, irpc_id)
- .context("second caller b")
- .unwrap()
- );
- }
-}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 28de1ec8..5cb3afcd 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -18,18 +18,17 @@ use crate::attestation_key_utils::{get_attest_key_info, AttestationKeyInfo};
use crate::audit_log::{
log_key_deleted, log_key_generated, log_key_imported, log_key_integrity_violation,
};
-use crate::database::{BlobInfo, CertificateInfo, KeyIdGuard};
+use crate::database::{CertificateInfo, KeyIdGuard};
use crate::error::{self, map_km_error, map_or_log_err, Error, ErrorCode};
-use crate::globals::{DB, ENFORCEMENTS, LEGACY_IMPORTER, SUPER_KEY};
+use crate::globals::{DB, ENFORCEMENTS, LEGACY_MIGRATOR, SUPER_KEY};
use crate::key_parameter::KeyParameter as KsKeyParam;
use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
use crate::metrics_store::log_key_creation_event_stats;
use crate::remote_provisioning::RemProvState;
use crate::super_key::{KeyBlob, SuperKeyManager};
use crate::utils::{
- check_device_attestation_permissions, check_key_permission,
- check_unique_id_attestation_permissions, is_device_id_attestation_tag,
- key_characteristics_to_internal, uid_to_android_user, watchdog as wd,
+ check_device_attestation_permissions, check_key_permission, is_device_id_attestation_tag,
+ key_characteristics_to_internal, uid_to_android_user, watchdog as wd, Asp,
};
use crate::{
database::{
@@ -55,16 +54,14 @@ use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, EphemeralStorageKeyResponse::EphemeralStorageKeyResponse,
IKeystoreOperation::IKeystoreOperation, IKeystoreSecurityLevel::BnKeystoreSecurityLevel,
IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
- KeyMetadata::KeyMetadata, KeyParameters::KeyParameters, ResponseCode::ResponseCode,
+ KeyMetadata::KeyMetadata, KeyParameters::KeyParameters,
};
use anyhow::{anyhow, Context, Result};
-use std::convert::TryInto;
-use std::time::SystemTime;
/// Implementation of the IKeystoreSecurityLevel Interface.
pub struct KeystoreSecurityLevel {
security_level: SecurityLevel,
- keymint: Strong<dyn IKeyMintDevice>,
+ keymint: Asp,
hw_info: KeyMintHardwareInfo,
km_uuid: Uuid,
operation_db: OperationDb,
@@ -133,7 +130,8 @@ impl KeystoreSecurityLevel {
_ => Some(
certificate_chain
.iter()
- .flat_map(|c| c.encodedCertificate.iter())
+ .map(|c| c.encodedCertificate.iter())
+ .flatten()
.copied()
.collect(),
),
@@ -160,11 +158,9 @@ impl KeystoreSecurityLevel {
let mut db = db.borrow_mut();
let (key_blob, mut blob_metadata) = SUPER_KEY
- .read()
- .unwrap()
.handle_super_encryption_on_key_init(
&mut db,
- &LEGACY_IMPORTER,
+ &LEGACY_MIGRATOR,
&(key.domain),
&key_parameters,
flags,
@@ -182,7 +178,7 @@ impl KeystoreSecurityLevel {
&key,
KeyType::Client,
&key_parameters,
- &BlobInfo::new(&key_blob, &blob_metadata),
+ &(&key_blob, &blob_metadata),
&cert_info,
&key_metadata,
&self.km_uuid,
@@ -220,10 +216,10 @@ impl KeystoreSecurityLevel {
let scoping_blob: Vec<u8>;
let (km_blob, key_properties, key_id_guard, blob_metadata) = match key.domain {
Domain::BLOB => {
- check_key_permission(KeyPerm::Use, key, &None)
+ check_key_permission(KeyPerm::use_(), key, &None)
.context("In create_operation: checking use permission for Domain::BLOB.")?;
if forced {
- check_key_permission(KeyPerm::ReqForcedOp, key, &None).context(
+ check_key_permission(KeyPerm::req_forced_op(), key, &None).context(
"In create_operation: checking forced permission for Domain::BLOB.",
)?;
}
@@ -243,22 +239,18 @@ impl KeystoreSecurityLevel {
)
}
_ => {
- let super_key = SUPER_KEY
- .read()
- .unwrap()
- .get_per_boot_key_by_user_id(uid_to_android_user(caller_uid));
let (key_id_guard, mut key_entry) = DB
.with::<_, Result<(KeyIdGuard, KeyEntry)>>(|db| {
- LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || {
+ LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
db.borrow_mut().load_key_entry(
- key,
+ &key,
KeyType::Client,
KeyEntryLoadBits::KM,
caller_uid,
|k, av| {
- check_key_permission(KeyPerm::Use, k, &av)?;
+ check_key_permission(KeyPerm::use_(), k, &av)?;
if forced {
- check_key_permission(KeyPerm::ReqForcedOp, k, &av)?;
+ check_key_permission(KeyPerm::req_forced_op(), k, &av)?;
}
Ok(())
},
@@ -309,30 +301,28 @@ impl KeystoreSecurityLevel {
.context("In create_operation.")?;
let km_blob = SUPER_KEY
- .read()
- .unwrap()
.unwrap_key_if_required(&blob_metadata, km_blob)
.context("In create_operation. Failed to handle super encryption.")?;
+ let km_dev: Strong<dyn IKeyMintDevice> = self
+ .keymint
+ .get_interface()
+ .context("In create_operation: Failed to get KeyMint device")?;
+
let (begin_result, upgraded_blob) = self
.upgrade_keyblob_if_required_with(
- &*self.keymint,
+ &*km_dev,
key_id_guard,
&km_blob,
- blob_metadata.km_uuid().copied(),
- operation_parameters,
+ &blob_metadata,
+ &operation_parameters,
|blob| loop {
match map_km_error({
let _wp = self.watch_millis(
"In KeystoreSecurityLevel::create_operation: calling begin",
500,
);
- self.keymint.begin(
- purpose,
- blob,
- operation_parameters,
- immediate_hat.as_ref(),
- )
+ km_dev.begin(purpose, blob, &operation_parameters, immediate_hat.as_ref())
}) {
Err(Error::Km(ErrorCode::TOO_MANY_OPERATIONS)) => {
self.operation_db.prune(caller_uid, forced)?;
@@ -376,7 +366,7 @@ impl KeystoreSecurityLevel {
}
};
- let op_binder: binder::Strong<dyn IKeystoreOperation> =
+ let op_binder: binder::public_api::Strong<dyn IKeystoreOperation> =
KeystoreOperation::new_native_binder(operation)
.as_binder()
.into_interface()
@@ -396,53 +386,25 @@ impl KeystoreSecurityLevel {
})
}
- fn add_required_parameters(
+ fn add_certificate_parameters(
&self,
uid: u32,
params: &[KeyParameter],
key: &KeyDescriptor,
) -> Result<Vec<KeyParameter>> {
let mut result = params.to_vec();
-
- // Unconditionally add the CREATION_DATETIME tag and prevent callers from
- // specifying it.
- if params.iter().any(|kp| kp.tag == Tag::CREATION_DATETIME) {
- return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
- "In KeystoreSecurityLevel::add_required_parameters: \
- Specifying Tag::CREATION_DATETIME is not allowed.",
- );
- }
-
- // Add CREATION_DATETIME only if the backend version Keymint V1 (100) or newer.
- if self.hw_info.versionNumber >= 100 {
- result.push(KeyParameter {
- tag: Tag::CREATION_DATETIME,
- value: KeyParameterValue::DateTime(
- SystemTime::now()
- .duration_since(SystemTime::UNIX_EPOCH)
- .context(
- "In KeystoreSecurityLevel::add_required_parameters: \
- Failed to get epoch time.",
- )?
- .as_millis()
- .try_into()
- .context(
- "In KeystoreSecurityLevel::add_required_parameters: \
- Failed to convert epoch time.",
- )?,
- ),
- });
- }
-
// If there is an attestation challenge we need to get an application id.
if params.iter().any(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE) {
let aaid = {
let _wp = self.watch_millis(
- "In KeystoreSecurityLevel::add_required_parameters calling: get_aaid",
+ "In KeystoreSecurityLevel::add_certificate_parameters calling: get_aaid",
500,
);
keystore2_aaid::get_aaid(uid).map_err(|e| {
- anyhow!(format!("In add_required_parameters: get_aaid returned status {}.", e))
+ anyhow!(format!(
+ "In add_certificate_parameters: get_aaid returned status {}.",
+ e
+ ))
})
}?;
@@ -453,18 +415,14 @@ impl KeystoreSecurityLevel {
}
if params.iter().any(|kp| kp.tag == Tag::INCLUDE_UNIQUE_ID) {
- if check_key_permission(KeyPerm::GenUniqueId, key, &None).is_err()
- && check_unique_id_attestation_permissions().is_err()
- {
- return Err(Error::perm()).context(
- "In add_required_parameters: \
- Caller does not have the permission to generate a unique ID",
- );
- }
+ check_key_permission(KeyPerm::gen_unique_id(), key, &None).context(concat!(
+ "In add_certificate_parameters: ",
+ "Caller does not have the permission to generate a unique ID"
+ ))?;
if self.id_rotation_state.had_factory_reset_since_id_rotation().context(
- "In add_required_parameters: Call to had_factory_reset_since_id_rotation failed.",
+ "In add_certificate_parameters: Call to had_factory_reset_since_id_rotation failed."
)? {
- result.push(KeyParameter {
+ result.push(KeyParameter{
tag: Tag::RESET_SINCE_ID_ROTATION,
value: KeyParameterValue::BoolValue(true),
})
@@ -475,7 +433,7 @@ impl KeystoreSecurityLevel {
// correct Android permission.
if params.iter().any(|kp| is_device_id_attestation_tag(kp.tag)) {
check_device_attestation_permissions().context(concat!(
- "In add_required_parameters: ",
+ "In add_certificate_parameters: ",
"Caller does not have the permission to attest device identifiers."
))?;
}
@@ -529,7 +487,7 @@ impl KeystoreSecurityLevel {
// generate_key requires the rebind permission.
// Must return on error for security reasons.
- check_key_permission(KeyPerm::Rebind, &key, &None).context("In generate_key.")?;
+ check_key_permission(KeyPerm::rebind(), &key, &None).context("In generate_key.")?;
let attestation_key_info = match (key.domain, attest_key_descriptor) {
(Domain::BLOB, _) => None,
@@ -547,9 +505,11 @@ impl KeystoreSecurityLevel {
.context("In generate_key: Trying to get an attestation key")?,
};
let params = self
- .add_required_parameters(caller_uid, params, &key)
+ .add_certificate_parameters(caller_uid, params, &key)
.context("In generate_key: Trying to get aaid.")?;
+ let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface()?;
+
let creation_result = match attestation_key_info {
Some(AttestationKeyInfo::UserGenerated {
key_id_guard,
@@ -558,10 +518,10 @@ impl KeystoreSecurityLevel {
issuer_subject,
}) => self
.upgrade_keyblob_if_required_with(
- &*self.keymint,
+ &*km_dev,
Some(key_id_guard),
&KeyBlob::Ref(&blob),
- blob_metadata.km_uuid().copied(),
+ &blob_metadata,
&params,
|blob| {
let attest_key = Some(AttestationKey {
@@ -577,46 +537,29 @@ impl KeystoreSecurityLevel {
),
5000, // Generate can take a little longer.
);
- self.keymint.generateKey(&params, attest_key.as_ref())
+ km_dev.generateKey(&params, attest_key.as_ref())
})
},
)
.context("In generate_key: Using user generated attestation key.")
.map(|(result, _)| result),
- Some(AttestationKeyInfo::RemoteProvisioned {
- key_id_guard,
- attestation_key,
- attestation_certs,
- }) => self
- .upgrade_keyblob_if_required_with(
- &*self.keymint,
- Some(key_id_guard),
- &KeyBlob::Ref(&attestation_key.keyBlob),
- Some(self.rem_prov_state.get_uuid()),
- &[],
- |blob| {
- map_km_error({
- let _wp = self.watch_millis(
- concat!(
- "In KeystoreSecurityLevel::generate_key (RemoteProvisioned): ",
- "calling generate_key.",
- ),
- 5000, // Generate can take a little longer.
- );
- let dynamic_attest_key = Some(AttestationKey {
- keyBlob: blob.to_vec(),
- attestKeyParams: vec![],
- issuerSubjectName: attestation_key.issuerSubjectName.clone(),
- });
- self.keymint.generateKey(&params, dynamic_attest_key.as_ref())
- })
- },
- )
+ Some(AttestationKeyInfo::RemoteProvisioned { attestation_key, attestation_certs }) => {
+ map_km_error({
+ let _wp = self.watch_millis(
+ concat!(
+ "In KeystoreSecurityLevel::generate_key (RemoteProvisioned): ",
+ "calling generate_key.",
+ ),
+ 5000, // Generate can take a little longer.
+ );
+ km_dev.generateKey(&params, Some(&attestation_key))
+ })
.context("While generating Key with remote provisioned attestation key.")
- .map(|(mut result, _)| {
- result.certificateChain.push(attestation_certs);
- result
- }),
+ .map(|mut creation_result| {
+ creation_result.certificateChain.push(attestation_certs);
+ creation_result
+ })
+ }
None => map_km_error({
let _wp = self.watch_millis(
concat!(
@@ -625,7 +568,7 @@ impl KeystoreSecurityLevel {
),
5000, // Generate can take a little longer.
);
- self.keymint.generateKey(&params, None)
+ km_dev.generateKey(&params, None)
})
.context("While generating Key without explicit attestation key."),
}
@@ -660,10 +603,10 @@ impl KeystoreSecurityLevel {
};
// 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("In import_key.")?;
let params = self
- .add_required_parameters(caller_uid, params, &key)
+ .add_certificate_parameters(caller_uid, params, &key)
.context("In import_key: Trying to get aaid.")?;
let format = params
@@ -682,7 +625,8 @@ impl KeystoreSecurityLevel {
})
.context("In import_key.")?;
- let km_dev = &self.keymint;
+ let km_dev: Strong<dyn IKeyMintDevice> =
+ self.keymint.get_interface().context("In import_key: Trying to get the KM device")?;
let creation_result = map_km_error({
let _wp =
self.watch_millis("In KeystoreSecurityLevel::import_key: calling importKey.", 500);
@@ -744,19 +688,17 @@ impl KeystoreSecurityLevel {
};
// Import_wrapped_key requires the rebind permission for the new key.
- check_key_permission(KeyPerm::Rebind, &key, &None).context("In import_wrapped_key.")?;
-
- let super_key = SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(user_id);
+ check_key_permission(KeyPerm::rebind(), &key, &None).context("In import_wrapped_key.")?;
let (wrapping_key_id_guard, mut wrapping_key_entry) = DB
.with(|db| {
- LEGACY_IMPORTER.with_try_import(&key, caller_uid, super_key, || {
+ LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
db.borrow_mut().load_key_entry(
- wrapping_key,
+ &wrapping_key,
KeyType::Client,
KeyEntryLoadBits::KM,
caller_uid,
- |k, av| check_key_permission(KeyPerm::Use, k, &av),
+ |k, av| check_key_permission(KeyPerm::use_(), k, &av),
)
})
})
@@ -767,11 +709,8 @@ impl KeystoreSecurityLevel {
.ok_or_else(error::Error::sys)
.context("No km_blob after successfully loading key. This should never happen.")?;
- let wrapping_key_blob = SUPER_KEY
- .read()
- .unwrap()
- .unwrap_key_if_required(&wrapping_blob_metadata, &wrapping_key_blob)
- .context(
+ let wrapping_key_blob =
+ SUPER_KEY.unwrap_key_if_required(&wrapping_blob_metadata, &wrapping_key_blob).context(
"In import_wrapped_key. Failed to handle super encryption for wrapping key.",
)?;
@@ -797,23 +736,24 @@ impl KeystoreSecurityLevel {
let masking_key = masking_key.unwrap_or(ZERO_BLOB_32);
+ let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface()?;
let (creation_result, _) = self
.upgrade_keyblob_if_required_with(
- &*self.keymint,
+ &*km_dev,
Some(wrapping_key_id_guard),
&wrapping_key_blob,
- wrapping_blob_metadata.km_uuid().copied(),
+ &wrapping_blob_metadata,
&[],
|wrapping_blob| {
let _wp = self.watch_millis(
"In KeystoreSecurityLevel::import_wrapped_key: calling importWrappedKey.",
500,
);
- let creation_result = map_km_error(self.keymint.importWrappedKey(
+ let creation_result = map_km_error(km_dev.importWrappedKey(
wrapped_data,
wrapping_blob,
masking_key,
- params,
+ &params,
pw_sid,
fp_sid,
))?;
@@ -828,17 +768,17 @@ impl KeystoreSecurityLevel {
fn store_upgraded_keyblob(
key_id_guard: KeyIdGuard,
- km_uuid: Option<Uuid>,
+ km_uuid: Option<&Uuid>,
key_blob: &KeyBlob,
upgraded_blob: &[u8],
) -> Result<()> {
let (upgraded_blob_to_be_stored, new_blob_metadata) =
- SuperKeyManager::reencrypt_if_required(key_blob, upgraded_blob)
+ SuperKeyManager::reencrypt_if_required(key_blob, &upgraded_blob)
.context("In store_upgraded_keyblob: Failed to handle super encryption.")?;
let mut new_blob_metadata = new_blob_metadata.unwrap_or_default();
if let Some(uuid) = km_uuid {
- new_blob_metadata.add(BlobMetaEntry::KmUuid(uuid));
+ new_blob_metadata.add(BlobMetaEntry::KmUuid(*uuid));
}
DB.with(|db| {
@@ -856,46 +796,69 @@ impl KeystoreSecurityLevel {
fn upgrade_keyblob_if_required_with<T, F>(
&self,
km_dev: &dyn IKeyMintDevice,
- mut key_id_guard: Option<KeyIdGuard>,
+ key_id_guard: Option<KeyIdGuard>,
key_blob: &KeyBlob,
- km_uuid: Option<Uuid>,
+ blob_metadata: &BlobMetaData,
params: &[KeyParameter],
f: F,
) -> Result<(T, Option<Vec<u8>>)>
where
F: Fn(&[u8]) -> Result<T, Error>,
{
- let (v, upgraded_blob) = crate::utils::upgrade_keyblob_if_required_with(
- km_dev,
- key_blob,
- params,
- f,
- |upgraded_blob| {
- if key_id_guard.is_some() {
- // Unwrap cannot panic, because the is_some was true.
- let kid = key_id_guard.take().unwrap();
- Self::store_upgraded_keyblob(kid, km_uuid, key_blob, upgraded_blob).context(
- "In upgrade_keyblob_if_required_with: store_upgraded_keyblob failed",
+ match f(key_blob) {
+ Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
+ let upgraded_blob = {
+ let _wp = self.watch_millis(
+ concat!(
+ "In KeystoreSecurityLevel::upgrade_keyblob_if_required_with: ",
+ "calling upgradeKey."
+ ),
+ 500,
+ );
+ map_km_error(km_dev.upgradeKey(key_blob, params))
+ }
+ .context("In upgrade_keyblob_if_required_with: Upgrade failed.")?;
+
+ if let Some(kid) = key_id_guard {
+ Self::store_upgraded_keyblob(
+ kid,
+ blob_metadata.km_uuid(),
+ key_blob,
+ &upgraded_blob,
)
- } else {
- Ok(())
+ .context(
+ "In upgrade_keyblob_if_required_with: store_upgraded_keyblob failed",
+ )?;
}
- },
- )
- .context("In KeystoreSecurityLevel::upgrade_keyblob_if_required_with.")?;
-
- // If no upgrade was needed, use the opportunity to reencrypt the blob if required
- // and if the a key_id_guard is held. Note: key_id_guard can only be Some if no
- // upgrade was performed above and if one was given in the first place.
- if key_blob.force_reencrypt() {
- if let Some(kid) = key_id_guard {
- Self::store_upgraded_keyblob(kid, km_uuid, key_blob, key_blob).context(concat!(
- "In upgrade_keyblob_if_required_with: ",
- "store_upgraded_keyblob failed in forced reencrypt"
- ))?;
+
+ match f(&upgraded_blob) {
+ Ok(v) => Ok((v, Some(upgraded_blob))),
+ Err(e) => Err(e).context(concat!(
+ "In upgrade_keyblob_if_required_with: ",
+ "Failed to perform operation on second try."
+ )),
+ }
+ }
+ result => {
+ if let Some(kid) = key_id_guard {
+ if key_blob.force_reencrypt() {
+ Self::store_upgraded_keyblob(
+ kid,
+ blob_metadata.km_uuid(),
+ key_blob,
+ key_blob,
+ )
+ .context(concat!(
+ "In upgrade_keyblob_if_required_with: ",
+ "store_upgraded_keyblob failed in forced reencrypt"
+ ))?;
+ }
+ }
+ result
+ .map(|v| (v, None))
+ .context("In upgrade_keyblob_if_required_with: Called closure failed.")
}
}
- Ok((v, upgraded_blob))
}
fn convert_storage_key_to_ephemeral(
@@ -917,10 +880,13 @@ impl KeystoreSecurityLevel {
)?;
// convert_storage_key_to_ephemeral requires the associated permission
- check_key_permission(KeyPerm::ConvertStorageKeyToEphemeral, storage_key, &None)
+ check_key_permission(KeyPerm::convert_storage_key_to_ephemeral(), storage_key, &None)
.context("In convert_storage_key_to_ephemeral: Check permission")?;
- let km_dev = &self.keymint;
+ let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface().context(concat!(
+ "In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: ",
+ "Getting keymint device interface"
+ ))?;
match {
let _wp = self.watch_millis(
concat!(
@@ -976,14 +942,17 @@ impl KeystoreSecurityLevel {
.ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
.context("In IKeystoreSecurityLevel delete_key: No key blob specified")?;
- check_key_permission(KeyPerm::Delete, key, &None)
+ check_key_permission(KeyPerm::delete(), key, &None)
.context("In IKeystoreSecurityLevel delete_key: Checking delete permissions")?;
- let km_dev = &self.keymint;
+ let km_dev: Strong<dyn IKeyMintDevice> = self
+ .keymint
+ .get_interface()
+ .context("In IKeystoreSecurityLevel delete_key: Getting keymint device interface")?;
{
let _wp =
self.watch_millis("In KeystoreSecuritylevel::delete_key: calling deleteKey", 500);
- map_km_error(km_dev.deleteKey(key_blob)).context("In keymint device deleteKey")
+ map_km_error(km_dev.deleteKey(&key_blob)).context("In keymint device deleteKey")
}
}
}
@@ -996,7 +965,7 @@ impl IKeystoreSecurityLevel for KeystoreSecurityLevel {
key: &KeyDescriptor,
operation_parameters: &[KeyParameter],
forced: bool,
- ) -> binder::Result<CreateOperationResponse> {
+ ) -> binder::public_api::Result<CreateOperationResponse> {
let _wp = self.watch_millis("IKeystoreSecurityLevel::createOperation", 500);
map_or_log_err(self.create_operation(key, operation_parameters, forced), Ok)
}
@@ -1007,7 +976,7 @@ impl IKeystoreSecurityLevel for KeystoreSecurityLevel {
params: &[KeyParameter],
flags: i32,
entropy: &[u8],
- ) -> binder::Result<KeyMetadata> {
+ ) -> binder::public_api::Result<KeyMetadata> {
// Duration is set to 5 seconds, because generateKey - especially for RSA keys, takes more
// time than other operations
let _wp = self.watch_millis("IKeystoreSecurityLevel::generateKey", 5000);
@@ -1023,7 +992,7 @@ impl IKeystoreSecurityLevel for KeystoreSecurityLevel {
params: &[KeyParameter],
flags: i32,
key_data: &[u8],
- ) -> binder::Result<KeyMetadata> {
+ ) -> binder::public_api::Result<KeyMetadata> {
let _wp = self.watch_millis("IKeystoreSecurityLevel::importKey", 500);
let result = self.import_key(key, attestation_key, params, flags, key_data);
log_key_creation_event_stats(self.security_level, params, &result);
@@ -1037,7 +1006,7 @@ impl IKeystoreSecurityLevel for KeystoreSecurityLevel {
masking_key: Option<&[u8]>,
params: &[KeyParameter],
authenticators: &[AuthenticatorSpec],
- ) -> binder::Result<KeyMetadata> {
+ ) -> binder::public_api::Result<KeyMetadata> {
let _wp = self.watch_millis("IKeystoreSecurityLevel::importWrappedKey", 500);
let result =
self.import_wrapped_key(key, wrapping_key, masking_key, params, authenticators);
@@ -1048,11 +1017,11 @@ impl IKeystoreSecurityLevel for KeystoreSecurityLevel {
fn convertStorageKeyToEphemeral(
&self,
storage_key: &KeyDescriptor,
- ) -> binder::Result<EphemeralStorageKeyResponse> {
+ ) -> binder::public_api::Result<EphemeralStorageKeyResponse> {
let _wp = self.watch_millis("IKeystoreSecurityLevel::convertStorageKeyToEphemeral", 500);
map_or_log_err(self.convert_storage_key_to_ephemeral(storage_key), Ok)
}
- fn deleteKey(&self, key: &KeyDescriptor) -> binder::Result<()> {
+ fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
let _wp = self.watch_millis("IKeystoreSecurityLevel::deleteKey", 500);
let result = self.delete_key(key);
log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index d634e0c0..d65743d2 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -22,11 +22,11 @@ use crate::permission::{KeyPerm, KeystorePerm};
use crate::security_level::KeystoreSecurityLevel;
use crate::utils::{
check_grant_permission, check_key_permission, check_keystore_permission,
- key_parameters_to_authorizations, list_key_entries, uid_to_android_user, watchdog as wd,
+ key_parameters_to_authorizations, watchdog as wd, Asp,
};
use crate::{
database::Uuid,
- globals::{create_thread_local_db, DB, LEGACY_BLOB_LOADER, LEGACY_IMPORTER, SUPER_KEY},
+ globals::{create_thread_local_db, DB, LEGACY_BLOB_LOADER, LEGACY_MIGRATOR},
};
use crate::{database::KEYSTORE_UUID, permission};
use crate::{
@@ -51,7 +51,7 @@ use keystore2_selinux as selinux;
/// Implementation of the IKeystoreService.
#[derive(Default)]
pub struct KeystoreService {
- i_sec_level_by_uuid: HashMap<Uuid, Strong<dyn IKeystoreSecurityLevel>>,
+ i_sec_level_by_uuid: HashMap<Uuid, Asp>,
uuid_by_sec_level: HashMap<SecurityLevel, Uuid>,
}
@@ -68,20 +68,22 @@ impl KeystoreService {
.context(concat!(
"In KeystoreService::new_native_binder: ",
"Trying to construct mandatory security level TEE."
- ))?;
+ ))
+ .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))?;
result.i_sec_level_by_uuid.insert(uuid, dev);
result.uuid_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, uuid);
// Strongbox is optional, so we ignore errors and turn the result into an Option.
if let Ok((dev, uuid)) =
KeystoreSecurityLevel::new_native_binder(SecurityLevel::STRONGBOX, id_rotation_state)
+ .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))
{
result.i_sec_level_by_uuid.insert(uuid, dev);
result.uuid_by_sec_level.insert(SecurityLevel::STRONGBOX, uuid);
}
let uuid_by_sec_level = result.uuid_by_sec_level.clone();
- LEGACY_IMPORTER
+ LEGACY_MIGRATOR
.set_init(move || {
(create_thread_local_db(), uuid_by_sec_level, LEGACY_BLOB_LOADER.clone())
})
@@ -105,7 +107,7 @@ impl KeystoreService {
fn get_i_sec_level_by_uuid(&self, uuid: &Uuid) -> Result<Strong<dyn IKeystoreSecurityLevel>> {
if let Some(dev) = self.i_sec_level_by_uuid.get(uuid) {
- Ok(dev.clone())
+ dev.get_interface().context("In get_i_sec_level_by_uuid.")
} else {
Err(error::Error::sys())
.context("In get_i_sec_level_by_uuid: KeyMint instance for key not found.")
@@ -121,7 +123,7 @@ impl KeystoreService {
.get(&sec_level)
.and_then(|uuid| self.i_sec_level_by_uuid.get(uuid))
{
- Ok(dev.clone())
+ dev.get_interface().context("In get_security_level.")
} else {
Err(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
.context("In get_security_level: No such security level.")
@@ -130,19 +132,15 @@ impl KeystoreService {
fn get_key_entry(&self, key: &KeyDescriptor) -> Result<KeyEntryResponse> {
let caller_uid = ThreadState::get_calling_uid();
-
- let super_key =
- SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(caller_uid));
-
let (key_id_guard, mut key_entry) = DB
.with(|db| {
- LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || {
+ LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
db.borrow_mut().load_key_entry(
- key,
+ &key,
KeyType::Client,
KeyEntryLoadBits::PUBLIC,
caller_uid,
- |k, av| check_key_permission(KeyPerm::GetInfo, k, &av),
+ |k, av| check_key_permission(KeyPerm::get_info(), k, &av),
)
})
})
@@ -186,18 +184,15 @@ impl KeystoreService {
certificate_chain: Option<&[u8]>,
) -> Result<()> {
let caller_uid = ThreadState::get_calling_uid();
- let super_key =
- SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(caller_uid));
-
DB.with::<_, Result<()>>(|db| {
- let entry = match LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || {
+ let entry = match LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
db.borrow_mut().load_key_entry(
- key,
+ &key,
KeyType::Client,
KeyEntryLoadBits::NONE,
caller_uid,
|k, av| {
- check_key_permission(KeyPerm::Update, k, &av)
+ check_key_permission(KeyPerm::update(), k, &av)
.context("In update_subcomponent.")
},
)
@@ -243,7 +238,7 @@ impl KeystoreService {
};
// Security critical: This must return on failure. Do not remove the `?`;
- check_key_permission(KeyPerm::Rebind, &key, &None)
+ check_key_permission(KeyPerm::rebind(), &key, &None)
.context("Caller does not have permission to insert this certificate.")?;
db.store_new_certificate(
@@ -276,32 +271,47 @@ impl KeystoreService {
// If the first check fails we check if the caller has the list permission allowing to list
// any namespace. In that case we also adjust the queried namespace if a specific uid was
// selected.
- if let Err(e) = check_key_permission(KeyPerm::GetInfo, &k, &None) {
- if let Some(selinux::Error::PermissionDenied) =
- e.root_cause().downcast_ref::<selinux::Error>() {
-
- check_keystore_permission(KeystorePerm::List)
- .context("In list_entries: While checking keystore permission.")?;
- if namespace != -1 {
- k.nspace = namespace;
+ match check_key_permission(KeyPerm::get_info(), &k, &None) {
+ Err(e) => {
+ if let Some(selinux::Error::PermissionDenied) =
+ e.root_cause().downcast_ref::<selinux::Error>()
+ {
+ check_keystore_permission(KeystorePerm::list())
+ .context("In list_entries: While checking keystore permission.")?;
+ if namespace != -1 {
+ k.nspace = namespace;
+ }
+ } else {
+ return Err(e).context("In list_entries: While checking key permission.")?;
}
- } else {
- return Err(e).context("In list_entries: While checking key permission.")?;
}
- }
+ Ok(()) => {}
+ };
- DB.with(|db| list_key_entries(&mut db.borrow_mut(), k.domain, k.nspace))
+ let mut result = LEGACY_MIGRATOR
+ .list_uid(k.domain, k.nspace)
+ .context("In list_entries: Trying to list legacy keys.")?;
+
+ result.append(
+ &mut DB
+ .with(|db| {
+ let mut db = db.borrow_mut();
+ db.list(k.domain, k.nspace, KeyType::Client)
+ })
+ .context("In list_entries: Trying to list keystore database.")?,
+ );
+
+ result.sort_unstable();
+ result.dedup();
+ Ok(result)
}
fn delete_key(&self, key: &KeyDescriptor) -> Result<()> {
let caller_uid = ThreadState::get_calling_uid();
- let super_key =
- SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(caller_uid));
-
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.")
+ LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
+ db.borrow_mut().unbind_key(&key, KeyType::Client, caller_uid, |k, av| {
+ check_key_permission(KeyPerm::delete(), k, &av).context("During delete_key.")
})
})
})
@@ -316,13 +326,10 @@ impl KeystoreService {
access_vector: permission::KeyPermSet,
) -> Result<KeyDescriptor> {
let caller_uid = ThreadState::get_calling_uid();
- let super_key =
- SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(caller_uid));
-
DB.with(|db| {
- LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || {
+ LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
db.borrow_mut().grant(
- key,
+ &key,
caller_uid,
grantee_uid as u32,
access_vector,
@@ -335,8 +342,8 @@ impl KeystoreService {
fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> Result<()> {
DB.with(|db| {
- db.borrow_mut().ungrant(key, ThreadState::get_calling_uid(), grantee_uid as u32, |k| {
- check_key_permission(KeyPerm::Grant, k, &None)
+ db.borrow_mut().ungrant(&key, ThreadState::get_calling_uid(), grantee_uid as u32, |k| {
+ check_key_permission(KeyPerm::grant(), k, &None)
})
})
.context("In KeystoreService::ungrant.")
@@ -351,13 +358,13 @@ impl IKeystoreService for KeystoreService {
fn getSecurityLevel(
&self,
security_level: SecurityLevel,
- ) -> binder::Result<Strong<dyn IKeystoreSecurityLevel>> {
+ ) -> binder::public_api::Result<Strong<dyn IKeystoreSecurityLevel>> {
let _wp = wd::watch_millis_with("IKeystoreService::getSecurityLevel", 500, move || {
format!("security_level: {}", security_level.0)
});
map_or_log_err(self.get_security_level(security_level), Ok)
}
- fn getKeyEntry(&self, key: &KeyDescriptor) -> binder::Result<KeyEntryResponse> {
+ fn getKeyEntry(&self, key: &KeyDescriptor) -> binder::public_api::Result<KeyEntryResponse> {
let _wp = wd::watch_millis("IKeystoreService::get_key_entry", 500);
map_or_log_err(self.get_key_entry(key), Ok)
}
@@ -366,15 +373,19 @@ impl IKeystoreService for KeystoreService {
key: &KeyDescriptor,
public_cert: Option<&[u8]>,
certificate_chain: Option<&[u8]>,
- ) -> binder::Result<()> {
+ ) -> binder::public_api::Result<()> {
let _wp = wd::watch_millis("IKeystoreService::updateSubcomponent", 500);
map_or_log_err(self.update_subcomponent(key, public_cert, certificate_chain), Ok)
}
- fn listEntries(&self, domain: Domain, namespace: i64) -> binder::Result<Vec<KeyDescriptor>> {
+ fn listEntries(
+ &self,
+ domain: Domain,
+ namespace: i64,
+ ) -> binder::public_api::Result<Vec<KeyDescriptor>> {
let _wp = wd::watch_millis("IKeystoreService::listEntries", 500);
map_or_log_err(self.list_entries(domain, namespace), Ok)
}
- fn deleteKey(&self, key: &KeyDescriptor) -> binder::Result<()> {
+ fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
let _wp = wd::watch_millis("IKeystoreService::deleteKey", 500);
let result = self.delete_key(key);
log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
@@ -385,11 +396,11 @@ impl IKeystoreService for KeystoreService {
key: &KeyDescriptor,
grantee_uid: i32,
access_vector: i32,
- ) -> binder::Result<KeyDescriptor> {
+ ) -> binder::public_api::Result<KeyDescriptor> {
let _wp = wd::watch_millis("IKeystoreService::grant", 500);
map_or_log_err(self.grant(key, grantee_uid, access_vector.into()), Ok)
}
- fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> binder::Result<()> {
+ fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> binder::public_api::Result<()> {
let _wp = wd::watch_millis("IKeystoreService::ungrant", 500);
map_or_log_err(self.ungrant(key, grantee_uid), Ok)
}
diff --git a/keystore2/src/shared_secret_negotiation.rs b/keystore2/src/shared_secret_negotiation.rs
index 42d38d29..64bc2c33 100644
--- a/keystore2/src/shared_secret_negotiation.rs
+++ b/keystore2/src/shared_secret_negotiation.rs
@@ -15,14 +15,13 @@
//! This module implements the shared secret negotiation.
use crate::error::{map_binder_status, map_binder_status_code, Error};
-use crate::globals::get_keymint_device;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
use android_hardware_security_keymint::binder::Strong;
use android_hardware_security_sharedsecret::aidl::android::hardware::security::sharedsecret::{
ISharedSecret::ISharedSecret, SharedSecretParameters::SharedSecretParameters,
};
use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
-use anyhow::Result;
+use anyhow::{Context, Result};
use keystore2_vintf::{get_aidl_instances, get_hidl_instances};
use std::fmt::{self, Display, Formatter};
use std::time::Duration;
@@ -44,10 +43,6 @@ pub fn perform_shared_secret_negotiation() {
let connected = connect_participants(participants);
negotiate_shared_secret(connected);
log::info!("Shared secret negotiation concluded successfully.");
-
- // Once shared secret negotiation is done, the StrongBox and TEE have a common key that
- // can be used to authenticate a possible RootOfTrust transfer.
- transfer_root_of_trust();
});
}
@@ -123,32 +118,47 @@ fn list_participants() -> Result<Vec<SharedSecretParticipant>> {
.iter()
.map(|(ma, mi)| {
get_hidl_instances(KEYMASTER_PACKAGE_NAME, *ma, *mi, KEYMASTER_INTERFACE_NAME)
- .into_iter()
- .filter_map(|name| {
- filter_map_legacy_km_instances(name, (*ma, *mi)).and_then(|sp| {
- if let SharedSecretParticipant::Hidl { is_strongbox: true, .. } = &sp {
- if !legacy_strongbox_found {
- legacy_strongbox_found = true;
- return Some(sp);
- }
- } else if !legacy_default_found {
- legacy_default_found = true;
- return Some(sp);
- }
- None
- })
+ .as_vec()
+ .with_context(|| format!("Trying to convert KM{}.{} names to vector.", *ma, *mi))
+ .map(|instances| {
+ instances
+ .into_iter()
+ .filter_map(|name| {
+ filter_map_legacy_km_instances(name.to_string(), (*ma, *mi)).and_then(
+ |sp| {
+ if let SharedSecretParticipant::Hidl {
+ is_strongbox: true,
+ ..
+ } = &sp
+ {
+ if !legacy_strongbox_found {
+ legacy_strongbox_found = true;
+ return Some(sp);
+ }
+ } else if !legacy_default_found {
+ legacy_default_found = true;
+ return Some(sp);
+ }
+ None
+ },
+ )
+ })
+ .collect::<Vec<SharedSecretParticipant>>()
})
- .collect::<Vec<SharedSecretParticipant>>()
})
- .into_iter()
- .flatten()
- .chain({
- get_aidl_instances(SHARED_SECRET_PACKAGE_NAME, 1, SHARED_SECRET_INTERFACE_NAME)
- .into_iter()
- .map(SharedSecretParticipant::Aidl)
- .collect::<Vec<_>>()
- .into_iter()
+ .collect::<Result<Vec<_>>>()
+ .map(|v| v.into_iter().flatten())
+ .and_then(|i| {
+ let participants_aidl: Vec<SharedSecretParticipant> =
+ get_aidl_instances(SHARED_SECRET_PACKAGE_NAME, 1, SHARED_SECRET_INTERFACE_NAME)
+ .as_vec()
+ .context("In list_participants: Trying to convert KM1.0 names to vector.")?
+ .into_iter()
+ .map(|name| SharedSecretParticipant::Aidl(name.to_string()))
+ .collect();
+ Ok(i.chain(participants_aidl.into_iter()))
})
+ .context("In list_participants.")?
.collect())
}
@@ -283,48 +293,3 @@ fn negotiate_shared_secret(
}
}
}
-
-/// Perform RootOfTrust transfer from TEE to StrongBox (if available).
-pub fn transfer_root_of_trust() {
- let strongbox = match get_keymint_device(&SecurityLevel::STRONGBOX) {
- Ok((s, _, _)) => s,
- Err(_e) => {
- log::info!("No StrongBox Keymint available, so no RoT transfer");
- return;
- }
- };
- // Ask the StrongBox KeyMint for a challenge.
- let challenge = match strongbox.getRootOfTrustChallenge() {
- Ok(data) => data,
- Err(e) => {
- // If StrongBox doesn't provide a challenge, it might be because:
- // - it already has RootOfTrust information
- // - it's a KeyMint v1 implementation that doesn't understand the method.
- // In either case, we're done.
- log::info!("StrongBox does not provide a challenge, so no RoT transfer: {:?}", e);
- return;
- }
- };
- // Get the RoT info from the TEE
- let tee = match get_keymint_device(&SecurityLevel::TRUSTED_ENVIRONMENT) {
- Ok((s, _, _)) => s,
- Err(e) => {
- log::error!("No TEE KeyMint implementation found! {:?}", e);
- return;
- }
- };
- let root_of_trust = match tee.getRootOfTrust(&challenge) {
- Ok(rot) => rot,
- Err(e) => {
- log::error!("TEE KeyMint failed to return RootOfTrust info: {:?}", e);
- return;
- }
- };
- // The RootOfTrust information is CBOR-serialized data, but we don't need to parse it.
- // Just pass it on to the StrongBox KeyMint instance.
- let result = strongbox.sendRootOfTrust(&root_of_trust);
- if let Err(e) = result {
- log::error!("Failed to send RootOfTrust to StrongBox: {:?}", e);
- }
- log::info!("RootOfTrust transfer process complete");
-}
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 74e3e560..e02d9bcf 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -26,9 +26,11 @@ use crate::{
error::ResponseCode,
key_parameter::{KeyParameter, KeyParameterValue},
legacy_blob::LegacyBlobLoader,
- legacy_importer::LegacyImporter,
+ legacy_migrator::LegacyMigrator,
raw_device::KeyMintDevice,
- utils::{watchdog as wd, AesGcm, AID_KEYSTORE},
+ try_insert::TryInsert,
+ utils::watchdog as wd,
+ utils::AID_KEYSTORE,
};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, BlockMode::BlockMode, HardwareAuthToken::HardwareAuthToken,
@@ -44,11 +46,11 @@ use keystore2_crypto::{
aes_gcm_decrypt, aes_gcm_encrypt, generate_aes256_key, generate_salt, Password, ZVec,
AES_256_KEY_LENGTH,
};
-use rustutils::system_properties::PropertyWatcher;
+use keystore2_system_property::PropertyWatcher;
use std::{
collections::HashMap,
sync::Arc,
- sync::{Mutex, RwLock, Weak},
+ sync::{Mutex, Weak},
};
use std::{convert::TryFrom, ops::Deref};
@@ -73,9 +75,9 @@ pub enum SuperEncryptionAlgorithm {
/// A particular user may have several superencryption keys in the database, each for a
/// different purpose, distinguished by alias. Each is associated with a static
/// constant of this type.
-pub struct SuperKeyType<'a> {
- /// Alias used to look up the key in the `persistent.keyentry` table.
- pub alias: &'a str,
+pub struct SuperKeyType {
+ /// Alias used to look the key up in the `persistent.keyentry` table.
+ pub alias: &'static str,
/// Encryption algorithm
pub algorithm: SuperEncryptionAlgorithm,
}
@@ -124,8 +126,10 @@ impl SuperKeyIdentifier {
fn from_metadata(metadata: &BlobMetaData) -> Option<Self> {
if let Some(EncryptedBy::KeyId(key_id)) = metadata.encrypted_by() {
Some(SuperKeyIdentifier::DatabaseId(*key_id))
+ } else if let Some(boot_level) = metadata.max_boot_level() {
+ Some(SuperKeyIdentifier::BootLevel(*boot_level))
} else {
- metadata.max_boot_level().map(|boot_level| SuperKeyIdentifier::BootLevel(*boot_level))
+ None
}
}
@@ -153,22 +157,15 @@ pub struct SuperKey {
reencrypt_with: Option<Arc<SuperKey>>,
}
-impl AesGcm for SuperKey {
- fn decrypt(&self, data: &[u8], iv: &[u8], tag: &[u8]) -> Result<ZVec> {
+impl SuperKey {
+ /// For most purposes `unwrap_key` handles decryption,
+ /// but legacy handling and some tests need to assume AES and decrypt directly.
+ pub fn aes_gcm_decrypt(&self, data: &[u8], iv: &[u8], tag: &[u8]) -> Result<ZVec> {
if self.algorithm == SuperEncryptionAlgorithm::Aes256Gcm {
aes_gcm_decrypt(data, iv, tag, &self.key)
- .context("In SuperKey::decrypt: Decryption failed.")
+ .context("In aes_gcm_decrypt: decryption failed")
} else {
- Err(Error::sys()).context("In SuperKey::decrypt: Key is not an AES key.")
- }
- }
-
- fn encrypt(&self, plaintext: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>)> {
- if self.algorithm == SuperEncryptionAlgorithm::Aes256Gcm {
- aes_gcm_encrypt(plaintext, &self.key)
- .context("In SuperKey::encrypt: Encryption failed.")
- } else {
- Err(Error::sys()).context("In SuperKey::encrypt: Key is not an AES key.")
+ Err(Error::sys()).context("In aes_gcm_decrypt: Key is not an AES key")
}
}
}
@@ -261,7 +258,7 @@ struct UserSuperKeys {
struct SkmState {
user_keys: HashMap<UserId, UserSuperKeys>,
key_index: HashMap<i64, Weak<SuperKey>>,
- boot_level_key_cache: Option<Mutex<BootLevelKeyCache>>,
+ boot_level_key_cache: Option<BootLevelKeyCache>,
}
impl SkmState {
@@ -280,24 +277,24 @@ impl SkmState {
#[derive(Default)]
pub struct SuperKeyManager {
- data: SkmState,
+ data: Mutex<SkmState>,
}
impl SuperKeyManager {
- pub fn set_up_boot_level_cache(skm: &Arc<RwLock<Self>>, db: &mut KeystoreDB) -> Result<()> {
- let mut skm_guard = skm.write().unwrap();
- if skm_guard.data.boot_level_key_cache.is_some() {
+ pub fn set_up_boot_level_cache(self: &Arc<Self>, db: &mut KeystoreDB) -> Result<()> {
+ let mut data = self.data.lock().unwrap();
+ if data.boot_level_key_cache.is_some() {
log::info!("In set_up_boot_level_cache: called for a second time");
return Ok(());
}
let level_zero_key = get_level_zero_key(db)
.context("In set_up_boot_level_cache: get_level_zero_key failed")?;
- skm_guard.data.boot_level_key_cache =
- Some(Mutex::new(BootLevelKeyCache::new(level_zero_key)));
+ data.boot_level_key_cache = Some(BootLevelKeyCache::new(level_zero_key));
log::info!("Starting boot level watcher.");
- let clone = skm.clone();
+ let clone = self.clone();
std::thread::spawn(move || {
- Self::watch_boot_level(clone)
+ clone
+ .watch_boot_level()
.unwrap_or_else(|e| log::error!("watch_boot_level failed:\n{:?}", e));
});
Ok(())
@@ -305,40 +302,32 @@ impl SuperKeyManager {
/// Watch the `keystore.boot_level` system property, and keep boot level up to date.
/// Blocks waiting for system property changes, so must be run in its own thread.
- fn watch_boot_level(skm: Arc<RwLock<Self>>) -> Result<()> {
+ fn watch_boot_level(&self) -> Result<()> {
let mut w = PropertyWatcher::new("keystore.boot_level")
.context("In watch_boot_level: PropertyWatcher::new failed")?;
loop {
let level = w
.read(|_n, v| v.parse::<usize>().map_err(std::convert::Into::into))
.context("In watch_boot_level: read of property failed")?;
-
- // This scope limits the skm_guard life, so we don't hold the skm_guard while
- // waiting.
- {
- let mut skm_guard = skm.write().unwrap();
- let boot_level_key_cache = skm_guard
- .data
- .boot_level_key_cache
+ // watch_boot_level should only be called once data.boot_level_key_cache is Some,
+ // so it's safe to unwrap in the branches below.
+ if level < MAX_MAX_BOOT_LEVEL {
+ log::info!("Read keystore.boot_level value {}", level);
+ let mut data = self.data.lock().unwrap();
+ data.boot_level_key_cache
.as_mut()
- .ok_or_else(Error::sys)
- .context("In watch_boot_level: Boot level cache not initialized")?
- .get_mut()
- .unwrap();
- if level < MAX_MAX_BOOT_LEVEL {
- log::info!("Read keystore.boot_level value {}", level);
- boot_level_key_cache
- .advance_boot_level(level)
- .context("In watch_boot_level: advance_boot_level failed")?;
- } else {
- log::info!(
- "keystore.boot_level {} hits maximum {}, finishing.",
- level,
- MAX_MAX_BOOT_LEVEL
- );
- boot_level_key_cache.finish();
- break;
- }
+ .unwrap()
+ .advance_boot_level(level)
+ .context("In watch_boot_level: advance_boot_level failed")?;
+ } else {
+ log::info!(
+ "keystore.boot_level {} hits maximum {}, finishing.",
+ level,
+ MAX_MAX_BOOT_LEVEL
+ );
+ let mut data = self.data.lock().unwrap();
+ data.boot_level_key_cache.as_mut().unwrap().finish();
+ break;
}
w.wait().context("In watch_boot_level: property wait failed")?;
}
@@ -347,37 +336,34 @@ impl SuperKeyManager {
pub fn level_accessible(&self, boot_level: i32) -> bool {
self.data
+ .lock()
+ .unwrap()
.boot_level_key_cache
.as_ref()
- .map_or(false, |c| c.lock().unwrap().level_accessible(boot_level as usize))
+ .map_or(false, |c| c.level_accessible(boot_level as usize))
}
- pub fn forget_all_keys_for_user(&mut self, user: UserId) {
- self.data.user_keys.remove(&user);
+ pub fn forget_all_keys_for_user(&self, user: UserId) {
+ let mut data = self.data.lock().unwrap();
+ data.user_keys.remove(&user);
}
- fn install_per_boot_key_for_user(
- &mut self,
- user: UserId,
- super_key: Arc<SuperKey>,
- ) -> Result<()> {
- self.data
- .add_key_to_key_index(&super_key)
+ fn install_per_boot_key_for_user(&self, user: UserId, super_key: Arc<SuperKey>) -> Result<()> {
+ let mut data = self.data.lock().unwrap();
+ data.add_key_to_key_index(&super_key)
.context("In install_per_boot_key_for_user: add_key_to_key_index failed")?;
- self.data.user_keys.entry(user).or_default().per_boot = Some(super_key);
+ data.user_keys.entry(user).or_default().per_boot = Some(super_key);
Ok(())
}
fn lookup_key(&self, key_id: &SuperKeyIdentifier) -> Result<Option<Arc<SuperKey>>> {
+ let mut data = self.data.lock().unwrap();
Ok(match key_id {
- SuperKeyIdentifier::DatabaseId(id) => {
- self.data.key_index.get(id).and_then(|k| k.upgrade())
- }
- SuperKeyIdentifier::BootLevel(level) => self
- .data
+ SuperKeyIdentifier::DatabaseId(id) => data.key_index.get(id).and_then(|k| k.upgrade()),
+ SuperKeyIdentifier::BootLevel(level) => data
.boot_level_key_cache
- .as_ref()
- .map(|b| b.lock().unwrap().aes_key(*level as usize))
+ .as_mut()
+ .map(|b| b.aes_key(*level as usize))
.transpose()
.context("In lookup_key: aes_key failed")?
.flatten()
@@ -392,16 +378,9 @@ impl SuperKeyManager {
})
}
- pub fn get_per_boot_key_by_user_id(
- &self,
- user_id: UserId,
- ) -> Option<Arc<dyn AesGcm + Send + Sync>> {
- self.get_per_boot_key_by_user_id_internal(user_id)
- .map(|sk| -> Arc<dyn AesGcm + Send + Sync> { sk })
- }
-
- fn get_per_boot_key_by_user_id_internal(&self, user_id: UserId) -> Option<Arc<SuperKey>> {
- self.data.user_keys.get(&user_id).and_then(|e| e.per_boot.as_ref().cloned())
+ pub fn get_per_boot_key_by_user_id(&self, user_id: UserId) -> Option<Arc<SuperKey>> {
+ let data = self.data.lock().unwrap();
+ data.user_keys.get(&user_id).and_then(|e| e.per_boot.as_ref().cloned())
}
/// This function unlocks the super keys for a given user.
@@ -409,7 +388,7 @@ impl SuperKeyManager {
/// super key cache. If there is no such key a new key is created, encrypted with
/// a key derived from the given password and stored in the database.
pub fn unlock_user_key(
- &mut self,
+ &self,
db: &mut KeystoreDB,
user: UserId,
pw: &Password,
@@ -419,7 +398,7 @@ impl SuperKeyManager {
.get_or_create_key_with(
Domain::APP,
user as u64 as i64,
- USER_SUPER_KEY.alias,
+ &USER_SUPER_KEY.alias,
crate::database::KEYSTORE_UUID,
|| {
// For backward compatibility we need to check if there is a super key present.
@@ -478,7 +457,7 @@ impl SuperKeyManager {
match key.algorithm {
SuperEncryptionAlgorithm::Aes256Gcm => match (metadata.iv(), metadata.aead_tag()) {
(Some(iv), Some(tag)) => key
- .decrypt(blob, iv, tag)
+ .aes_gcm_decrypt(blob, iv, tag)
.context("In unwrap_key_with_key: Failed to decrypt the key blob."),
(iv, tag) => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
concat!(
@@ -516,22 +495,19 @@ impl SuperKeyManager {
}
/// Checks if user has setup LSKF, even when super key cache is empty for the user.
- /// The reference to self is unused but it is required to prevent calling this function
- /// concurrently with skm state database changes.
- fn super_key_exists_in_db_for_user(
- &self,
+ pub fn super_key_exists_in_db_for_user(
db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
+ legacy_migrator: &LegacyMigrator,
user_id: UserId,
) -> Result<bool> {
let key_in_db = db
- .key_exists(Domain::APP, user_id as u64 as i64, USER_SUPER_KEY.alias, KeyType::Super)
+ .key_exists(Domain::APP, user_id as u64 as i64, &USER_SUPER_KEY.alias, KeyType::Super)
.context("In super_key_exists_in_db_for_user.")?;
if key_in_db {
Ok(key_in_db)
} else {
- legacy_importer
+ legacy_migrator
.has_super_key(user_id)
.context("In super_key_exists_in_db_for_user: Trying to query legacy db.")
}
@@ -541,15 +517,15 @@ impl SuperKeyManager {
/// legacy database). If not, return Uninitialized state.
/// Otherwise, decrypt the super key from the password and return LskfUnlocked state.
pub fn check_and_unlock_super_key(
- &mut self,
+ &self,
db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
+ legacy_migrator: &LegacyMigrator,
user_id: UserId,
pw: &Password,
) -> Result<UserState> {
let alias = &USER_SUPER_KEY;
- let result = legacy_importer
- .with_try_import_super_key(user_id, pw, || db.load_super_key(alias, user_id))
+ let result = legacy_migrator
+ .with_try_migrate_super_key(user_id, pw, || db.load_super_key(alias, user_id))
.context("In check_and_unlock_super_key. Failed to load super key")?;
match result {
@@ -570,23 +546,24 @@ impl SuperKeyManager {
/// and return LskfUnlocked state.
/// If the password is not provided, return Uninitialized state.
pub fn check_and_initialize_super_key(
- &mut self,
+ &self,
db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
+ legacy_migrator: &LegacyMigrator,
user_id: UserId,
pw: Option<&Password>,
) -> Result<UserState> {
- let super_key_exists_in_db = self
- .super_key_exists_in_db_for_user(db, legacy_importer, user_id)
- .context("In check_and_initialize_super_key. Failed to check if super key exists.")?;
+ let super_key_exists_in_db =
+ Self::super_key_exists_in_db_for_user(db, legacy_migrator, user_id).context(
+ "In check_and_initialize_super_key. Failed to check if super key exists.",
+ )?;
if super_key_exists_in_db {
Ok(UserState::LskfLocked)
} else if let Some(pw) = pw {
- // Generate a new super key.
+ //generate a new super key.
let super_key = generate_aes256_key()
.context("In check_and_initialize_super_key: Failed to generate AES 256 key.")?;
- // Derive an AES256 key from the password and re-encrypt the super key
- // before we insert it in the database.
+ //derive an AES256 key from the password and re-encrypt the super key
+ //before we insert it in the database.
let (encrypted_super_key, blob_metadata) = Self::encrypt_with_password(&super_key, pw)
.context("In check_and_initialize_super_key.")?;
@@ -614,9 +591,9 @@ impl SuperKeyManager {
}
}
- // Helper function to populate super key cache from the super key blob loaded from the database.
+ //helper function to populate super key cache from the super key blob loaded from the database
fn populate_cache_from_super_key_blob(
- &mut self,
+ &self,
user_id: UserId,
algorithm: SuperEncryptionAlgorithm,
entry: KeyEntry,
@@ -630,7 +607,7 @@ impl SuperKeyManager {
Ok(super_key)
}
- /// Extracts super key from the entry loaded from the database.
+ /// Extracts super key from the entry loaded from the database
pub fn extract_super_key_from_key_entry(
algorithm: SuperEncryptionAlgorithm,
entry: KeyEntry,
@@ -645,7 +622,7 @@ impl SuperKeyManager {
metadata.aead_tag(),
) {
(Some(&EncryptedBy::Password), Some(salt), Some(iv), Some(tag)) => {
- // Note that password encryption is AES no matter the value of algorithm.
+ // Note that password encryption is AES no matter the value of algorithm
let key = pw.derive_key(Some(salt), AES_256_KEY_LENGTH).context(
"In extract_super_key_from_key_entry: Failed to generate key from password.",
)?;
@@ -705,12 +682,11 @@ impl SuperKeyManager {
fn super_encrypt_on_key_init(
&self,
db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
+ legacy_migrator: &LegacyMigrator,
user_id: UserId,
key_blob: &[u8],
) -> Result<(Vec<u8>, BlobMetaData)> {
- match self
- .get_user_state(db, legacy_importer, user_id)
+ match UserState::get(db, legacy_migrator, self, user_id)
.context("In super_encrypt. Failed to get user state.")?
{
UserState::LskfUnlocked(super_key) => {
@@ -725,9 +701,9 @@ impl SuperKeyManager {
}
}
- // Helper function to encrypt a key with the given super key. Callers should select which super
- // key to be used. This is called when a key is super encrypted at its creation as well as at
- // its upgrade.
+ //Helper function to encrypt a key with the given super key. Callers should select which super
+ //key to be used. This is called when a key is super encrypted at its creation as well as at its
+ //upgrade.
fn encrypt_with_aes_super_key(
key_blob: &[u8],
super_key: &SuperKey,
@@ -751,7 +727,7 @@ impl SuperKeyManager {
pub fn handle_super_encryption_on_key_init(
&self,
db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
+ legacy_migrator: &LegacyMigrator,
domain: &Domain,
key_parameters: &[KeyParameter],
flags: Option<i32>,
@@ -761,16 +737,16 @@ impl SuperKeyManager {
match Enforcements::super_encryption_required(domain, key_parameters, flags) {
SuperEncryptionType::None => Ok((key_blob.to_vec(), BlobMetaData::new())),
SuperEncryptionType::LskfBound => self
- .super_encrypt_on_key_init(db, legacy_importer, user_id, key_blob)
+ .super_encrypt_on_key_init(db, legacy_migrator, user_id, &key_blob)
.context(concat!(
"In handle_super_encryption_on_key_init. ",
"Failed to super encrypt with LskfBound key."
)),
SuperEncryptionType::ScreenLockBound => {
- let entry =
- self.data.user_keys.get(&user_id).and_then(|e| e.screen_lock_bound.as_ref());
- if let Some(super_key) = entry {
- Self::encrypt_with_aes_super_key(key_blob, super_key).context(concat!(
+ let mut data = self.data.lock().unwrap();
+ let entry = data.user_keys.entry(user_id).or_default();
+ if let Some(super_key) = entry.screen_lock_bound.as_ref() {
+ Self::encrypt_with_aes_super_key(key_blob, &super_key).context(concat!(
"In handle_super_encryption_on_key_init. ",
"Failed to encrypt with ScreenLockBound key."
))
@@ -839,7 +815,6 @@ impl SuperKeyManager {
/// When this is called, the caller must hold the lock on the SuperKeyManager.
/// So it's OK that the check and creation are different DB transactions.
fn get_or_create_super_key(
- &mut self,
db: &mut KeystoreDB,
user_id: UserId,
key_type: &SuperKeyType,
@@ -874,8 +849,8 @@ impl SuperKeyManager {
)
}
};
- // Derive an AES256 key from the password and re-encrypt the super key
- // before we insert it in the database.
+ //derive an AES256 key from the password and re-encrypt the super key
+ //before we insert it in the database.
let (encrypted_super_key, blob_metadata) =
Self::encrypt_with_password(&super_key, password)
.context("In get_or_create_super_key.")?;
@@ -903,64 +878,52 @@ impl SuperKeyManager {
/// Decrypt the screen-lock bound keys for this user using the password and store in memory.
pub fn unlock_screen_lock_bound_key(
- &mut self,
+ &self,
db: &mut KeystoreDB,
user_id: UserId,
password: &Password,
) -> Result<()> {
- let (screen_lock_bound, screen_lock_bound_private) = self
- .data
- .user_keys
- .get(&user_id)
- .map(|e| (e.screen_lock_bound.clone(), e.screen_lock_bound_private.clone()))
- .unwrap_or((None, None));
-
- if screen_lock_bound.is_some() && screen_lock_bound_private.is_some() {
- // Already unlocked.
- return Ok(());
- }
-
- let aes = if let Some(screen_lock_bound) = screen_lock_bound {
- // This is weird. If this point is reached only one of the screen locked keys was
- // initialized. This should never happen.
- screen_lock_bound
- } else {
- self.get_or_create_super_key(db, user_id, &USER_SCREEN_LOCK_BOUND_KEY, password, None)
- .context("In unlock_screen_lock_bound_key: Trying to get or create symmetric key.")?
- };
-
- let ecdh = if let Some(screen_lock_bound_private) = screen_lock_bound_private {
- // This is weird. If this point is reached only one of the screen locked keys was
- // initialized. This should never happen.
- screen_lock_bound_private
- } else {
- self.get_or_create_super_key(
- db,
- user_id,
- &USER_SCREEN_LOCK_BOUND_P521_KEY,
- password,
- Some(aes.clone()),
- )
- .context("In unlock_screen_lock_bound_key: Trying to get or create asymmetric key.")?
- };
-
- self.data.add_key_to_key_index(&aes)?;
- self.data.add_key_to_key_index(&ecdh)?;
- let entry = self.data.user_keys.entry(user_id).or_default();
- entry.screen_lock_bound = Some(aes);
- entry.screen_lock_bound_private = Some(ecdh);
+ let mut data = self.data.lock().unwrap();
+ let entry = data.user_keys.entry(user_id).or_default();
+ let aes = entry
+ .screen_lock_bound
+ .get_or_try_to_insert_with(|| {
+ Self::get_or_create_super_key(
+ db,
+ user_id,
+ &USER_SCREEN_LOCK_BOUND_KEY,
+ password,
+ None,
+ )
+ })?
+ .clone();
+ let ecdh = entry
+ .screen_lock_bound_private
+ .get_or_try_to_insert_with(|| {
+ Self::get_or_create_super_key(
+ db,
+ user_id,
+ &USER_SCREEN_LOCK_BOUND_P521_KEY,
+ password,
+ Some(aes.clone()),
+ )
+ })?
+ .clone();
+ data.add_key_to_key_index(&aes)?;
+ data.add_key_to_key_index(&ecdh)?;
Ok(())
}
/// Wipe the screen-lock bound keys for this user from memory.
pub fn lock_screen_lock_bound_key(
- &mut self,
+ &self,
db: &mut KeystoreDB,
user_id: UserId,
unlocking_sids: &[i64],
) {
log::info!("Locking screen bound for user {} sids {:?}", user_id, unlocking_sids);
- let mut entry = self.data.user_keys.entry(user_id).or_default();
+ let mut data = self.data.lock().unwrap();
+ let mut entry = data.user_keys.entry(user_id).or_default();
if !unlocking_sids.is_empty() {
if let (Some(aes), Some(ecdh)) = (
entry.screen_lock_bound.as_ref().cloned(),
@@ -1032,11 +995,12 @@ impl SuperKeyManager {
/// User has unlocked, not using a password. See if any of our stored auth tokens can be used
/// to unlock the keys protecting UNLOCKED_DEVICE_REQUIRED keys.
pub fn try_unlock_user_with_biometric(
- &mut self,
+ &self,
db: &mut KeystoreDB,
user_id: UserId,
) -> Result<()> {
- let mut entry = self.data.user_keys.entry(user_id).or_default();
+ let mut data = self.data.lock().unwrap();
+ let mut entry = data.user_keys.entry(user_id).or_default();
if let Some(biometric) = entry.biometric_unlock.as_ref() {
let (key_id_guard, key_entry) = db
.load_key_entry(
@@ -1076,8 +1040,8 @@ impl SuperKeyManager {
Ok((slb, slbp)) => {
entry.screen_lock_bound = Some(slb.clone());
entry.screen_lock_bound_private = Some(slbp.clone());
- self.data.add_key_to_key_index(&slb)?;
- self.data.add_key_to_key_index(&slbp)?;
+ data.add_key_to_key_index(&slb)?;
+ data.add_key_to_key_index(&slbp)?;
log::info!(concat!(
"In try_unlock_user_with_biometric: ",
"Successfully unlocked with biometric"
@@ -1093,24 +1057,37 @@ impl SuperKeyManager {
}
Ok(())
}
+}
- /// Returns the keystore locked state of the given user. It requires the thread local
- /// keystore database and a reference to the legacy migrator because it may need to
- /// import the super key from the legacy blob database to the keystore database.
- pub fn get_user_state(
- &self,
+/// This enum represents different states of the user's life cycle in the device.
+/// For now, only three states are defined. More states may be added later.
+pub enum UserState {
+ // The user has registered LSKF and has unlocked the device by entering PIN/Password,
+ // and hence the per-boot super key is available in the cache.
+ LskfUnlocked(Arc<SuperKey>),
+ // The user has registered LSKF, but has not unlocked the device using password, after reboot.
+ // Hence the per-boot super-key(s) is not available in the cache.
+ // However, the encrypted super key is available in the database.
+ LskfLocked,
+ // There's no user in the device for the given user id, or the user with the user id has not
+ // setup LSKF.
+ Uninitialized,
+}
+
+impl UserState {
+ pub fn get(
db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
+ legacy_migrator: &LegacyMigrator,
+ skm: &SuperKeyManager,
user_id: UserId,
) -> Result<UserState> {
- match self.get_per_boot_key_by_user_id_internal(user_id) {
+ match skm.get_per_boot_key_by_user_id(user_id) {
Some(super_key) => Ok(UserState::LskfUnlocked(super_key)),
None => {
- // Check if a super key exists in the database or legacy database.
- // If so, return locked user state.
- if self
- .super_key_exists_in_db_for_user(db, legacy_importer, user_id)
- .context("In get_user_state.")?
+ //Check if a super key exists in the database or legacy database.
+ //If so, return locked user state.
+ if SuperKeyManager::super_key_exists_in_db_for_user(db, legacy_migrator, user_id)
+ .context("In get.")?
{
Ok(UserState::LskfLocked)
} else {
@@ -1120,70 +1097,60 @@ impl SuperKeyManager {
}
}
- /// If the given user is unlocked:
- /// * and `password` is None, the user is reset, all authentication bound keys are deleted and
- /// `Ok(UserState::Uninitialized)` is returned.
- /// * and `password` is Some, `Ok(UserState::LskfUnlocked)` is returned.
- /// If the given user is locked:
- /// * and the user was initialized before, `Ok(UserState::Locked)` is returned.
- /// * and the user was not initialized before:
- /// * and `password` is None, `Ok(Uninitialized)` is returned.
- /// * and `password` is Some, super keys are generated and `Ok(UserState::LskfUnlocked)` is
- /// returned.
- pub fn reset_or_init_user_and_get_user_state(
- &mut self,
+ /// Queries user state when serving password change requests.
+ pub fn get_with_password_changed(
db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
+ legacy_migrator: &LegacyMigrator,
+ skm: &SuperKeyManager,
user_id: UserId,
password: Option<&Password>,
) -> Result<UserState> {
- match self.get_per_boot_key_by_user_id_internal(user_id) {
- Some(_) if password.is_none() => {
- // Transitioning to swiping, delete only the super key in database and cache,
- // and super-encrypted keys in database (and in KM).
- self.reset_user(db, legacy_importer, user_id, true).context(
- "In reset_or_init_user_and_get_user_state: Trying to delete keys from the db.",
- )?;
- // Lskf is now removed in Keystore.
- Ok(UserState::Uninitialized)
- }
+ match skm.get_per_boot_key_by_user_id(user_id) {
Some(super_key) => {
- // Keystore won't be notified when changing to a new password when LSKF is
- // already setup. Therefore, ideally this path wouldn't be reached.
- Ok(UserState::LskfUnlocked(super_key))
+ if password.is_none() {
+ //transitioning to swiping, delete only the super key in database and cache, and
+ //super-encrypted keys in database (and in KM)
+ Self::reset_user(db, skm, legacy_migrator, user_id, true).context(
+ "In get_with_password_changed: Trying to delete keys from the db.",
+ )?;
+ //Lskf is now removed in Keystore
+ Ok(UserState::Uninitialized)
+ } else {
+ //Keystore won't be notified when changing to a new password when LSKF is
+ //already setup. Therefore, ideally this path wouldn't be reached.
+ Ok(UserState::LskfUnlocked(super_key))
+ }
}
None => {
- // Check if a super key exists in the database or legacy database.
- // If so, return LskfLocked state.
- // Otherwise, i) if the password is provided, initialize the super key and return
- // LskfUnlocked state ii) if password is not provided, return Uninitialized state.
- self.check_and_initialize_super_key(db, legacy_importer, user_id, password)
+ //Check if a super key exists in the database or legacy database.
+ //If so, return LskfLocked state.
+ //Otherwise, i) if the password is provided, initialize the super key and return
+ //LskfUnlocked state ii) if password is not provided, return Uninitialized state.
+ skm.check_and_initialize_super_key(db, legacy_migrator, user_id, password)
}
}
}
- /// Unlocks the given user with the given password. If the key was already unlocked or unlocking
- /// was successful, `Ok(UserState::LskfUnlocked)` is returned.
- /// If the user was never initialized `Ok(UserState::Uninitialized)` is returned.
- pub fn unlock_and_get_user_state(
- &mut self,
+ /// Queries user state when serving password unlock requests.
+ pub fn get_with_password_unlock(
db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
+ legacy_migrator: &LegacyMigrator,
+ skm: &SuperKeyManager,
user_id: UserId,
password: &Password,
) -> Result<UserState> {
- match self.get_per_boot_key_by_user_id_internal(user_id) {
+ match skm.get_per_boot_key_by_user_id(user_id) {
Some(super_key) => {
- log::info!("In unlock_and_get_user_state. Trying to unlock when already unlocked.");
+ log::info!("In get_with_password_unlock. Trying to unlock when already unlocked.");
Ok(UserState::LskfUnlocked(super_key))
}
None => {
- // Check if a super key exists in the database or legacy database.
- // If not, return Uninitialized state.
- // Otherwise, try to unlock the super key and if successful,
- // return LskfUnlocked.
- self.check_and_unlock_super_key(db, legacy_importer, user_id, password)
- .context("In unlock_and_get_user_state. Failed to unlock super key.")
+ //Check if a super key exists in the database or legacy database.
+ //If not, return Uninitialized state.
+ //Otherwise, try to unlock the super key and if successful,
+ //return LskfUnlocked state
+ skm.check_and_unlock_super_key(db, legacy_migrator, user_id, password)
+ .context("In get_with_password_unlock. Failed to unlock super key.")
}
}
}
@@ -1192,40 +1159,25 @@ impl SuperKeyManager {
/// If 'keep_non_super_encrypted_keys' is set to true, delete only the super key and super
/// encrypted keys.
pub fn reset_user(
- &mut self,
db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
+ skm: &SuperKeyManager,
+ legacy_migrator: &LegacyMigrator,
user_id: UserId,
keep_non_super_encrypted_keys: bool,
) -> Result<()> {
- // Mark keys created on behalf of the user as unreferenced.
- legacy_importer
+ // mark keys created on behalf of the user as unreferenced.
+ legacy_migrator
.bulk_delete_user(user_id, keep_non_super_encrypted_keys)
.context("In reset_user: Trying to delete legacy keys.")?;
db.unbind_keys_for_user(user_id, keep_non_super_encrypted_keys)
.context("In reset user. Error in unbinding keys.")?;
- // Delete super key in cache, if exists.
- self.forget_all_keys_for_user(user_id);
+ //delete super key in cache, if exists
+ skm.forget_all_keys_for_user(user_id);
Ok(())
}
}
-/// This enum represents different states of the user's life cycle in the device.
-/// For now, only three states are defined. More states may be added later.
-pub enum UserState {
- // The user has registered LSKF and has unlocked the device by entering PIN/Password,
- // and hence the per-boot super key is available in the cache.
- LskfUnlocked(Arc<SuperKey>),
- // The user has registered LSKF, but has not unlocked the device using password, after reboot.
- // Hence the per-boot super-key(s) is not available in the cache.
- // However, the encrypted super key is available in the database.
- LskfLocked,
- // There's no user in the device for the given user id, or the user with the user id has not
- // setup LSKF.
- Uninitialized,
-}
-
/// This enum represents three states a KeyMint Blob can be in, w.r.t super encryption.
/// `Sensitive` holds the non encrypted key and a reference to its super key.
/// `NonSensitive` holds a non encrypted key that is never supposed to be encrypted.
@@ -1263,8 +1215,8 @@ impl<'a> Deref for KeyBlob<'a> {
fn deref(&self) -> &Self::Target {
match self {
- Self::Sensitive { key, .. } => key,
- Self::NonSensitive(key) => key,
+ Self::Sensitive { key, .. } => &key,
+ Self::NonSensitive(key) => &key,
Self::Ref(key) => key,
}
}
diff --git a/keystore2/src/try_insert.rs b/keystore2/src/try_insert.rs
new file mode 100644
index 00000000..6dd39620
--- /dev/null
+++ b/keystore2/src/try_insert.rs
@@ -0,0 +1,100 @@
+// 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.
+
+//! The TryInsert trait adds to Option<T> the method
+//! get_or_try_to_insert_with, which is analogous to
+//! get_or_insert_with, but allows the called function to fail and propagates the failure.
+
+/// The TryInsert trait adds to Option<T> the method
+/// get_or_try_to_insert_with, which is analogous to
+/// get_or_insert_with, but allows the called function to fail and propagates the failure.
+pub trait TryInsert {
+ /// Type of the Ok branch of the Result
+ type Item;
+ /// Inserts a value computed from `f` into the option if it is [`None`],
+ /// then returns a mutable reference to the contained value. If `f`
+ /// returns Err, the Option is unchanged.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let mut x = None;
+ /// assert_eq!(x.get_or_try_to_insert_with(Err("oops".to_string())), Err("oops".to_string()))
+ /// {
+ /// let y: &mut u32 = x.get_or_try_to_insert_with(|| Ok(5))?;
+ /// assert_eq!(y, &5);
+ ///
+ /// *y = 7;
+ /// }
+ ///
+ /// assert_eq!(x, Some(7));
+ /// ```
+ fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
+ &mut self,
+ f: F,
+ ) -> Result<&mut Self::Item, E>;
+}
+
+impl<T> TryInsert for Option<T> {
+ type Item = T;
+ fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
+ &mut self,
+ f: F,
+ ) -> Result<&mut Self::Item, E> {
+ if self.is_none() {
+ *self = Some(f()?);
+ }
+
+ match self {
+ Some(v) => Ok(v),
+ // SAFETY: a `None` variant for `self` would have been replaced by a `Some`
+ // variant in the code above.
+ None => unsafe { std::hint::unreachable_unchecked() },
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ fn fails() -> Result<i32, String> {
+ Err("fail".to_string())
+ }
+
+ fn succeeds() -> Result<i32, String> {
+ Ok(99)
+ }
+
+ #[test]
+ fn test() {
+ let mut x = None;
+ assert_eq!(x.get_or_try_to_insert_with(fails), Err("fail".to_string()));
+ assert_eq!(x, None);
+ assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 99);
+ assert_eq!(x, Some(99));
+ x = Some(42);
+ assert_eq!(*x.get_or_try_to_insert_with(fails).unwrap(), 42);
+ assert_eq!(x, Some(42));
+ assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 42);
+ assert_eq!(x, Some(42));
+ *x.get_or_try_to_insert_with(fails).unwrap() = 2;
+ assert_eq!(x, Some(2));
+ *x.get_or_try_to_insert_with(succeeds).unwrap() = 3;
+ assert_eq!(x, Some(3));
+ x = None;
+ *x.get_or_try_to_insert_with(succeeds).unwrap() = 5;
+ assert_eq!(x, Some(5));
+ }
+}
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 9db2eb9d..a110c64e 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -15,17 +15,11 @@
//! This module implements utility functions used by the Keystore 2.0 service
//! implementation.
-use crate::error::{map_binder_status, map_km_error, Error, ErrorCode};
-use crate::key_parameter::KeyParameter;
+use crate::error::{map_binder_status, Error, ErrorCode};
use crate::permission;
use crate::permission::{KeyPerm, KeyPermSet, KeystorePerm};
-use crate::{
- database::{KeyType, KeystoreDB},
- globals::LEGACY_IMPORTER,
-};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- IKeyMintDevice::IKeyMintDevice, KeyCharacteristics::KeyCharacteristics,
- KeyParameter::KeyParameter as KmKeyParameter, Tag::Tag,
+ KeyCharacteristics::KeyCharacteristics, Tag::Tag,
};
use android_os_permissions_aidl::aidl::android::os::IPermissionController;
use android_security_apc::aidl::android::security::apc::{
@@ -33,17 +27,16 @@ use android_security_apc::aidl::android::security::apc::{
ResponseCode::ResponseCode as ApcResponseCode,
};
use android_system_keystore2::aidl::android::system::keystore2::{
- Authorization::Authorization, Domain::Domain, KeyDescriptor::KeyDescriptor,
+ Authorization::Authorization, KeyDescriptor::KeyDescriptor,
};
-use anyhow::{Context, Result};
-use binder::{Strong, ThreadState};
+use anyhow::{anyhow, Context};
+use binder::{FromIBinder, SpIBinder, ThreadState};
use keystore2_apc_compat::{
ApcCompatUiOptions, APC_COMPAT_ERROR_ABORTED, APC_COMPAT_ERROR_CANCELLED,
APC_COMPAT_ERROR_IGNORED, APC_COMPAT_ERROR_OK, APC_COMPAT_ERROR_OPERATION_PENDING,
APC_COMPAT_ERROR_SYSTEM_ERROR,
};
-use keystore2_crypto::{aes_gcm_decrypt, aes_gcm_encrypt, ZVec};
-use std::iter::IntoIterator;
+use std::sync::Mutex;
/// This function uses its namesake in the permission module and in
/// combination with with_calling_sid from the binder crate to check
@@ -51,7 +44,7 @@ use std::iter::IntoIterator;
pub fn check_keystore_permission(perm: KeystorePerm) -> anyhow::Result<()> {
ThreadState::with_calling_sid(|calling_sid| {
permission::check_keystore_permission(
- calling_sid.ok_or_else(Error::sys).context(
+ &calling_sid.ok_or_else(Error::sys).context(
"In check_keystore_permission: Cannot check permission without calling_sid.",
)?,
perm,
@@ -65,7 +58,7 @@ pub fn check_keystore_permission(perm: KeystorePerm) -> anyhow::Result<()> {
pub fn check_grant_permission(access_vec: KeyPermSet, key: &KeyDescriptor) -> anyhow::Result<()> {
ThreadState::with_calling_sid(|calling_sid| {
permission::check_grant_permission(
- calling_sid.ok_or_else(Error::sys).context(
+ &calling_sid.ok_or_else(Error::sys).context(
"In check_grant_permission: Cannot check permission without calling_sid.",
)?,
access_vec,
@@ -85,7 +78,7 @@ pub fn check_key_permission(
ThreadState::with_calling_sid(|calling_sid| {
permission::check_key_permission(
ThreadState::get_calling_uid(),
- calling_sid
+ &calling_sid
.ok_or_else(Error::sys)
.context("In check_key_permission: Cannot check permission without calling_sid.")?,
perm,
@@ -107,21 +100,10 @@ pub fn is_device_id_attestation_tag(tag: Tag) -> bool {
}
/// This function checks whether the calling app has the Android permissions needed to attest device
-/// identifiers. It throws an error if the permissions cannot be verified or if the caller doesn't
-/// have the right permissions. Otherwise it returns silently.
+/// identifiers. It throws an error if the permissions cannot be verified, or if the caller doesn't
+/// have the right permissions, and returns silently otherwise.
pub fn check_device_attestation_permissions() -> anyhow::Result<()> {
- check_android_permission("android.permission.READ_PRIVILEGED_PHONE_STATE")
-}
-
-/// This function checks whether the calling app has the Android permissions needed to attest the
-/// device-unique identifier. It throws an error if the permissions cannot be verified or if the
-/// caller doesn't have the right permissions. Otherwise it returns silently.
-pub fn check_unique_id_attestation_permissions() -> anyhow::Result<()> {
- check_android_permission("android.permission.REQUEST_UNIQUE_ID_ATTESTATION")
-}
-
-fn check_android_permission(permission: &str) -> anyhow::Result<()> {
- let permission_controller: Strong<dyn IPermissionController::IPermissionController> =
+ let permission_controller: binder::Strong<dyn IPermissionController::IPermissionController> =
binder::get_interface("permission")?;
let binder_result = {
@@ -130,7 +112,7 @@ fn check_android_permission(permission: &str) -> anyhow::Result<()> {
500,
);
permission_controller.checkPermission(
- permission,
+ "android.permission.READ_PRIVILEGED_PHONE_STATE",
ThreadState::get_calling_pid(),
ThreadState::get_calling_uid() as i32,
)
@@ -146,64 +128,55 @@ fn check_android_permission(permission: &str) -> anyhow::Result<()> {
}
}
+/// Thread safe wrapper around SpIBinder. It is safe to have SpIBinder smart pointers to the
+/// same object in multiple threads, but cloning a SpIBinder is not thread safe.
+/// Keystore frequently hands out binder tokens to the security level interface. If this
+/// is to happen from a multi threaded thread pool, the SpIBinder needs to be protected by a
+/// Mutex.
+#[derive(Debug)]
+pub struct Asp(Mutex<SpIBinder>);
+
+impl Asp {
+ /// Creates a new instance owning a SpIBinder wrapped in a Mutex.
+ pub fn new(i: SpIBinder) -> Self {
+ Self(Mutex::new(i))
+ }
+
+ /// Clones the owned SpIBinder and attempts to convert it into the requested interface.
+ pub fn get_interface<T: FromIBinder + ?Sized>(&self) -> anyhow::Result<binder::Strong<T>> {
+ // We can use unwrap here because we never panic when locked, so the mutex
+ // can never be poisoned.
+ let lock = self.0.lock().unwrap();
+ (*lock)
+ .clone()
+ .into_interface()
+ .map_err(|e| anyhow!(format!("get_interface failed with error code {:?}", e)))
+ }
+}
+
+impl Clone for Asp {
+ fn clone(&self) -> Self {
+ let lock = self.0.lock().unwrap();
+ Self(Mutex::new((*lock).clone()))
+ }
+}
+
/// Converts a set of key characteristics as returned from KeyMint into the internal
/// representation of the keystore service.
pub fn key_characteristics_to_internal(
key_characteristics: Vec<KeyCharacteristics>,
-) -> Vec<KeyParameter> {
+) -> Vec<crate::key_parameter::KeyParameter> {
key_characteristics
.into_iter()
.flat_map(|aidl_key_char| {
let sec_level = aidl_key_char.securityLevel;
- aidl_key_char
- .authorizations
- .into_iter()
- .map(move |aidl_kp| KeyParameter::new(aidl_kp.into(), sec_level))
+ aidl_key_char.authorizations.into_iter().map(move |aidl_kp| {
+ crate::key_parameter::KeyParameter::new(aidl_kp.into(), sec_level)
+ })
})
.collect()
}
-/// This function can be used to upgrade key blobs on demand. The return value of
-/// `km_op` is inspected and if ErrorCode::KEY_REQUIRES_UPGRADE is encountered,
-/// an attempt is made to upgrade the key blob. On success `new_blob_handler` is called
-/// with the upgraded blob as argument. Then `km_op` is called a second time with the
-/// upgraded blob as argument. On success a tuple of the `km_op`s result and the
-/// optional upgraded blob is returned.
-pub fn upgrade_keyblob_if_required_with<T, KmOp, NewBlobHandler>(
- km_dev: &dyn IKeyMintDevice,
- key_blob: &[u8],
- upgrade_params: &[KmKeyParameter],
- km_op: KmOp,
- new_blob_handler: NewBlobHandler,
-) -> Result<(T, Option<Vec<u8>>)>
-where
- KmOp: Fn(&[u8]) -> Result<T, Error>,
- NewBlobHandler: FnOnce(&[u8]) -> Result<()>,
-{
- match km_op(key_blob) {
- Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
- let upgraded_blob = {
- let _wp = watchdog::watch_millis(
- "In utils::upgrade_keyblob_if_required_with: calling upgradeKey.",
- 500,
- );
- map_km_error(km_dev.upgradeKey(key_blob, upgrade_params))
- }
- .context("In utils::upgrade_keyblob_if_required_with: Upgrade failed.")?;
-
- new_blob_handler(&upgraded_blob)
- .context("In utils::upgrade_keyblob_if_required_with: calling new_blob_handler.")?;
-
- km_op(&upgraded_blob)
- .map(|v| (v, Some(upgraded_blob)))
- .context("In utils::upgrade_keyblob_if_required_with: Calling km_op after upgrade.")
- }
- r => r
- .map(|v| (v, None))
- .context("In utils::upgrade_keyblob_if_required_with: Calling km_op."),
- }
-}
-
/// Converts a set of key characteristics from the internal representation into a set of
/// Authorizations as they are used to convey key characteristics to the clients of keystore.
pub fn key_parameters_to_authorizations(
@@ -249,37 +222,16 @@ pub fn ui_opts_2_compat(opt: i32) -> ApcCompatUiOptions {
}
/// AID offset for uid space partitioning.
-pub const AID_USER_OFFSET: u32 = rustutils::users::AID_USER_OFFSET;
+pub const AID_USER_OFFSET: u32 = cutils_bindgen::AID_USER_OFFSET;
/// AID of the keystore process itself, used for keys that
/// keystore generates for its own use.
-pub const AID_KEYSTORE: u32 = rustutils::users::AID_KEYSTORE;
+pub const AID_KEYSTORE: u32 = cutils_bindgen::AID_KEYSTORE;
/// Extracts the android user from the given uid.
pub fn uid_to_android_user(uid: u32) -> u32 {
- rustutils::users::multiuser_get_user_id(uid)
-}
-
-/// List all key aliases for a given domain + namespace.
-pub fn list_key_entries(
- db: &mut KeystoreDB,
- domain: Domain,
- namespace: i64,
-) -> Result<Vec<KeyDescriptor>> {
- let mut result = Vec::new();
- result.append(
- &mut LEGACY_IMPORTER
- .list_uid(domain, namespace)
- .context("In list_key_entries: Trying to list legacy keys.")?,
- );
- result.append(
- &mut db
- .list(domain, namespace, KeyType::Client)
- .context("In list_key_entries: Trying to list keystore database.")?,
- );
- result.sort_unstable();
- result.dedup();
- Ok(result)
+ // Safety: No memory access
+ unsafe { cutils_bindgen::multiuser_get_user_id(uid) }
}
/// This module provides helpers for simplified use of the watchdog module.
@@ -312,36 +264,6 @@ pub mod watchdog {
}
}
-/// Trait implemented by objects that can be used to decrypt cipher text using AES-GCM.
-pub trait AesGcm {
- /// Deciphers `data` using the initialization vector `iv` and AEAD tag `tag`
- /// and AES-GCM. The implementation provides the key material and selects
- /// the implementation variant, e.g., AES128 or AES265.
- fn decrypt(&self, data: &[u8], iv: &[u8], tag: &[u8]) -> Result<ZVec>;
-
- /// Encrypts `data` and returns the ciphertext, the initialization vector `iv`
- /// and AEAD tag `tag`. The implementation provides the key material and selects
- /// the implementation variant, e.g., AES128 or AES265.
- fn encrypt(&self, plaintext: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>)>;
-}
-
-/// Marks an object as AES-GCM key.
-pub trait AesGcmKey {
- /// Provides access to the raw key material.
- fn key(&self) -> &[u8];
-}
-
-impl<T: AesGcmKey> AesGcm for T {
- fn decrypt(&self, data: &[u8], iv: &[u8], tag: &[u8]) -> Result<ZVec> {
- aes_gcm_decrypt(data, iv, tag, self.key())
- .context("In AesGcm<T>::decrypt: Decryption failed")
- }
-
- fn encrypt(&self, plaintext: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>)> {
- aes_gcm_encrypt(plaintext, self.key()).context("In AesGcm<T>::encrypt: Encryption failed.")
- }
-}
-
/// This module provides empty/noop implementations of the watch dog utility functions.
#[cfg(not(feature = "watchdog"))]
pub mod watchdog {
diff --git a/keystore2/src/vintf/Android.bp b/keystore2/src/vintf/Android.bp
index 34719aaa..3ab0ec51 100644
--- a/keystore2/src/vintf/Android.bp
+++ b/keystore2/src/vintf/Android.bp
@@ -26,32 +26,39 @@ rust_library {
crate_name: "keystore2_vintf",
srcs: ["lib.rs"],
rustlibs: [
- "libcxx",
+ "libkeystore2_vintf_bindgen",
],
shared_libs: [
- "libvintf",
- ],
- static_libs: [
"libkeystore2_vintf_cpp",
+ "libvintf",
],
}
-cc_library_static {
+cc_library {
name: "libkeystore2_vintf_cpp",
- srcs: ["vintf.cpp"],
- generated_headers: ["cxx-bridge-header"],
- generated_sources: ["vintf_bridge_code"],
+ srcs: [
+ "vintf.cpp",
+ ],
shared_libs: [
"libvintf",
],
}
-genrule {
- name: "vintf_bridge_code",
- tools: ["cxxbridge"],
- cmd: "$(location cxxbridge) $(in) >> $(out)",
- srcs: ["lib.rs"],
- out: ["vintf_cxx_generated.cc"],
+rust_bindgen {
+ name: "libkeystore2_vintf_bindgen",
+ wrapper_src: "vintf.hpp",
+ crate_name: "keystore2_vintf_bindgen",
+ source_stem: "bindings",
+ host_supported: true,
+ shared_libs: ["libvintf"],
+ bindgen_flags: [
+ "--size_t-is-usize",
+ "--allowlist-function", "getHalNames",
+ "--allowlist-function", "getHalNamesAndVersions",
+ "--allowlist-function", "getHidlInstances",
+ "--allowlist-function", "getAidlInstances",
+ "--allowlist-function", "freeNames",
+ ],
}
rust_test {
@@ -61,7 +68,7 @@ rust_test {
test_suites: ["general-tests"],
auto_gen_config: true,
rustlibs: [
- "libcxx",
+ "libkeystore2_vintf_bindgen",
],
static_libs: [
"libkeystore2_vintf_cpp",
diff --git a/keystore2/src/vintf/lib.rs b/keystore2/src/vintf/lib.rs
index 89e18eba..8730a3e3 100644
--- a/keystore2/src/vintf/lib.rs
+++ b/keystore2/src/vintf/lib.rs
@@ -14,35 +14,96 @@
//! Bindings for getting the list of HALs.
-#[cxx::bridge]
-mod ffi {
- unsafe extern "C++" {
- include!("vintf.hpp");
-
- /// Gets all HAL names.
- /// Note that this is not a zero-cost shim: it will make copies of the strings.
- fn get_hal_names() -> Vec<String>;
-
- /// Gets all HAL names and versions.
- /// Note that this is not a zero-cost shim: it will make copies of the strings.
- fn get_hal_names_and_versions() -> Vec<String>;
-
- /// Gets the instances of the given package, version, and interface tuple.
- /// Note that this is not a zero-cost shim: it will make copies of the strings.
- fn get_hidl_instances(
- package: &str,
- major_version: usize,
- minor_version: usize,
- interface_name: &str,
- ) -> Vec<String>;
-
- /// Gets the instances of the given package, version, and interface tuple.
- /// Note that this is not a zero-cost shim: it will make copies of the strings.
- fn get_aidl_instances(package: &str, version: usize, interface_name: &str) -> Vec<String>;
+use keystore2_vintf_bindgen::{
+ freeNames, getAidlInstances, getHalNames, getHalNamesAndVersions, getHidlInstances,
+};
+use std::ffi::{CStr, CString};
+use std::os::raw::c_char;
+use std::str::Utf8Error;
+
+/// A struct that contains a list of HALs (optionally with version numbers).
+/// To use it, call as_vec to get a Vec view of the data it contains.
+pub struct HalNames {
+ data: *mut *mut c_char,
+ len: usize,
+}
+
+impl Drop for HalNames {
+ fn drop(&mut self) {
+ // Safety: The memory is allocated by our C shim so it must free it as well.
+ unsafe { freeNames(self.data, self.len) }
}
}
-pub use ffi::*;
+impl<'a> HalNames {
+ /// Get a Vec view of the list of HALs.
+ pub fn as_vec(&'a self) -> Result<Vec<&'a str>, Utf8Error> {
+ // Safety: self.data contains self.len C strings.
+ // The lifetimes ensure that the HalNames (and hence the strings) live
+ // at least as long as the returned vector.
+ unsafe { (0..self.len).map(|i| CStr::from_ptr(*self.data.add(i)).to_str()) }.collect()
+ }
+}
+
+/// Gets all HAL names.
+/// Note that this is not a zero-cost shim: it will make copies of the strings.
+pub fn get_hal_names() -> HalNames {
+ let mut len: usize = 0;
+ // Safety: We'll wrap this in HalNames to free the memory it allocates.
+ // It stores the size of the array it returns in len.
+ let raw_strs = unsafe { getHalNames(&mut len) };
+ HalNames { data: raw_strs, len }
+}
+
+/// Gets all HAL names and versions.
+/// Note that this is not a zero-cost shim: it will make copies of the strings.
+pub fn get_hal_names_and_versions() -> HalNames {
+ let mut len: usize = 0;
+ // Safety: We'll wrap this in HalNames to free the memory it allocates.
+ // It stores the size of the array it returns in len.
+ let raw_strs = unsafe { getHalNamesAndVersions(&mut len) };
+ HalNames { data: raw_strs, len }
+}
+
+/// Gets the instances of the given package, version, and interface tuple.
+/// Note that this is not a zero-cost shim: it will make copies of the strings.
+pub fn get_hidl_instances(
+ package: &str,
+ major_version: usize,
+ minor_version: usize,
+ interface_name: &str,
+) -> HalNames {
+ let mut len: usize = 0;
+ let packages = CString::new(package).expect("Failed to make CString from package.");
+ let interface_name =
+ CString::new(interface_name).expect("Failed to make CString from interface_name.");
+ // Safety: We'll wrap this in HalNames to free the memory it allocates.
+ // It stores the size of the array it returns in len.
+ let raw_strs = unsafe {
+ getHidlInstances(
+ &mut len,
+ packages.as_ptr(),
+ major_version,
+ minor_version,
+ interface_name.as_ptr(),
+ )
+ };
+ HalNames { data: raw_strs, len }
+}
+
+/// Gets the instances of the given package, version, and interface tuple.
+/// Note that this is not a zero-cost shim: it will make copies of the strings.
+pub fn get_aidl_instances(package: &str, version: usize, interface_name: &str) -> HalNames {
+ let mut len: usize = 0;
+ let packages = CString::new(package).expect("Failed to make CString from package.");
+ let interface_name =
+ CString::new(interface_name).expect("Failed to make CString from interface_name.");
+ // Safety: We'll wrap this in HalNames to free the memory it allocates.
+ // It stores the size of the array it returns in len.
+ let raw_strs =
+ unsafe { getAidlInstances(&mut len, packages.as_ptr(), version, interface_name.as_ptr()) };
+ HalNames { data: raw_strs, len }
+}
#[cfg(test)]
mod tests {
@@ -50,13 +111,17 @@ mod tests {
use super::*;
#[test]
- fn test() {
- let names = get_hal_names();
+ fn test() -> Result<(), Utf8Error> {
+ let result = get_hal_names();
+ let names = result.as_vec()?;
assert_ne!(names.len(), 0);
- let names_and_versions = get_hal_names_and_versions();
+ let result = get_hal_names_and_versions();
+ let names_and_versions = result.as_vec()?;
assert_ne!(names_and_versions.len(), 0);
assert!(names_and_versions.len() >= names.len());
+
+ Ok(())
}
}
diff --git a/keystore2/src/vintf/vintf.cpp b/keystore2/src/vintf/vintf.cpp
index 00625bff..e407efac 100644
--- a/keystore2/src/vintf/vintf.cpp
+++ b/keystore2/src/vintf/vintf.cpp
@@ -14,43 +14,55 @@
* limitations under the License.
*/
-#include <algorithm>
+#include "vintf.hpp"
+
#include <vintf/HalManifest.h>
#include <vintf/VintfObject.h>
-#include "rust/cxx.h"
-
-rust::Vec<rust::String> convert(const std::set<std::string>& names) {
- rust::Vec<rust::String> result;
- std::copy(names.begin(), names.end(), std::back_inserter(result));
- return result;
+// Converts a set<string> into a C-style array of C strings.
+static char** convert(const std::set<std::string>& names) {
+ char** ret = new char*[names.size()];
+ char** ptr = ret;
+ for (const auto& name : names) {
+ *(ptr++) = strdup(name.c_str());
+ }
+ return ret;
}
-rust::Vec<rust::String> get_hal_names() {
- const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+char** getHalNames(size_t* len) {
+ auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
const auto names = manifest->getHalNames();
+ *len = names.size();
return convert(names);
}
-rust::Vec<rust::String> get_hal_names_and_versions() {
- const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+char** getHalNamesAndVersions(size_t* len) {
+ auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
const auto names = manifest->getHalNamesAndVersions();
+ *len = names.size();
return convert(names);
}
-rust::Vec<rust::String> get_hidl_instances(rust::Str package, size_t major_version,
- size_t minor_version, rust::Str interfaceName) {
+char** getHidlInstances(size_t* len, const char* package, size_t major_version,
+ size_t minor_version, const char* interfaceName) {
android::vintf::Version version(major_version, minor_version);
- const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
- const auto names = manifest->getHidlInstances(static_cast<std::string>(package), version,
- static_cast<std::string>(interfaceName));
+ auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+ const auto names = manifest->getHidlInstances(package, version, interfaceName);
+ *len = names.size();
return convert(names);
}
-rust::Vec<rust::String> get_aidl_instances(rust::Str package, size_t version,
- rust::Str interfaceName) {
- const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
- const auto names = manifest->getAidlInstances(static_cast<std::string>(package), version,
- static_cast<std::string>(interfaceName));
+char** getAidlInstances(size_t* len, const char* package, size_t version,
+ const char* interfaceName) {
+ auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+ const auto names = manifest->getAidlInstances(package, version, interfaceName);
+ *len = names.size();
return convert(names);
}
+
+void freeNames(char** names, size_t len) {
+ for (int i = 0; i < len; i++) {
+ free(names[i]);
+ }
+ delete[] names;
+}
diff --git a/keystore2/src/vintf/vintf.hpp b/keystore2/src/vintf/vintf.hpp
index dbc88f0f..091e8e8d 100644
--- a/keystore2/src/vintf/vintf.hpp
+++ b/keystore2/src/vintf/vintf.hpp
@@ -14,13 +14,20 @@
* limitations under the License.
*/
-#pragma once
+#ifndef __VINTF_H__
+#define __VINTF_H__
-#include "rust/cxx.h"
+#include <stddef.h>
-rust::Vec<rust::String> get_hal_names();
-rust::Vec<rust::String> get_hal_names_and_versions();
-rust::Vec<rust::String> get_hidl_instances(rust::Str package, size_t major_version,
- size_t minor_version, rust::Str interfaceName);
-rust::Vec<rust::String> get_aidl_instances(rust::Str package, size_t version,
- rust::Str interfaceName);
+extern "C" {
+
+char** getHalNames(size_t* len);
+char** getHalNamesAndVersions(size_t* len);
+char** getHidlInstances(size_t* len, const char* package, size_t major_version,
+ size_t minor_version, const char* interfaceName);
+char** getAidlInstances(size_t* len, const char* package, size_t version,
+ const char* interfaceName);
+void freeNames(char** names, size_t len);
+}
+
+#endif // __VINTF_H__
diff --git a/keystore2/src/watchdog.rs b/keystore2/src/watchdog.rs
index a26b632a..9cca171e 100644
--- a/keystore2/src/watchdog.rs
+++ b/keystore2/src/watchdog.rs
@@ -111,44 +111,11 @@ impl WatchdogState {
}
self.last_report = Instant::now();
self.has_overdue = has_overdue;
- log::warn!("### Keystore Watchdog report - BEGIN ###");
-
+ log::warn!("Keystore Watchdog report:");
+ log::warn!("Overdue records:");
let now = Instant::now();
- let mut overdue_records: Vec<(&Index, &Record)> = self
- .records
- .iter()
- .filter(|(_, r)| r.deadline.saturating_duration_since(now) == Duration::new(0, 0))
- .collect();
-
- log::warn!("When extracting from a bug report, please include this header");
- log::warn!("and all {} records below.", overdue_records.len());
-
- // Watch points can be nested, i.e., a single thread may have multiple armed
- // watch points. And the most recent on each thread (thread recent) is closest to the point
- // where something is blocked. Furthermore, keystore2 has various critical section
- // and common backend resources KeyMint that can only be entered serialized. So if one
- // thread hangs, the others will soon follow suite. Thus the oldest "thread recent" watch
- // point is most likely pointing toward the culprit.
- // Thus, sort by start time first.
- overdue_records.sort_unstable_by(|(_, r1), (_, r2)| r1.started.cmp(&r2.started));
- // Then we groups all of the watch points per thread preserving the order within
- // groups.
- let groups = overdue_records.iter().fold(
- HashMap::<thread::ThreadId, Vec<(&Index, &Record)>>::new(),
- |mut acc, (i, r)| {
- acc.entry(i.tid).or_default().push((i, r));
- acc
- },
- );
- // Put the groups back into a vector.
- let mut groups: Vec<Vec<(&Index, &Record)>> = groups.into_iter().map(|(_, v)| v).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.
- groups.sort_by(|v1, v2| v1.last().unwrap().1.started.cmp(&v2.last().unwrap().1.started));
-
- for g in groups.iter() {
- for (i, r) in g.iter() {
+ for (i, r) in self.records.iter() {
+ if r.deadline.saturating_duration_since(now) == Duration::new(0, 0) {
match &r.callback {
Some(cb) => {
log::warn!(
@@ -172,7 +139,6 @@ impl WatchdogState {
}
}
}
- log::warn!("### Keystore Watchdog report - END ###");
true
}
diff --git a/keystore2/tests/legacy_blobs/Android.bp b/keystore2/system_property/Android.bp
index 9322a411..773804d9 100644
--- a/keystore2/tests/legacy_blobs/Android.bp
+++ b/keystore2/system_property/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2022, The Android Open Source Project
+// 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.
@@ -21,31 +21,33 @@ package {
default_applicable_licenses: ["system_security_license"],
}
-rust_test {
- name: "keystore2_legacy_blobs_test",
- srcs: ["keystore2_legacy_blob_tests.rs"],
- test_suites: [
- "general-tests",
+rust_bindgen {
+ name: "libkeystore2_system_property_bindgen",
+ wrapper_src: "system_property_bindgen.hpp",
+ crate_name: "keystore2_system_property_bindgen",
+ source_stem: "bindings",
+
+ bindgen_flags: [
+ "--size_t-is-usize",
+ "--allowlist-function=__system_property_find",
+ "--allowlist-function=__system_property_read_callback",
+ "--allowlist-function=__system_property_set",
+ "--allowlist-function=__system_property_wait",
],
- // auto_gen_config: true,
- test_config: "AndroidTest.xml",
+}
+rust_library {
+ name: "libkeystore2_system_property-rust",
+ crate_name: "keystore2_system_property",
+ srcs: [
+ "lib.rs",
+ ],
rustlibs: [
- "libkeystore2_with_test_utils",
- "libkeystore2_crypto_rust",
- "android.system.keystore2-V2-rust",
- "android.hardware.security.keymint-V2-rust",
- "android.security.maintenance-rust",
- "android.security.authorization-rust",
- "librustutils",
- "libkeystore2_test_utils",
- "libnix",
"libanyhow",
- "libbinder_rs",
- "liblazy_static",
- "liblibc",
- "libserde",
+ "libkeystore2_system_property_bindgen",
"libthiserror",
],
- require_root: true,
+ shared_libs: [
+ "libbase",
+ ],
}
diff --git a/keystore2/system_property/lib.rs b/keystore2/system_property/lib.rs
new file mode 100644
index 00000000..b993c879
--- /dev/null
+++ b/keystore2/system_property/lib.rs
@@ -0,0 +1,217 @@
+// 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 the PropertyWatcher type, which watches for changes
+//! in Android system properties.
+
+use anyhow::{anyhow, Context, Result as AnyhowResult};
+use keystore2_system_property_bindgen::prop_info as PropInfo;
+use std::os::raw::c_char;
+use std::ptr::null;
+use std::{
+ ffi::{c_void, CStr, CString},
+ str::Utf8Error,
+};
+use thiserror::Error;
+
+/// Errors this crate can generate
+#[derive(Error, Debug)]
+pub enum PropertyWatcherError {
+ /// We can't watch for a property whose name contains a NUL character.
+ #[error("Cannot convert name to C string")]
+ BadNameError(#[from] std::ffi::NulError),
+ /// We can only watch for properties that exist when the watcher is created.
+ #[error("System property is absent")]
+ SystemPropertyAbsent,
+ /// __system_property_wait timed out despite being given no timeout.
+ #[error("Wait failed")]
+ WaitFailed,
+ /// read callback was not called
+ #[error("__system_property_read_callback did not call callback")]
+ ReadCallbackNotCalled,
+ /// read callback gave us a NULL pointer
+ #[error("__system_property_read_callback gave us a NULL pointer instead of a string")]
+ MissingCString,
+ /// read callback gave us a bad C string
+ #[error("__system_property_read_callback gave us a non-UTF8 C string")]
+ BadCString(#[from] Utf8Error),
+ /// read callback returned an error
+ #[error("Callback failed")]
+ CallbackError(#[from] anyhow::Error),
+ /// Failure in setting the system property
+ #[error("__system_property_set failed.")]
+ SetPropertyFailed,
+}
+
+/// Result type specific for this crate.
+pub type Result<T> = std::result::Result<T, PropertyWatcherError>;
+
+/// PropertyWatcher takes the name of an Android system property such
+/// as `keystore.boot_level`; it can report the current value of this
+/// property, or wait for it to change.
+pub struct PropertyWatcher {
+ prop_name: CString,
+ prop_info: *const PropInfo,
+ serial: keystore2_system_property_bindgen::__uint32_t,
+}
+
+impl PropertyWatcher {
+ /// Create a PropertyWatcher for the named system property.
+ pub fn new(name: &str) -> Result<Self> {
+ Ok(Self { prop_name: CString::new(name)?, prop_info: null(), serial: 0 })
+ }
+
+ // Lazy-initializing accessor for self.prop_info.
+ fn get_prop_info(&mut self) -> Option<*const PropInfo> {
+ if self.prop_info.is_null() {
+ // Unsafe required for FFI call. Input and output are both const.
+ // The returned pointer is valid for the lifetime of the program.
+ self.prop_info = unsafe {
+ keystore2_system_property_bindgen::__system_property_find(self.prop_name.as_ptr())
+ };
+ }
+ if self.prop_info.is_null() {
+ None
+ } else {
+ Some(self.prop_info)
+ }
+ }
+
+ fn read_raw(prop_info: *const PropInfo, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>)) {
+ // Unsafe function converts values passed to us by
+ // __system_property_read_callback to Rust form
+ // and pass them to inner callback.
+ unsafe extern "C" fn callback(
+ res_p: *mut c_void,
+ name: *const c_char,
+ value: *const c_char,
+ _: keystore2_system_property_bindgen::__uint32_t,
+ ) {
+ let name = if name.is_null() { None } else { Some(CStr::from_ptr(name)) };
+ let value = if value.is_null() { None } else { Some(CStr::from_ptr(value)) };
+ let f = &mut *res_p.cast::<&mut dyn FnMut(Option<&CStr>, Option<&CStr>)>();
+ f(name, value);
+ }
+
+ let mut f: &mut dyn FnOnce(Option<&CStr>, Option<&CStr>) = &mut f;
+
+ // Unsafe block for FFI call. We convert the FnOnce
+ // to a void pointer, and unwrap it in our callback.
+ unsafe {
+ keystore2_system_property_bindgen::__system_property_read_callback(
+ prop_info,
+ Some(callback),
+ &mut f as *mut _ as *mut c_void,
+ )
+ }
+ }
+
+ /// Call the passed function, passing it the name and current value
+ /// of this system property. See documentation for
+ /// `__system_property_read_callback` for details.
+ /// Returns an error if the property is empty or doesn't exist.
+ pub fn read<T, F>(&mut self, f: F) -> Result<T>
+ where
+ F: FnOnce(&str, &str) -> anyhow::Result<T>,
+ {
+ let prop_info = self.get_prop_info().ok_or(PropertyWatcherError::SystemPropertyAbsent)?;
+ let mut result = Err(PropertyWatcherError::ReadCallbackNotCalled);
+ Self::read_raw(prop_info, |name, value| {
+ // use a wrapping closure as an erzatz try block.
+ result = (|| {
+ let name = name.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
+ let value = value.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
+ f(name, value).map_err(PropertyWatcherError::CallbackError)
+ })()
+ });
+ result
+ }
+
+ // Waits for the property that self is watching to be created. Returns immediately if the
+ // property already exists.
+ fn wait_for_property_creation(&mut self) -> Result<()> {
+ let mut global_serial = 0;
+ loop {
+ match self.get_prop_info() {
+ Some(_) => return Ok(()),
+ None => {
+ // Unsafe call for FFI. The function modifies only global_serial, and has
+ // no side-effects.
+ if !unsafe {
+ // Wait for a global serial number change, then try again. On success,
+ // the function will update global_serial with the last version seen.
+ keystore2_system_property_bindgen::__system_property_wait(
+ null(),
+ global_serial,
+ &mut global_serial,
+ null(),
+ )
+ } {
+ return Err(PropertyWatcherError::WaitFailed);
+ }
+ }
+ }
+ }
+ }
+
+ /// Wait for the system property to change. This
+ /// records the serial number of the last change, so
+ /// race conditions are avoided.
+ pub fn wait(&mut self) -> Result<()> {
+ // If the property is null, then wait for it to be created. Subsequent waits will
+ // skip this step and wait for our specific property to change.
+ if self.prop_info.is_null() {
+ return self.wait_for_property_creation();
+ }
+
+ let mut new_serial = self.serial;
+ // Unsafe block to call __system_property_wait.
+ // All arguments are private to PropertyWatcher so we
+ // can be confident they are valid.
+ if !unsafe {
+ keystore2_system_property_bindgen::__system_property_wait(
+ self.prop_info,
+ self.serial,
+ &mut new_serial,
+ null(),
+ )
+ } {
+ return Err(PropertyWatcherError::WaitFailed);
+ }
+ self.serial = new_serial;
+ Ok(())
+ }
+}
+
+/// Writes a system property.
+pub fn write(name: &str, value: &str) -> AnyhowResult<()> {
+ if
+ // Unsafe required for FFI call. Input and output are both const and valid strings.
+ unsafe {
+ // If successful, __system_property_set returns 0, otherwise, returns -1.
+ keystore2_system_property_bindgen::__system_property_set(
+ CString::new(name)
+ .context("In keystore2::system_property::write: Construction CString from name.")?
+ .as_ptr(),
+ CString::new(value)
+ .context("In keystore2::system_property::write: Constructing CString from value.")?
+ .as_ptr(),
+ )
+ } == 0
+ {
+ Ok(())
+ } else {
+ Err(anyhow!(PropertyWatcherError::SetPropertyFailed))
+ }
+}
diff --git a/fsverity/fsverity_digests.proto b/keystore2/system_property/system_property_bindgen.hpp
index 816ae61b..e3c1ade0 100644
--- a/fsverity/fsverity_digests.proto
+++ b/keystore2/system_property/system_property_bindgen.hpp
@@ -13,15 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
-syntax = "proto3";
-
-package android.security.fsverity;
-
-message FSVerityDigests {
- message Digest {
- bytes digest = 1;
- string hash_alg = 2;
- }
- map<string, Digest> digests = 1;
-}
+#include "sys/system_properties.h"
diff --git a/keystore2/test_utils/authorizations.rs b/keystore2/test_utils/authorizations.rs
deleted file mode 100644
index 4fbe1241..00000000
--- a/keystore2/test_utils/authorizations.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-// 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.
-
-//! This module implements test utils to create Autherizations.
-
-use std::ops::Deref;
-
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, KeyParameter::KeyParameter,
- KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, Tag::Tag,
-};
-
-/// Helper struct to create set of Authorizations.
-pub struct AuthSetBuilder(Vec<KeyParameter>);
-
-impl Default for AuthSetBuilder {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl AuthSetBuilder {
- /// Creates new Authorizations list.
- pub fn new() -> Self {
- Self(Vec::new())
- }
-
- /// Add Purpose.
- pub fn purpose(mut self, p: KeyPurpose) -> Self {
- self.0.push(KeyParameter { tag: Tag::PURPOSE, value: KeyParameterValue::KeyPurpose(p) });
- self
- }
-
- /// Add Digest.
- pub fn digest(mut self, d: Digest) -> Self {
- self.0.push(KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(d) });
- self
- }
-
- /// Add Algorithm.
- pub fn algorithm(mut self, a: Algorithm) -> Self {
- self.0.push(KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(a) });
- self
- }
-
- /// Add EC-Curve.
- pub fn ec_curve(mut self, e: EcCurve) -> Self {
- self.0.push(KeyParameter { tag: Tag::EC_CURVE, value: KeyParameterValue::EcCurve(e) });
- self
- }
-
- /// Add Attestation-Challenge.
- pub fn attestation_challenge(mut self, b: Vec<u8>) -> Self {
- self.0.push(KeyParameter {
- tag: Tag::ATTESTATION_CHALLENGE,
- value: KeyParameterValue::Blob(b),
- });
- self
- }
-
- /// Add Attestation-ID.
- pub fn attestation_app_id(mut self, b: Vec<u8>) -> Self {
- self.0.push(KeyParameter {
- tag: Tag::ATTESTATION_APPLICATION_ID,
- value: KeyParameterValue::Blob(b),
- });
- self
- }
-}
-
-impl Deref for AuthSetBuilder {
- type Target = Vec<KeyParameter>;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
diff --git a/keystore2/test_utils/key_generations.rs b/keystore2/test_utils/key_generations.rs
deleted file mode 100644
index f49aa9ff..00000000
--- a/keystore2/test_utils/key_generations.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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.
-
-//! This module implements test utils to generate various types of keys.
-
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, KeyPurpose::KeyPurpose,
-};
-use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
- KeyMetadata::KeyMetadata,
-};
-
-use crate::authorizations::AuthSetBuilder;
-
-const SELINUX_SHELL_NAMESPACE: i64 = 1;
-
-/// Generate attested EC Key blob using given security level with below key parameters -
-/// Purposes: SIGN and VERIFY
-/// Digest: SHA_2_256
-/// Curve: P_256
-pub fn generate_ec_p256_signing_key_with_attestation(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
-) -> binder::Result<KeyMetadata> {
- let att_challenge: &[u8] = b"foo";
- let att_app_id: &[u8] = b"bar";
- let gen_params = AuthSetBuilder::new()
- .algorithm(Algorithm::EC)
- .purpose(KeyPurpose::SIGN)
- .purpose(KeyPurpose::VERIFY)
- .digest(Digest::SHA_2_256)
- .ec_curve(EcCurve::P_256)
- .attestation_challenge(att_challenge.to_vec())
- .attestation_app_id(att_app_id.to_vec());
-
- match sec_level.generateKey(
- &KeyDescriptor {
- domain: Domain::BLOB,
- nspace: SELINUX_SHELL_NAMESPACE,
- alias: None,
- blob: None,
- },
- None,
- &gen_params,
- 0,
- b"entropy",
- ) {
- Ok(key_metadata) => {
- assert!(key_metadata.certificate.is_some());
- assert!(key_metadata.certificateChain.is_some());
- assert!(key_metadata.key.blob.is_some());
-
- Ok(key_metadata)
- }
- Err(e) => Err(e),
- }
-}
diff --git a/keystore2/test_utils/lib.rs b/keystore2/test_utils/lib.rs
index c63bfacc..627af20c 100644
--- a/keystore2/test_utils/lib.rs
+++ b/keystore2/test_utils/lib.rs
@@ -19,14 +19,6 @@ use std::io::ErrorKind;
use std::path::{Path, PathBuf};
use std::{env::temp_dir, ops::Deref};
-use android_system_keystore2::aidl::android::system::keystore2::IKeystoreService::IKeystoreService;
-
-pub mod authorizations;
-pub mod key_generations;
-pub mod run_as;
-
-static KS2_SERVICE_NAME: &str = "android.system.keystore2.IKeystoreService/default";
-
/// Represents the lifecycle of a temporary directory for testing.
#[derive(Debug)]
pub struct TempDir {
@@ -110,8 +102,3 @@ impl Deref for PathBuilder {
&self.0
}
}
-
-/// Get Keystore2 service.
-pub fn get_keystore_service() -> binder::Strong<dyn IKeystoreService> {
- binder::get_interface(KS2_SERVICE_NAME).unwrap()
-}
diff --git a/keystore2/test_utils/run_as.rs b/keystore2/test_utils/run_as.rs
deleted file mode 100644
index 2485ab57..00000000
--- a/keystore2/test_utils/run_as.rs
+++ /dev/null
@@ -1,474 +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 is intended for testing access control enforcement of services such as keystore2,
-//! by assuming various identities with varying levels of privilege. Consequently, appropriate
-//! privileges are required, or the attempt will fail causing a panic.
-//! The `run_as` module provides the function `run_as`, which takes a UID, GID, an SELinux
-//! context, and a closure. The return type of the closure, which is also the return type of
-//! `run_as`, must implement `serde::Serialize` and `serde::Deserialize`.
-//! `run_as` forks, transitions to the given identity, and executes the closure in the newly
-//! forked process. If the closure returns, i.e., does not panic, the forked process exits with
-//! a status of `0`, and the return value is serialized and sent through a pipe to the parent where
-//! it gets deserialized and returned. The STDIO is not changed and the parent's panic handler
-//! remains unchanged. So if the closure panics, the panic message is printed on the parent's STDERR
-//! and the exit status is set to a non `0` value. The latter causes the parent to panic as well,
-//! and if run in a test context, the test to fail.
-
-use keystore2_selinux as selinux;
-use nix::sys::wait::{waitpid, WaitStatus};
-use nix::unistd::{
- close, fork, pipe as nix_pipe, read as nix_read, setgid, setuid, write as nix_write,
- ForkResult, Gid, Pid, Uid,
-};
-use serde::{de::DeserializeOwned, Serialize};
-use std::io::{Read, Write};
-use std::marker::PhantomData;
-use std::os::unix::io::RawFd;
-
-fn transition(se_context: selinux::Context, uid: Uid, gid: Gid) {
- setgid(gid).expect("Failed to set GID. This test might need more privileges.");
- setuid(uid).expect("Failed to set UID. This test might need more privileges.");
-
- selinux::setcon(&se_context)
- .expect("Failed to set SELinux context. This test might need more privileges.");
-}
-
-/// 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 Drop for PipeWriter {
- fn drop(&mut self) {
- close(self.0).expect("Failed to close writer pipe fd.");
- }
-}
-
-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(())
- }
-}
-
-/// Denotes the sender side of a serializing channel.
-pub struct ChannelWriter<T: Serialize + DeserializeOwned>(PipeWriter, PhantomData<T>);
-
-impl<T: Serialize + DeserializeOwned> ChannelWriter<T> {
- /// Sends a serializable object to a the corresponding ChannelReader.
- /// Sending is always non blocking. Panics if any error occurs during io or serialization.
- pub fn send(&mut self, value: &T) {
- let serialized = serde_cbor::to_vec(value)
- .expect("In ChannelWriter::send: Failed to serialize to vector.");
- let size = serialized.len().to_be_bytes();
- match self.0.write(&size).expect("In ChannelWriter::send: Failed to write serialized size.")
- {
- w if w != std::mem::size_of::<usize>() => {
- panic!(
- "In ChannelWriter::send: Failed to write serialized size. (written: {}).",
- w
- );
- }
- _ => {}
- };
- match self
- .0
- .write(&serialized)
- .expect("In ChannelWriter::send: Failed to write serialized data.")
- {
- w if w != serialized.len() => {
- panic!(
- "In ChannelWriter::send: Failed to write serialized data. (written: {}).",
- w
- );
- }
- _ => {}
- };
- }
-}
-
-/// Represents the receiving and of a serializing channel.
-pub struct ChannelReader<T>(PipeReader, PhantomData<T>);
-
-impl<T: Serialize + DeserializeOwned> ChannelReader<T> {
- /// Receives a serializable object from the corresponding ChannelWriter.
- /// Receiving blocks until an object of type T has been read from the channel.
- /// Panics if an error occurs during io or deserialization.
- pub fn recv(&mut self) -> T {
- let mut size_buffer = [0u8; std::mem::size_of::<usize>()];
- match self.0.read(&mut size_buffer).expect("In ChannelReader::recv: Failed to read size.") {
- r if r != size_buffer.len() => {
- panic!("In ChannelReader::recv: Failed to read size. Insufficient data: {}", r);
- }
- _ => {}
- };
- let size = usize::from_be_bytes(size_buffer);
- let mut data_buffer = vec![0u8; size];
- match self
- .0
- .read(&mut data_buffer)
- .expect("In ChannelReader::recv: Failed to read serialized data.")
- {
- r if r != data_buffer.len() => {
- panic!(
- "In ChannelReader::recv: Failed to read serialized data. Insufficient data: {}",
- r
- );
- }
- _ => {}
- };
-
- serde_cbor::from_slice(&data_buffer)
- .expect("In ChannelReader::recv: Failed to deserialize data.")
- }
-}
-
-fn pipe() -> Result<(PipeReader, PipeWriter), nix::Error> {
- let (read_fd, write_fd) = nix_pipe()?;
- Ok((PipeReader(read_fd), PipeWriter(write_fd)))
-}
-
-fn pipe_channel<T>() -> Result<(ChannelReader<T>, ChannelWriter<T>), nix::Error>
-where
- T: Serialize + DeserializeOwned,
-{
- let (reader, writer) = pipe()?;
- Ok((
- ChannelReader::<T>(reader, Default::default()),
- ChannelWriter::<T>(writer, Default::default()),
- ))
-}
-
-/// Handle for handling child processes.
-pub struct ChildHandle<R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned> {
- pid: Pid,
- result_reader: ChannelReader<R>,
- cmd_writer: ChannelWriter<M>,
- response_reader: ChannelReader<M>,
- exit_status: Option<WaitStatus>,
-}
-
-impl<R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned> ChildHandle<R, M> {
- /// Send a command message to the child.
- pub fn send(&mut self, data: &M) {
- self.cmd_writer.send(data)
- }
-
- /// Receive a response from the child.
- pub fn recv(&mut self) -> M {
- self.response_reader.recv()
- }
-
- /// Get child result. Panics if the child did not exit with status 0 or if a serialization
- /// error occurred.
- pub fn get_result(mut self) -> R {
- let status =
- waitpid(self.pid, None).expect("ChildHandle::wait: Failed while waiting for child.");
- match status {
- WaitStatus::Exited(pid, 0) => {
- // Child exited successfully.
- // Read the result from the pipe.
- self.exit_status = Some(WaitStatus::Exited(pid, 0));
- self.result_reader.recv()
- }
- WaitStatus::Exited(pid, c) => {
- panic!("Child did not exit as expected: {:?}", WaitStatus::Exited(pid, c));
- }
- status => {
- panic!("Child did not exit at all: {:?}", status);
- }
- }
- }
-}
-
-impl<R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned> Drop for ChildHandle<R, M> {
- fn drop(&mut self) {
- if self.exit_status.is_none() {
- panic!("Child result not checked.")
- }
- }
-}
-
-/// Run the given closure in a new process running with the new identity given as
-/// `uid`, `gid`, and `se_context`. Parent process will run without waiting for child status.
-///
-/// # Safety
-/// run_as_child runs the given closure in the client branch of fork. And it uses non
-/// async signal safe API. This means that calling this function in a multi threaded program
-/// yields undefined behavior in the child. As of this writing, it is safe to call this function
-/// from a Rust device test, because every test itself is spawned as a separate process.
-///
-/// # Safety Binder
-/// It is okay for the closure to use binder services, however, this does not work
-/// if the parent initialized libbinder already. So do not use binder outside of the closure
-/// in your test.
-pub unsafe fn run_as_child<F, R, M>(
- se_context: &str,
- uid: Uid,
- gid: Gid,
- f: F,
-) -> Result<ChildHandle<R, M>, nix::Error>
-where
- R: Serialize + DeserializeOwned,
- M: Serialize + DeserializeOwned,
- F: 'static + Send + FnOnce(&mut ChannelReader<M>, &mut ChannelWriter<M>) -> R,
-{
- let se_context =
- selinux::Context::new(se_context).expect("Unable to construct selinux::Context.");
- let (result_reader, mut result_writer) = pipe_channel().expect("Failed to create pipe.");
- let (mut cmd_reader, cmd_writer) = pipe_channel().expect("Failed to create cmd pipe.");
- let (response_reader, mut response_writer) =
- pipe_channel().expect("Failed to create cmd pipe.");
-
- match fork() {
- Ok(ForkResult::Parent { child, .. }) => {
- drop(response_writer);
- drop(cmd_reader);
- drop(result_writer);
-
- Ok(ChildHandle::<R, M> {
- pid: child,
- result_reader,
- response_reader,
- cmd_writer,
- exit_status: None,
- })
- }
- Ok(ForkResult::Child) => {
- drop(cmd_writer);
- drop(response_reader);
- drop(result_reader);
-
- // This will panic on error or insufficient privileges.
- transition(se_context, uid, gid);
-
- // Run the closure.
- let result = f(&mut cmd_reader, &mut response_writer);
-
- // Serialize the result of the closure.
- result_writer.send(&result);
-
- // Set exit status to `0`.
- std::process::exit(0);
- }
- Err(errno) => {
- panic!("Failed to fork: {:?}", errno);
- }
- }
-}
-
-/// Run the given closure in a new process running with the new identity given as
-/// `uid`, `gid`, and `se_context`.
-///
-/// # Safety
-/// run_as runs the given closure in the client branch of fork. And it uses non
-/// async signal safe API. This means that calling this function in a multi threaded program
-/// yields undefined behavior in the child. As of this writing, it is safe to call this function
-/// from a Rust device test, because every test itself is spawned as a separate process.
-///
-/// # Safety Binder
-/// It is okay for the closure to use binder services, however, this does not work
-/// if the parent initialized libbinder already. So do not use binder outside of the closure
-/// in your test.
-pub unsafe fn run_as<F, R>(se_context: &str, uid: Uid, gid: Gid, f: F) -> R
-where
- R: Serialize + DeserializeOwned,
- F: 'static + Send + FnOnce() -> R,
-{
- let se_context =
- selinux::Context::new(se_context).expect("Unable to construct selinux::Context.");
- let (mut reader, mut writer) = pipe_channel::<R>().expect("Failed to create pipe.");
-
- match 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.
- // let serialized_result =
- // reader.read_all().expect("Failed to read result from child.");
-
- // Deserialize the result and return it.
- reader.recv()
- } else {
- panic!("Child did not exit as expected {:?}", status);
- }
- }
- Ok(ForkResult::Child) => {
- // This will panic on error or insufficient privileges.
- transition(se_context, uid, gid);
-
- // Run the closure.
- let result = f();
-
- // Serialize the result of the closure.
- writer.send(&result);
-
- // Set exit status to `0`.
- std::process::exit(0);
- }
- Err(errno) => {
- panic!("Failed to fork: {:?}", errno);
- }
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
- use keystore2_selinux as selinux;
- use nix::unistd::{getgid, getuid};
- use serde::{Deserialize, Serialize};
-
- /// This test checks that the closure does not produce an exit status of `0` when run inside a
- /// test and the closure panics. This would mask test failures as success.
- #[test]
- #[should_panic]
- fn test_run_as_panics_on_closure_panic() {
- // Safety: run_as must be called from a single threaded process.
- // This device test is run as a separate single threaded process.
- unsafe {
- run_as(selinux::getcon().unwrap().to_str().unwrap(), getuid(), getgid(), || {
- panic!("Closure must panic.")
- })
- };
- }
-
- static TARGET_UID: Uid = Uid::from_raw(10020);
- static TARGET_GID: Gid = Gid::from_raw(10020);
- static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
-
- /// Tests that the closure is running as the target identity.
- #[test]
- fn test_transition_to_untrusted_app() {
- // Safety: run_as must be called from a single threaded process.
- // This device test is run as a separate single threaded process.
- unsafe {
- run_as(TARGET_CTX, TARGET_UID, TARGET_GID, || {
- assert_eq!(TARGET_UID, getuid());
- assert_eq!(TARGET_GID, getgid());
- assert_eq!(TARGET_CTX, selinux::getcon().unwrap().to_str().unwrap());
- })
- };
- }
-
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
- struct SomeResult {
- a: u32,
- b: u64,
- c: String,
- }
-
- #[test]
- fn test_serialized_result() {
- let test_result = SomeResult {
- a: 5,
- b: 0xffffffffffffffff,
- c: "supercalifragilisticexpialidocious".to_owned(),
- };
- let test_result_clone = test_result.clone();
- // Safety: run_as must be called from a single threaded process.
- // This device test is run as a separate single threaded process.
- let result = unsafe { run_as(TARGET_CTX, TARGET_UID, TARGET_GID, || test_result_clone) };
- assert_eq!(test_result, result);
- }
-
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
- enum PingPong {
- Ping,
- Pong,
- }
-
- /// Tests that closure is running under given user identity and communicates with calling
- /// process using pipe.
- #[test]
- fn test_run_as_child() {
- let test_result = SomeResult {
- a: 5,
- b: 0xffffffffffffffff,
- c: "supercalifragilisticexpialidocious".to_owned(),
- };
- let test_result_clone = test_result.clone();
-
- // Safety: run_as_child must be called from a single threaded process.
- // This device test is run as a separate single threaded process.
- let mut child_handle: ChildHandle<SomeResult, PingPong> = unsafe {
- run_as_child(TARGET_CTX, TARGET_UID, TARGET_GID, |cmd_reader, response_writer| {
- assert_eq!(TARGET_UID, getuid());
- assert_eq!(TARGET_GID, getgid());
- assert_eq!(TARGET_CTX, selinux::getcon().unwrap().to_str().unwrap());
-
- let ping: PingPong = cmd_reader.recv();
- assert_eq!(ping, PingPong::Ping);
-
- response_writer.send(&PingPong::Pong);
-
- let ping: PingPong = cmd_reader.recv();
- assert_eq!(ping, PingPong::Ping);
- let pong: PingPong = cmd_reader.recv();
- assert_eq!(pong, PingPong::Pong);
-
- response_writer.send(&PingPong::Pong);
- response_writer.send(&PingPong::Ping);
-
- test_result_clone
- })
- .unwrap()
- };
-
- // Send one ping.
- child_handle.send(&PingPong::Ping);
-
- // Expect one pong.
- let pong = child_handle.recv();
- assert_eq!(pong, PingPong::Pong);
-
- // Send ping and pong.
- child_handle.send(&PingPong::Ping);
- child_handle.send(&PingPong::Pong);
-
- // Expect pong and ping.
- let pong = child_handle.recv();
- assert_eq!(pong, PingPong::Pong);
- let ping = child_handle.recv();
- assert_eq!(ping, PingPong::Ping);
-
- assert_eq!(child_handle.get_result(), test_result);
- }
-}
diff --git a/keystore2/tests/legacy_blobs/AndroidTest.xml b/keystore2/tests/legacy_blobs/AndroidTest.xml
deleted file mode 100644
index ea83fbf7..00000000
--- a/keystore2/tests/legacy_blobs/AndroidTest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 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.
--->
-<configuration description="Config to run keystore2_legacy_blobs_test device tests.">
-
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
- </target_preparer>
-
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option
- name="push"
- value="keystore2_legacy_blobs_test->/data/local/tmp/keystore2_legacy_blobs_test"
- />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
- <option name="test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="keystore2_legacy_blobs_test" />
- <option name="native-test-flag" value="--test-threads=1" />
- </test>
-</configuration>
diff --git a/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs b/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
deleted file mode 100644
index 6def39e2..00000000
--- a/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
+++ /dev/null
@@ -1,579 +0,0 @@
-// 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 serde::{Deserialize, Serialize};
-
-use std::ops::Deref;
-use std::path::PathBuf;
-
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel;
-
-use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, KeyDescriptor::KeyDescriptor,
-};
-
-use android_security_maintenance::aidl::android::security::maintenance::{
- IKeystoreMaintenance::IKeystoreMaintenance, UserState::UserState,
-};
-
-use android_security_authorization::aidl::android::security::authorization::{
- IKeystoreAuthorization::IKeystoreAuthorization, LockScreenEvent::LockScreenEvent,
-};
-
-use keystore2::key_parameter::KeyParameter as KsKeyparameter;
-use keystore2::legacy_blob::test_utils::legacy_blob_test_vectors::*;
-use keystore2::legacy_blob::test_utils::*;
-use keystore2::legacy_blob::LegacyKeyCharacteristics;
-use keystore2::utils::AesGcm;
-use keystore2_crypto::{Password, ZVec};
-
-use keystore2_test_utils::get_keystore_service;
-use keystore2_test_utils::key_generations;
-use keystore2_test_utils::run_as;
-
-static USER_MANAGER_SERVICE_NAME: &str = "android.security.maintenance";
-static AUTH_SERVICE_NAME: &str = "android.security.authorization";
-const SELINUX_SHELL_NAMESPACE: i64 = 1;
-
-fn get_maintenance() -> binder::Strong<dyn IKeystoreMaintenance> {
- binder::get_interface(USER_MANAGER_SERVICE_NAME).unwrap()
-}
-
-fn get_authorization() -> binder::Strong<dyn IKeystoreAuthorization> {
- binder::get_interface(AUTH_SERVICE_NAME).unwrap()
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
-struct KeygenResult {
- cert: Vec<u8>,
- cert_chain: Vec<u8>,
- key_parameters: Vec<KsKeyparameter>,
-}
-
-struct TestKey(ZVec);
-
-impl keystore2::utils::AesGcmKey for TestKey {
- fn key(&self) -> &[u8] {
- &self.0
- }
-}
-
-impl Deref for TestKey {
- type Target = [u8];
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-fn keystore2_restart_service() {
- let output = std::process::Command::new("pidof")
- .arg("keystore2")
- .output()
- .expect("failed to execute pidof keystore2");
-
- let id = String::from_utf8(output.stdout).unwrap();
- let id: String = id.chars().filter(|c| c.is_digit(10)).collect();
-
- let _status = std::process::Command::new("kill").arg("-9").arg(id).status().unwrap();
-
- // Loop till we find keystore2 service up and running.
- loop {
- let output = std::process::Command::new("pidof")
- .arg("keystore2")
- .output()
- .expect("failed to execute pidof keystore2");
-
- if output.status.code() == Some(0) {
- break;
- }
- }
-}
-
-/// Create legacy blobs file layout for a user with user-id 99 and app-id 10001 with
-/// user-cert, ca-certs and encrypted key-characteristics files and tries to import
-/// these legacy blobs under user context.
-///
-/// Expected File layout for user with user-id "98" and app-id "10001" and key-alias
-/// "authbound":
-/// /data/misc/keystore/user_99/.masterkey
-/// /data/misc/keystore/user_99/9910001_USRPKEY_authbound
-/// /data/misc/keystore/user_99/.9910001_chr_USRPKEY_authbound
-/// /data/misc/keystore/user_99/9910001_USRCERT_authbound
-/// /data/misc/keystore/user_99/9910001_CACERT_authbound
-///
-/// Test performs below tasks -
-/// With su context it performs following tasks -
-/// 1. Remove this user if already exist.
-/// 2. Generate a key-blob, user cert-blob and ca-cert-blob to store it in legacy blobs file
-/// layout.
-/// 3. Prepare file layout using generated key-blob, user cert and ca certs.
-/// 4. Restart the keystore2 service to make it detect the populated legacy blobs.
-/// 5. Inform the keystore2 service about the user and unlock the user.
-/// With user-99 context it performs following tasks -
-/// 6. To load and import the legacy key using its alias.
-/// 7. After successful key import validate the user cert and cert-chain with initially
-/// generated blobs.
-/// 8. Validate imported key perameters. Imported key parameters list should be the combination
-/// of the key-parameters in characteristics file and the characteristics according to
-/// the augmentation rules. There might be duplicate entries with different values for the
-/// parameters like OS_VERSION, OS_VERSION, BOOT_PATCHLEVEL, VENDOR_PATCHLEVEL etc.
-/// 9. Confirm keystore2 service cleanup the legacy blobs after successful import.
-#[test]
-fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
- let auid = 99 * AID_USER_OFFSET + 10001;
- let agid = 99 * AID_USER_OFFSET + 10001;
- static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
- static TARGET_SU_CTX: &str = "u:r:su:s0";
-
- // Cleanup user directory if it exists
- let path_buf = PathBuf::from("/data/misc/keystore/user_99");
- if path_buf.as_path().is_dir() {
- std::fs::remove_dir_all(path_buf.as_path()).unwrap();
- }
-
- // Safety: run_as must be called from a single threaded process.
- // This device test is run as a separate single threaded process.
- let mut gen_key_result = unsafe {
- run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- // Remove user if already exist.
- let maint_service = get_maintenance();
- match maint_service.onUserRemoved(99) {
- Ok(_) => {
- println!("User was existed, deleted successfully");
- }
- Err(e) => {
- println!("onUserRemoved error: {:#?}", e);
- }
- }
-
- let keystore2 = get_keystore_service();
- let sec_level = keystore2
- .getSecurityLevel(SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT)
- .unwrap();
- // Generate Key BLOB and prepare legacy keystore blob files.
- let key_metadata =
- key_generations::generate_ec_p256_signing_key_with_attestation(&sec_level)
- .expect("Failed to generate key blob");
-
- // Create keystore file layout for user_99.
- let pw: Password = PASSWORD.into();
- let pw_key = TestKey(pw.derive_key(Some(SUPERKEY_SALT), 32).unwrap());
- let super_key =
- TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap());
-
- let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
- if !path_buf.as_path().is_dir() {
- std::fs::create_dir(path_buf.as_path()).unwrap();
- }
- path_buf.push(".masterkey");
- if !path_buf.as_path().is_file() {
- std::fs::write(path_buf.as_path(), SUPERKEY).unwrap();
- }
-
- let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
- path_buf.push("9910001_USRPKEY_authbound");
- if !path_buf.as_path().is_file() {
- make_encrypted_key_file(
- path_buf.as_path(),
- &super_key,
- &key_metadata.key.blob.unwrap(),
- )
- .unwrap();
- }
-
- let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
- path_buf.push(".9910001_chr_USRPKEY_authbound");
- if !path_buf.as_path().is_file() {
- make_encrypted_characteristics_file(path_buf.as_path(), &super_key, KEY_PARAMETERS)
- .unwrap();
- }
-
- let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
- path_buf.push("9910001_USRCERT_authbound");
- if !path_buf.as_path().is_file() {
- make_cert_blob_file(path_buf.as_path(), key_metadata.certificate.as_ref().unwrap())
- .unwrap();
- }
-
- let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
- path_buf.push("9910001_CACERT_authbound");
- if !path_buf.as_path().is_file() {
- make_cert_blob_file(
- path_buf.as_path(),
- key_metadata.certificateChain.as_ref().unwrap(),
- )
- .unwrap();
- }
-
- // Keystore2 disables the legacy importer when it finds the legacy database empty.
- // However, if the device boots with an empty legacy database, the optimization kicks in
- // and keystore2 never checks the legacy file system layout.
- // So, restart keystore2 service to detect populated legacy database.
- keystore2_restart_service();
-
- let auth_service = get_authorization();
- match auth_service.onLockScreenEvent(LockScreenEvent::UNLOCK, 99, Some(PASSWORD), None)
- {
- Ok(result) => {
- println!("Unlock Result: {:?}", result);
- }
- Err(e) => {
- panic!("Unlock should have succeeded: {:?}", e);
- }
- }
-
- let maint_service = get_maintenance();
- assert_eq!(Ok(UserState(1)), maint_service.getState(99));
-
- let mut key_params: Vec<KsKeyparameter> = Vec::new();
- for param in key_metadata.authorizations {
- let key_param = KsKeyparameter::new(param.keyParameter.into(), param.securityLevel);
- key_params.push(key_param);
- }
-
- KeygenResult {
- cert: key_metadata.certificate.unwrap(),
- cert_chain: key_metadata.certificateChain.unwrap(),
- key_parameters: key_params,
- }
- })
- };
-
- // Safety: run_as must be called from a single threaded process.
- // This device test is run as a separate single threaded process.
- unsafe {
- run_as::run_as(TARGET_CTX, Uid::from_raw(auid), Gid::from_raw(agid), move || {
- println!("UID: {}", getuid());
- println!("Android User ID: {}", rustutils::users::multiuser_get_user_id(9910001));
- println!("Android app ID: {}", rustutils::users::multiuser_get_app_id(9910001));
-
- let test_alias = "authbound";
- let keystore2 = get_keystore_service();
-
- match keystore2.getKeyEntry(&KeyDescriptor {
- domain: Domain::APP,
- nspace: SELINUX_SHELL_NAMESPACE,
- alias: Some(test_alias.to_string()),
- blob: None,
- }) {
- Ok(key_entry_response) => {
- assert_eq!(
- key_entry_response.metadata.certificate.unwrap(),
- gen_key_result.cert
- );
- assert_eq!(
- key_entry_response.metadata.certificateChain.unwrap(),
- gen_key_result.cert_chain
- );
- assert_eq!(key_entry_response.metadata.key.domain, Domain::KEY_ID);
- assert_ne!(key_entry_response.metadata.key.nspace, 0);
- assert_eq!(
- key_entry_response.metadata.keySecurityLevel,
- SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT
- );
-
- // Preapare KsKeyParameter list from getKeEntry response Authorizations.
- let mut key_params: Vec<KsKeyparameter> = Vec::new();
- for param in key_entry_response.metadata.authorizations {
- let key_param =
- KsKeyparameter::new(param.keyParameter.into(), param.securityLevel);
- key_params.push(key_param);
- }
-
- // Combine keyparameters from gen_key_result and keyparameters
- // from legacy key-char file.
- let mut legacy_file_key_params: Vec<KsKeyparameter> = Vec::new();
- match structured_test_params() {
- LegacyKeyCharacteristics::File(legacy_key_params) => {
- for param in &legacy_key_params {
- let mut present_in_gen_params = false;
- for gen_param in &gen_key_result.key_parameters {
- if param.get_tag() == gen_param.get_tag() {
- present_in_gen_params = true;
- }
- }
- if !present_in_gen_params {
- legacy_file_key_params.push(param.clone());
- }
- }
- }
- _ => {
- panic!("Expecting file characteristics");
- }
- }
-
- // Remove Key-Params which have security levels other than TRUSTED_ENVIRONMENT
- gen_key_result.key_parameters.retain(|in_element| {
- *in_element.security_level()
- == SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT
- });
-
- println!("GetKeyEntry response key params: {:#?}", key_params);
- println!("Generated key params: {:#?}", gen_key_result.key_parameters);
-
- gen_key_result.key_parameters.append(&mut legacy_file_key_params);
-
- println!("Combined key params: {:#?}", gen_key_result.key_parameters);
-
- // Validate all keyparameters present in getKeyEntry response.
- for param in &key_params {
- gen_key_result.key_parameters.retain(|in_element| *in_element != *param);
- }
-
- println!(
- "GetKeyEntry response unmatched key params: {:#?}",
- gen_key_result.key_parameters
- );
- assert_eq!(gen_key_result.key_parameters.len(), 0);
- }
- Err(s) => {
- panic!("getKeyEntry should have succeeded. {:?}", s);
- }
- };
- })
- };
-
- // Make sure keystore2 clean up imported legacy db.
- let path_buf = PathBuf::from("/data/misc/keystore/user_99");
- if path_buf.as_path().is_dir() {
- panic!("Keystore service should have deleted this dir {:?}", path_buf);
- }
- Ok(())
-}
-
-/// Create legacy blobs file layout for a user with user-id 98 and app-id 10001 with encrypted
-/// user-cert and ca-certs files and tries to import these legacy blobs under user context.
-///
-/// Expected File layout for user with user-id "98" and app-id "10001" and key-alias
-/// "authboundcertenc":
-/// /data/misc/keystore/user_98/.masterkey
-/// /data/misc/keystore/user_98/9810001_USRPKEY_authboundcertenc
-/// /data/misc/keystore/user_98/.9810001_chr_USRPKEY_authboundcertenc
-/// /data/misc/keystore/user_98/9810001_USRCERT_authboundcertenc
-/// /data/misc/keystore/user_98/9810001_CACERT_authboundcertenc
-///
-/// Test performs below tasks -
-/// With su context it performs following tasks -
-/// 1. Remove this user if already exist.
-/// 2. Generate a key-blob, user cert-blob and ca-cert-blob to store it in legacy blobs file
-/// layout.
-/// 3. Prepare file layout using generated key-blob, user cert and ca certs.
-/// 4. Restart the keystore2 service to make it detect the populated legacy blobs.
-/// 5. Inform the keystore2 service about the user and unlock the user.
-/// With user-98 context it performs following tasks -
-/// 6. To load and import the legacy key using its alias.
-/// 7. After successful key import validate the user cert and cert-chain with initially
-/// generated blobs.
-/// 8. Validate imported key perameters. Imported key parameters list should be the combination
-/// of the key-parameters in characteristics file and the characteristics according to
-/// the augmentation rules. There might be duplicate entries with different values for the
-/// parameters like OS_VERSION, OS_VERSION, BOOT_PATCHLEVEL, VENDOR_PATCHLEVEL etc.
-/// 9. Confirm keystore2 service cleanup the legacy blobs after successful import.
-#[test]
-fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
- let auid = 98 * AID_USER_OFFSET + 10001;
- let agid = 98 * AID_USER_OFFSET + 10001;
- static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
- static TARGET_SU_CTX: &str = "u:r:su:s0";
-
- // Cleanup user directory if it exists
- let path_buf = PathBuf::from("/data/misc/keystore/user_98");
- if path_buf.as_path().is_dir() {
- std::fs::remove_dir_all(path_buf.as_path()).unwrap();
- }
-
- // Safety: run_as must be called from a single threaded process.
- // This device test is run as a separate single threaded process.
- let gen_key_result = unsafe {
- run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- // Remove user if already exist.
- let maint_service = get_maintenance();
- match maint_service.onUserRemoved(98) {
- Ok(_) => {
- println!("User was existed, deleted successfully");
- }
- Err(e) => {
- println!("onUserRemoved error: {:#?}", e);
- }
- }
-
- let keystore2 = get_keystore_service();
- let sec_level = keystore2
- .getSecurityLevel(SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT)
- .unwrap();
- // Generate Key BLOB and prepare legacy keystore blob files.
- let key_metadata =
- key_generations::generate_ec_p256_signing_key_with_attestation(&sec_level)
- .expect("Failed to generate key blob");
-
- // Create keystore file layout for user_98.
- let pw: Password = PASSWORD.into();
- let pw_key = TestKey(pw.derive_key(Some(SUPERKEY_SALT), 32).unwrap());
- let super_key =
- TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap());
-
- let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
- if !path_buf.as_path().is_dir() {
- std::fs::create_dir(path_buf.as_path()).unwrap();
- }
- path_buf.push(".masterkey");
- if !path_buf.as_path().is_file() {
- std::fs::write(path_buf.as_path(), SUPERKEY).unwrap();
- }
-
- let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
- path_buf.push("9810001_USRPKEY_authboundcertenc");
- if !path_buf.as_path().is_file() {
- make_encrypted_key_file(
- path_buf.as_path(),
- &super_key,
- &key_metadata.key.blob.unwrap(),
- )
- .unwrap();
- }
-
- let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
- path_buf.push(".9810001_chr_USRPKEY_authboundcertenc");
- if !path_buf.as_path().is_file() {
- std::fs::write(path_buf.as_path(), USRPKEY_AUTHBOUND_CHR).unwrap();
- }
-
- let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
- path_buf.push("9810001_USRCERT_authboundcertenc");
- if !path_buf.as_path().is_file() {
- make_encrypted_usr_cert_file(
- path_buf.as_path(),
- &super_key,
- key_metadata.certificate.as_ref().unwrap(),
- )
- .unwrap();
- }
-
- let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
- path_buf.push("9810001_CACERT_authboundcertenc");
- if !path_buf.as_path().is_file() {
- make_encrypted_ca_cert_file(
- path_buf.as_path(),
- &super_key,
- key_metadata.certificateChain.as_ref().unwrap(),
- )
- .unwrap();
- }
-
- // Keystore2 disables the legacy importer when it finds the legacy database empty.
- // However, if the device boots with an empty legacy database, the optimization kicks in
- // and keystore2 never checks the legacy file system layout.
- // So, restart keystore2 service to detect populated legacy database.
- keystore2_restart_service();
-
- let auth_service = get_authorization();
- match auth_service.onLockScreenEvent(LockScreenEvent::UNLOCK, 98, Some(PASSWORD), None)
- {
- Ok(result) => {
- println!("Unlock Result: {:?}", result);
- }
- Err(e) => {
- panic!("Unlock should have succeeded: {:?}", e);
- }
- }
-
- let maint_service = get_maintenance();
- assert_eq!(Ok(UserState(1)), maint_service.getState(98));
-
- let mut key_params: Vec<KsKeyparameter> = Vec::new();
- for param in key_metadata.authorizations {
- let key_param = KsKeyparameter::new(param.keyParameter.into(), param.securityLevel);
- key_params.push(key_param);
- }
-
- KeygenResult {
- cert: key_metadata.certificate.unwrap(),
- cert_chain: key_metadata.certificateChain.unwrap(),
- key_parameters: key_params,
- }
- })
- };
-
- // Safety: run_as must be called from a single threaded process.
- // This device test is run as a separate single threaded process.
- unsafe {
- run_as::run_as(TARGET_CTX, Uid::from_raw(auid), Gid::from_raw(agid), move || {
- println!("UID: {}", getuid());
- println!("Android User ID: {}", rustutils::users::multiuser_get_user_id(9810001));
- println!("Android app ID: {}", rustutils::users::multiuser_get_app_id(9810001));
-
- let test_alias = "authboundcertenc";
- let keystore2 = get_keystore_service();
-
- match keystore2.getKeyEntry(&KeyDescriptor {
- domain: Domain::APP,
- nspace: SELINUX_SHELL_NAMESPACE,
- alias: Some(test_alias.to_string()),
- blob: None,
- }) {
- Ok(key_entry_response) => {
- assert_eq!(
- key_entry_response.metadata.certificate.unwrap(),
- gen_key_result.cert
- );
- assert_eq!(
- key_entry_response.metadata.certificateChain.unwrap(),
- gen_key_result.cert_chain
- );
-
- // Preapare KsKeyParameter list from getKeEntry response Authorizations.
- let mut key_params: Vec<KsKeyparameter> = Vec::new();
- for param in key_entry_response.metadata.authorizations {
- let key_param =
- KsKeyparameter::new(param.keyParameter.into(), param.securityLevel);
- key_params.push(key_param);
- }
-
- println!("GetKeyEntry response key params: {:#?}", key_params);
- println!("Generated key params: {:#?}", gen_key_result.key_parameters);
- match structured_test_params_cache() {
- LegacyKeyCharacteristics::Cache(legacy_key_params) => {
- println!("Legacy key-char cache: {:#?}", legacy_key_params);
- // Validate all keyparameters present in getKeyEntry response.
- for param in &legacy_key_params {
- key_params.retain(|in_element| *in_element != *param);
- }
-
- println!(
- "GetKeyEntry response unmatched key params: {:#?}",
- key_params
- );
- assert_eq!(key_params.len(), 0);
- }
- _ => {
- panic!("Expecting file characteristics");
- }
- }
- }
- Err(s) => {
- panic!("getKeyEntry should have succeeded. {:?}", s);
- }
- };
- })
- };
-
- // Make sure keystore2 clean up imported legacy db.
- let path_buf = PathBuf::from("/data/misc/keystore/user_98");
- if path_buf.as_path().is_dir() {
- panic!("Keystore service should have deleted this dir {:?}", path_buf);
- }
- Ok(())
-}
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
index d73f8fe5..432e5854 100644
--- a/ondevice-signing/Android.bp
+++ b/ondevice-signing/Android.bp
@@ -11,6 +11,8 @@
// 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.
+// List of clang-tidy checks that are reported as errors.
+// Please keep this list ordered lexicographically.
package {
// See: http://go/android-license-faq
@@ -21,8 +23,6 @@ package {
default_applicable_licenses: ["system_security_license"],
}
-// List of clang-tidy checks that are reported as errors.
-// Please keep this list ordered lexicographically.
tidy_errors = [
"cert-err34-c",
"google-default-arguments",
@@ -74,33 +74,6 @@ cc_defaults {
],
}
-cc_library {
- name: "libsigningutils",
- defaults: [
- "odsign_flags_defaults",
- ],
- cpp_std: "experimental",
- srcs: [
- "CertUtils.cpp",
- "VerityUtils.cpp",
- ],
-
- static_libs: [
- "libc++fs",
- ],
-
- shared_libs: [
- "libbase",
- "libcrypto",
- "libcrypto_utils",
- "libfsverity",
- "libprotobuf-cpp-lite",
- "libutils",
- ],
- export_include_dirs: ["include"],
- recovery_available: true,
-}
-
cc_binary {
name: "odsign",
defaults: [
@@ -109,19 +82,20 @@ cc_binary {
cpp_std: "experimental",
init_rc: ["odsign.rc"],
srcs: [
+ "odsign_main.cpp",
+ "CertUtils.cpp",
"KeystoreKey.cpp",
"KeystoreHmacKey.cpp",
- "odsign_main.cpp",
- "StatsReporter.cpp",
+ "VerityUtils.cpp",
],
header_libs: ["odrefresh_headers"],
static_libs: [
"libc++fs",
- "libsigningutils",
"lib_odsign_proto",
],
+
shared_libs: [
"android.system.keystore2-V1-cpp",
"android.hardware.security.keymint-V1-cpp",
@@ -131,7 +105,7 @@ cc_binary {
"libcrypto_utils",
"libfsverity",
"liblogwrap",
- "libprotobuf-cpp-lite",
+ "libprotobuf-cpp-full",
"libutils",
],
}
diff --git a/ondevice-signing/CertUtils.cpp b/ondevice-signing/CertUtils.cpp
index 8fe0816c..b0b75a6b 100644
--- a/ondevice-signing/CertUtils.cpp
+++ b/ondevice-signing/CertUtils.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "CertUtils.h"
-
#include <android-base/logging.h>
#include <android-base/result.h>
@@ -23,126 +21,54 @@
#include <openssl/crypto.h>
#include <openssl/pkcs7.h>
#include <openssl/rsa.h>
-#include <openssl/x509.h>
#include <openssl/x509v3.h>
-#include <optional>
+#include <fcntl.h>
#include <vector>
#include "KeyConstants.h"
-// Common properties for all of our certificates.
+const char kBasicConstraints[] = "CA:TRUE";
+const char kKeyUsage[] = "critical,keyCertSign,cRLSign,digitalSignature";
+const char kSubjectKeyIdentifier[] = "hash";
constexpr int kCertLifetimeSeconds = 10 * 365 * 24 * 60 * 60;
-const char* const kIssuerCountry = "US";
-const char* const kIssuerOrg = "Android";
-using android::base::ErrnoError;
-using android::base::Error;
using android::base::Result;
+// using android::base::ErrnoError;
+using android::base::Error;
-static Result<bssl::UniquePtr<X509>> loadX509(const std::string& path) {
- X509* rawCert;
- auto f = fopen(path.c_str(), "re");
- if (f == nullptr) {
- return Error() << "Failed to open " << path;
- }
- if (!d2i_X509_fp(f, &rawCert)) {
- fclose(f);
- return Error() << "Unable to decode x509 cert at " << path;
- }
- bssl::UniquePtr<X509> cert(rawCert);
+static bool add_ext(X509* cert, int nid, const char* value) {
+ size_t len = strlen(value) + 1;
+ std::vector<char> mutableValue(value, value + len);
+ X509V3_CTX context;
- fclose(f);
- return cert;
-}
+ X509V3_set_ctx_nodb(&context);
-static X509V3_CTX makeContext(X509* issuer, X509* subject) {
- X509V3_CTX context = {};
- X509V3_set_ctx(&context, issuer, subject, nullptr, nullptr, 0);
- return context;
-}
-
-static bool add_ext(X509V3_CTX* context, X509* cert, int nid, const char* value) {
- bssl::UniquePtr<X509_EXTENSION> ex(X509V3_EXT_nconf_nid(nullptr, context, nid, value));
+ X509V3_set_ctx(&context, cert, cert, nullptr, nullptr, 0);
+ X509_EXTENSION* ex = X509V3_EXT_nconf_nid(nullptr, &context, nid, mutableValue.data());
if (!ex) {
return false;
}
- X509_add_ext(cert, ex.get(), -1);
+ X509_add_ext(cert, ex, -1);
+ X509_EXTENSION_free(ex);
return true;
}
-static void addNameEntry(X509_NAME* name, const char* field, const char* value) {
- X509_NAME_add_entry_by_txt(name, field, MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>(value), -1, -1, 0);
-}
-
-static Result<bssl::UniquePtr<RSA>> getRsaFromModulus(const std::vector<uint8_t>& publicKey) {
- bssl::UniquePtr<BIGNUM> n(BN_new());
- bssl::UniquePtr<BIGNUM> e(BN_new());
+Result<bssl::UniquePtr<RSA>> getRsa(const std::vector<uint8_t>& publicKey) {
bssl::UniquePtr<RSA> rsaPubkey(RSA_new());
- if (!n || !e || !rsaPubkey || !BN_bin2bn(publicKey.data(), publicKey.size(), n.get()) ||
- !BN_set_word(e.get(), kRsaKeyExponent) ||
- !RSA_set0_key(rsaPubkey.get(), n.get(), e.get(), /*d=*/nullptr)) {
- return Error() << "Failed to create RSA key";
- }
- // RSA_set0_key takes ownership of |n| and |e| on success.
- (void)n.release();
- (void)e.release();
+ rsaPubkey->n = BN_new();
+ rsaPubkey->e = BN_new();
- return rsaPubkey;
-}
+ BN_bin2bn(publicKey.data(), publicKey.size(), rsaPubkey->n);
+ BN_set_word(rsaPubkey->e, kRsaKeyExponent);
-static Result<bssl::UniquePtr<RSA>>
-getRsaFromRsaPublicKey(const std::vector<uint8_t>& rsaPublicKey) {
- auto derBytes = rsaPublicKey.data();
- bssl::UniquePtr<RSA> rsaKey(d2i_RSAPublicKey(nullptr, &derBytes, rsaPublicKey.size()));
- if (rsaKey.get() == nullptr) {
- return Error() << "Failed to parse RsaPublicKey";
- }
- if (derBytes != rsaPublicKey.data() + rsaPublicKey.size()) {
- return Error() << "Key has unexpected trailing data";
- }
-
- return rsaKey;
-}
-
-static Result<bssl::UniquePtr<EVP_PKEY>> modulusToRsaPkey(const std::vector<uint8_t>& publicKey) {
- // "publicKey" corresponds to the raw public key bytes - need to create
- // a new RSA key with the correct exponent.
- auto rsaPubkey = getRsaFromModulus(publicKey);
- if (!rsaPubkey.ok()) {
- return rsaPubkey.error();
- }
-
- bssl::UniquePtr<EVP_PKEY> public_key(EVP_PKEY_new());
- if (!EVP_PKEY_assign_RSA(public_key.get(), rsaPubkey->release())) {
- return Error() << "Failed to assign key";
- }
- return public_key;
-}
-
-static Result<bssl::UniquePtr<EVP_PKEY>>
-rsaPublicKeyToRsaPkey(const std::vector<uint8_t>& rsaPublicKey) {
- // rsaPublicKey contains both modulus and exponent, DER-encoded.
- auto rsaKey = getRsaFromRsaPublicKey(rsaPublicKey);
- if (!rsaKey.ok()) {
- return rsaKey.error();
- }
-
- bssl::UniquePtr<EVP_PKEY> public_key(EVP_PKEY_new());
- if (!EVP_PKEY_assign_RSA(public_key.get(), rsaKey->release())) {
- return Error() << "Failed to assign key";
- }
- return public_key;
+ return rsaPubkey;
}
Result<void> verifySignature(const std::string& message, const std::string& signature,
const std::vector<uint8_t>& publicKey) {
- auto rsaKey = getRsaFromModulus(publicKey);
- if (!rsaKey.ok()) {
- return rsaKey.error();
- }
+ auto rsaKey = getRsa(publicKey);
uint8_t hashBuf[SHA256_DIGEST_LENGTH];
SHA256(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(message.c_str())),
message.length(), hashBuf);
@@ -151,172 +77,101 @@ Result<void> verifySignature(const std::string& message, const std::string& sign
(const uint8_t*)signature.c_str(), signature.length(), rsaKey->get());
if (!success) {
- return Error() << "Failed to verify signature";
- }
- return {};
-}
-
-Result<void> verifyRsaPublicKeySignature(const std::string& message, const std::string& signature,
- const std::vector<uint8_t>& rsaPublicKey) {
- auto rsaKey = getRsaFromRsaPublicKey(rsaPublicKey);
- if (!rsaKey.ok()) {
- return rsaKey.error();
- }
-
- uint8_t hashBuf[SHA256_DIGEST_LENGTH];
- SHA256(reinterpret_cast<const uint8_t*>(message.data()), message.size(), hashBuf);
-
- bool success = RSA_verify(NID_sha256, hashBuf, sizeof(hashBuf),
- reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
- rsaKey->get());
- if (!success) {
- return Error() << "Failed to verify signature";
+ return Error() << "Failed to verify signature.";
}
return {};
}
-static Result<void> createCertificate(
- const CertSubject& subject, EVP_PKEY* publicKey,
- const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
- const std::optional<std::string>& issuerCertPath, const std::string& path) {
-
- // If an issuer cert is specified, we are signing someone else's key.
- // Otherwise we are signing our key - a self-signed certificate.
- bool selfSigned = !issuerCertPath;
-
+Result<void> createSelfSignedCertificate(
+ const std::vector<uint8_t>& publicKey,
+ const std::function<Result<std::string>(const std::string&)>& signFunction,
+ const std::string& path) {
bssl::UniquePtr<X509> x509(X509_new());
if (!x509) {
return Error() << "Unable to allocate x509 container";
}
X509_set_version(x509.get(), 2);
+
+ ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 1);
X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
X509_gmtime_adj(X509_get_notAfter(x509.get()), kCertLifetimeSeconds);
- ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), subject.serialNumber);
-
- bssl::UniquePtr<X509_ALGOR> algor(X509_ALGOR_new());
- if (!algor ||
- !X509_ALGOR_set0(algor.get(), OBJ_nid2obj(NID_sha256WithRSAEncryption), V_ASN1_NULL,
- NULL) ||
- !X509_set1_signature_algo(x509.get(), algor.get())) {
- return Error() << "Unable to set x509 signature algorithm";
- }
- if (!X509_set_pubkey(x509.get(), publicKey)) {
+ // "publicKey" corresponds to the raw public key bytes - need to create
+ // a new RSA key with the correct exponent.
+ auto rsaPubkey = getRsa(publicKey);
+
+ EVP_PKEY* public_key = EVP_PKEY_new();
+ EVP_PKEY_assign_RSA(public_key, rsaPubkey->release());
+
+ if (!X509_set_pubkey(x509.get(), public_key)) {
return Error() << "Unable to set x509 public key";
}
- X509_NAME* subjectName = X509_get_subject_name(x509.get());
- if (!subjectName) {
+ X509_NAME* name = X509_get_subject_name(x509.get());
+ if (!name) {
return Error() << "Unable to get x509 subject name";
}
- addNameEntry(subjectName, "C", kIssuerCountry);
- addNameEntry(subjectName, "O", kIssuerOrg);
- addNameEntry(subjectName, "CN", subject.commonName);
-
- if (selfSigned) {
- if (!X509_set_issuer_name(x509.get(), subjectName)) {
- return Error() << "Unable to set x509 issuer name";
- }
- } else {
- X509_NAME* issuerName = X509_get_issuer_name(x509.get());
- if (!issuerName) {
- return Error() << "Unable to get x509 issuer name";
- }
- addNameEntry(issuerName, "C", kIssuerCountry);
- addNameEntry(issuerName, "O", kIssuerOrg);
- addNameEntry(issuerName, "CN", kRootSubject.commonName);
- }
-
- // Beware: context contains a pointer to issuerCert, so we need to keep it alive.
- bssl::UniquePtr<X509> issuerCert;
- X509V3_CTX context;
-
- if (selfSigned) {
- context = makeContext(x509.get(), x509.get());
- } else {
- auto certStatus = loadX509(*issuerCertPath);
- if (!certStatus.ok()) {
- return Error() << "Unable to load issuer cert: " << certStatus.error();
- }
- issuerCert = std::move(certStatus.value());
- context = makeContext(issuerCert.get(), x509.get());
+ X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("ODS"), -1, -1, 0);
+ if (!X509_set_issuer_name(x509.get(), name)) {
+ return Error() << "Unable to set x509 issuer name";
}
- // If it's a self-signed cert we use it for signing certs, otherwise only for signing data.
- const char* basicConstraints = selfSigned ? "CA:TRUE" : "CA:FALSE";
- const char* keyUsage =
- selfSigned ? "critical,keyCertSign,cRLSign,digitalSignature" : "critical,digitalSignature";
+ add_ext(x509.get(), NID_basic_constraints, kBasicConstraints);
+ add_ext(x509.get(), NID_key_usage, kKeyUsage);
+ add_ext(x509.get(), NID_subject_key_identifier, kSubjectKeyIdentifier);
+ add_ext(x509.get(), NID_authority_key_identifier, "keyid:always");
- add_ext(&context, x509.get(), NID_basic_constraints, basicConstraints);
- add_ext(&context, x509.get(), NID_key_usage, keyUsage);
- add_ext(&context, x509.get(), NID_subject_key_identifier, "hash");
- add_ext(&context, x509.get(), NID_authority_key_identifier, "keyid:always");
+ X509_ALGOR_set0(x509->cert_info->signature, OBJ_nid2obj(NID_sha256WithRSAEncryption),
+ V_ASN1_NULL, NULL);
+ X509_ALGOR_set0(x509->sig_alg, OBJ_nid2obj(NID_sha256WithRSAEncryption), V_ASN1_NULL, NULL);
// Get the data to be signed
- unsigned char* to_be_signed_buf(nullptr);
- size_t to_be_signed_length = i2d_re_X509_tbs(x509.get(), &to_be_signed_buf);
+ char* to_be_signed_buf(nullptr);
+ size_t to_be_signed_length = i2d_re_X509_tbs(x509.get(), (unsigned char**)&to_be_signed_buf);
- auto signed_data = signFunction(
- std::string(reinterpret_cast<const char*>(to_be_signed_buf), to_be_signed_length));
+ auto signed_data = signFunction(std::string(to_be_signed_buf, to_be_signed_length));
if (!signed_data.ok()) {
return signed_data.error();
}
- if (!X509_set1_signature_value(x509.get(),
- reinterpret_cast<const uint8_t*>(signed_data->data()),
- signed_data->size())) {
- return Error() << "Unable to set x509 signature";
- }
+ // This is the only part that doesn't use boringssl default functions - we manually copy in the
+ // signature that was provided to us.
+ x509->signature->data = (unsigned char*)OPENSSL_malloc(signed_data->size());
+ memcpy(x509->signature->data, signed_data->c_str(), signed_data->size());
+ x509->signature->length = signed_data->size();
+ x509->signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+ x509->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
auto f = fopen(path.c_str(), "wbe");
if (f == nullptr) {
- return ErrnoError() << "Failed to open " << path;
+ return Error() << "Failed to open " << path;
}
i2d_X509_fp(f, x509.get());
- if (fclose(f) != 0) {
- return ErrnoError() << "Failed to close " << path;
- }
+ fclose(f);
+ EVP_PKEY_free(public_key);
return {};
}
-Result<void> createSelfSignedCertificate(
- const std::vector<uint8_t>& publicKey,
- const std::function<Result<std::string>(const std::string&)>& signFunction,
- const std::string& path) {
- auto rsa_pkey = modulusToRsaPkey(publicKey);
- if (!rsa_pkey.ok()) {
- return rsa_pkey.error();
- }
-
- return createCertificate(kRootSubject, rsa_pkey.value().get(), signFunction, {}, path);
-}
-
-android::base::Result<void> createLeafCertificate(
- const CertSubject& subject, const std::vector<uint8_t>& rsaPublicKey,
- const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
- const std::string& issuerCertPath, const std::string& path) {
- auto rsa_pkey = rsaPublicKeyToRsaPkey(rsaPublicKey);
- if (!rsa_pkey.ok()) {
- return rsa_pkey.error();
- }
-
- return createCertificate(subject, rsa_pkey.value().get(), signFunction, issuerCertPath, path);
-}
-
Result<std::vector<uint8_t>> extractPublicKey(EVP_PKEY* pkey) {
if (pkey == nullptr) {
return Error() << "Failed to extract public key from x509 cert";
}
- if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) {
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
return Error() << "The public key is not an RSA key";
}
- RSA* rsa = EVP_PKEY_get0_RSA(pkey);
- auto num_bytes = BN_num_bytes(RSA_get0_n(rsa));
+ RSA* rsa = EVP_PKEY_get1_RSA(pkey);
+ auto num_bytes = BN_num_bytes(rsa->n);
std::vector<uint8_t> pubKey(num_bytes);
- int res = BN_bn2bin(RSA_get0_n(rsa), pubKey.data());
+ int res = BN_bn2bin(rsa->n, pubKey.data());
+ RSA_free(rsa);
if (!res) {
return Error() << "Failed to convert public key to bytes";
@@ -328,14 +183,14 @@ Result<std::vector<uint8_t>> extractPublicKey(EVP_PKEY* pkey) {
Result<std::vector<uint8_t>>
extractPublicKeyFromSubjectPublicKeyInfo(const std::vector<uint8_t>& keyData) {
auto keyDataBytes = keyData.data();
- bssl::UniquePtr<EVP_PKEY> public_key(d2i_PUBKEY(nullptr, &keyDataBytes, keyData.size()));
+ EVP_PKEY* public_key = d2i_PUBKEY(nullptr, &keyDataBytes, keyData.size());
- return extractPublicKey(public_key.get());
+ return extractPublicKey(public_key);
}
-Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::vector<uint8_t>& derCert) {
- auto derCertBytes = derCert.data();
- bssl::UniquePtr<X509> decoded_cert(d2i_X509(nullptr, &derCertBytes, derCert.size()));
+Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::vector<uint8_t>& keyData) {
+ auto keyDataBytes = keyData.data();
+ bssl::UniquePtr<X509> decoded_cert(d2i_X509(nullptr, &keyDataBytes, keyData.size()));
if (decoded_cert.get() == nullptr) {
return Error() << "Failed to decode X509 certificate.";
}
@@ -345,75 +200,21 @@ Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::vector<uint8_t>
}
Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path) {
- auto cert = loadX509(path);
- if (!cert.ok()) {
- return cert.error();
- }
- return extractPublicKey(X509_get_pubkey(cert.value().get()));
-}
-
-static Result<std::vector<uint8_t>> extractRsaPublicKey(EVP_PKEY* pkey) {
- RSA* rsa = EVP_PKEY_get0_RSA(pkey);
- if (rsa == nullptr) {
- return Error() << "The public key is not an RSA key";
- }
-
- uint8_t* out = nullptr;
- int size = i2d_RSAPublicKey(rsa, &out);
- if (size < 0 || !out) {
- return Error() << "Failed to convert to RSAPublicKey";
- }
-
- bssl::UniquePtr<uint8_t> buffer(out);
- std::vector<uint8_t> result(out, out + size);
- return result;
-}
-
-Result<CertInfo> verifyAndExtractCertInfoFromX509(const std::string& path,
- const std::vector<uint8_t>& publicKey) {
- auto public_key = modulusToRsaPkey(publicKey);
- if (!public_key.ok()) {
- return public_key.error();
- }
-
- auto cert = loadX509(path);
- if (!cert.ok()) {
- return cert.error();
- }
- X509* x509 = cert.value().get();
-
- // Make sure we signed it.
- if (X509_verify(x509, public_key.value().get()) != 1) {
- return Error() << "Failed to verify certificate.";
- }
-
- bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(x509));
- auto subject_key = extractRsaPublicKey(pkey.get());
- if (!subject_key.ok()) {
- return subject_key.error();
+ X509* cert;
+ auto f = fopen(path.c_str(), "re");
+ if (f == nullptr) {
+ return Error() << "Failed to open " << path;
}
-
- // The pointers here are all owned by x509, and each function handles an
- // error return from the previous call correctly.
- X509_NAME* name = X509_get_subject_name(x509);
- int index = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
- X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, index);
- ASN1_STRING* asn1cn = X509_NAME_ENTRY_get_data(entry);
- unsigned char* utf8cn;
- int length = ASN1_STRING_to_UTF8(&utf8cn, asn1cn);
- if (length < 0) {
- return Error() << "Failed to read subject CN";
+ if (!d2i_X509_fp(f, &cert)) {
+ fclose(f);
+ return Error() << "Unable to decode x509 cert at " << path;
}
- bssl::UniquePtr<unsigned char> utf8owner(utf8cn);
- std::string cn(reinterpret_cast<char*>(utf8cn), static_cast<size_t>(length));
-
- CertInfo cert_info{std::move(cn), std::move(subject_key.value())};
- return cert_info;
+ fclose(f);
+ return extractPublicKey(X509_get_pubkey(cert));
}
-Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signed_digest,
- const CertSubject& signer) {
+Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signed_digest) {
CBB out, outer_seq, wrapped_seq, seq, digest_algos_set, digest_algo, null;
CBB content_info, issuer_and_serial, signer_infos, signer_info, sign_algo, signature;
uint8_t *pkcs7_data, *name_der;
@@ -421,20 +222,19 @@ Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signed_dige
BIGNUM* serial = BN_new();
int sig_nid = NID_rsaEncryption;
- X509_NAME* issuer_name = X509_NAME_new();
- if (!issuer_name) {
- return Error() << "Unable to create x509 subject name";
+ X509_NAME* name = X509_NAME_new();
+ if (!name) {
+ return Error() << "Unable to get x509 subject name";
}
- X509_NAME_add_entry_by_txt(issuer_name, "C", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>(kIssuerCountry), -1, -1, 0);
- X509_NAME_add_entry_by_txt(issuer_name, "O", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>(kIssuerOrg), -1, -1, 0);
- X509_NAME_add_entry_by_txt(issuer_name, "CN", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>(kRootSubject.commonName), -1,
- -1, 0);
-
- BN_set_word(serial, signer.serialNumber);
- name_der_len = i2d_X509_NAME(issuer_name, &name_der);
+ X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("ODS"), -1, -1, 0);
+
+ BN_set_word(serial, 1);
+ name_der_len = i2d_X509_NAME(name, &name_der);
CBB_init(&out, 1024);
if (!CBB_add_asn1(&out, &outer_seq, CBS_ASN1_SEQUENCE) ||
diff --git a/ondevice-signing/include/CertUtils.h b/ondevice-signing/CertUtils.h
index fe703fa0..66dff04f 100644
--- a/ondevice-signing/include/CertUtils.h
+++ b/ondevice-signing/CertUtils.h
@@ -16,43 +16,13 @@
#pragma once
-#include <functional>
-#include <string>
-#include <vector>
-
#include <android-base/result.h>
-// Information extracted from a certificate.
-struct CertInfo {
- std::string subjectCn;
- std::vector<uint8_t> subjectRsaPublicKey;
-};
-
-// Subjects of certificates we issue.
-struct CertSubject {
- const char* commonName;
- unsigned serialNumber;
-};
-
-// These are all the certificates we ever sign (the first one being our
-// self-signed cert). We shouldn't really re-use serial numbers for different
-// certificates for the same subject but we do; only one should be in use at a
-// time though.
-inline const CertSubject kRootSubject{"ODS", 1};
-inline const CertSubject kCompOsSubject{"CompOs", 2};
-
android::base::Result<void> createSelfSignedCertificate(
const std::vector<uint8_t>& publicKey,
const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
const std::string& path);
-
-android::base::Result<void> createLeafCertificate(
- const CertSubject& subject, const std::vector<uint8_t>& publicKey,
- const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
- const std::string& issuerCertPath, const std::string& outPath);
-
-android::base::Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signedData,
- const CertSubject& signer);
+android::base::Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signedData);
android::base::Result<std::vector<uint8_t>>
extractPublicKeyFromX509(const std::vector<uint8_t>& x509);
@@ -60,13 +30,6 @@ android::base::Result<std::vector<uint8_t>>
extractPublicKeyFromSubjectPublicKeyInfo(const std::vector<uint8_t>& subjectKeyInfo);
android::base::Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path);
-android::base::Result<CertInfo>
-verifyAndExtractCertInfoFromX509(const std::string& path, const std::vector<uint8_t>& publicKey);
-
android::base::Result<void> verifySignature(const std::string& message,
const std::string& signature,
const std::vector<uint8_t>& publicKey);
-
-android::base::Result<void> verifyRsaPublicKeySignature(const std::string& message,
- const std::string& signature,
- const std::vector<uint8_t>& rsaPublicKey);
diff --git a/ondevice-signing/KeystoreHmacKey.cpp b/ondevice-signing/KeystoreHmacKey.cpp
index 916cbbcd..a2208ce4 100644
--- a/ondevice-signing/KeystoreHmacKey.cpp
+++ b/ondevice-signing/KeystoreHmacKey.cpp
@@ -49,14 +49,17 @@ using android::base::Result;
using android::base::unique_fd;
-static KeyDescriptor getHmacKeyDescriptor(const android::String16& keyAlias, int64_t keyNspace) {
+// Keystore boot level that the odsign key uses
+static const int kOdsignBootLevel = 30;
+
+static KeyDescriptor getHmacKeyDescriptor() {
// AIDL parcelable objects don't have constructor
static KeyDescriptor descriptor;
static std::once_flag flag;
std::call_once(flag, [&]() {
descriptor.domain = Domain::SELINUX;
- descriptor.alias = keyAlias + android::String16("-hmac");
- descriptor.nspace = keyNspace;
+ descriptor.alias = String16("ondevice-signing-hmac");
+ descriptor.nspace = 101; // odsign_key
});
return descriptor;
@@ -103,13 +106,13 @@ Result<void> KeystoreHmacKey::createKey() {
KeyParameter boot_level;
boot_level.tag = Tag::MAX_BOOT_LEVEL;
- boot_level.value = KeyParameterValue::make<KeyParameterValue::integer>(mKeyBootLevel);
+ boot_level.value = KeyParameterValue::make<KeyParameterValue::integer>(kOdsignBootLevel);
params.push_back(boot_level);
KeyMetadata metadata;
auto status = mSecurityLevel->generateKey(mDescriptor, {}, params, 0, {}, &metadata);
if (!status.isOk()) {
- return Error() << "Failed to create new HMAC key: " << status;
+ return Error() << "Failed to create new HMAC key";
}
return {};
@@ -130,7 +133,7 @@ Result<void> KeystoreHmacKey::initialize(sp<IKeystoreService> service,
// Make sure this is an early boot key
for (const auto& auth : keyEntryResponse.metadata.authorizations) {
if (auth.keyParameter.tag == Tag::MAX_BOOT_LEVEL) {
- if (auth.keyParameter.value.get<KeyParameterValue::integer>() == mKeyBootLevel) {
+ if (auth.keyParameter.value.get<KeyParameterValue::integer>() == kOdsignBootLevel) {
keyValid = true;
break;
}
@@ -149,9 +152,9 @@ Result<void> KeystoreHmacKey::initialize(sp<IKeystoreService> service,
}
}
-KeystoreHmacKey::KeystoreHmacKey(const android::String16& keyAlias, int64_t keyNspace,
- int keyBootLevel)
- : mDescriptor(getHmacKeyDescriptor(keyAlias, keyNspace)), mKeyBootLevel(keyBootLevel) {}
+KeystoreHmacKey::KeystoreHmacKey() {
+ mDescriptor = getHmacKeyDescriptor();
+}
static std::vector<KeyParameter> getVerifyOpParameters() {
std::vector<KeyParameter> opParameters;
@@ -206,7 +209,8 @@ Result<std::string> KeystoreHmacKey::sign(const std::string& message) const {
auto status = mSecurityLevel->createOperation(mDescriptor, params, false, &opResponse);
if (!status.isOk()) {
- return Error() << "Failed to create keystore signing operation: " << status;
+ return Error() << "Failed to create keystore signing operation: "
+ << status.serviceSpecificErrorCode();
}
auto operation = opResponse.iOperation;
@@ -236,7 +240,8 @@ Result<void> KeystoreHmacKey::verify(const std::string& message,
auto status = mSecurityLevel->createOperation(mDescriptor, params, false, &opResponse);
if (!status.isOk()) {
- return Error() << "Failed to create keystore verification operation: " << status;
+ return Error() << "Failed to create keystore verification operation: "
+ << status.serviceSpecificErrorCode();
}
auto operation = opResponse.iOperation;
@@ -255,12 +260,3 @@ Result<void> KeystoreHmacKey::verify(const std::string& message,
return {};
}
-
-Result<void> KeystoreHmacKey::deleteKey() const {
- auto status = mService->deleteKey(mDescriptor);
- if (!status.isOk()) {
- return Error() << "Failed to delete HMAC key: " << status;
- }
-
- return {};
-}
diff --git a/ondevice-signing/KeystoreHmacKey.h b/ondevice-signing/KeystoreHmacKey.h
index 1a815a38..fbad0fda 100644
--- a/ondevice-signing/KeystoreHmacKey.h
+++ b/ondevice-signing/KeystoreHmacKey.h
@@ -31,19 +31,16 @@ class KeystoreHmacKey {
using KeyDescriptor = ::android::system::keystore2::KeyDescriptor;
public:
- KeystoreHmacKey(const android::String16& keyAlias, int64_t keyNspace, int keyBootLevel);
+ KeystoreHmacKey();
android::base::Result<void> initialize(android::sp<IKeystoreService> service,
android::sp<IKeystoreSecurityLevel> securityLevel);
android::base::Result<std::string> sign(const std::string& message) const;
android::base::Result<void> verify(const std::string& message,
const std::string& signature) const;
- android::base::Result<void> deleteKey() const;
private:
android::base::Result<void> createKey();
KeyDescriptor mDescriptor;
android::sp<IKeystoreService> mService;
android::sp<IKeystoreSecurityLevel> mSecurityLevel;
-
- int mKeyBootLevel;
};
diff --git a/ondevice-signing/KeystoreKey.cpp b/ondevice-signing/KeystoreKey.cpp
index 6ce65d65..0951d929 100644
--- a/ondevice-signing/KeystoreKey.cpp
+++ b/ondevice-signing/KeystoreKey.cpp
@@ -50,24 +50,27 @@ using android::system::keystore2::KeyEntryResponse;
using android::base::Error;
using android::base::Result;
-static KeyDescriptor getKeyDescriptor(const android::String16& keyAlias, int64_t keyNspace) {
+// Keystore boot level that the odsign key uses
+static const int kOdsignBootLevel = 30;
+
+const std::string kPublicKeySignature = "/data/misc/odsign/publickey.signature";
+
+static KeyDescriptor getKeyDescriptor() {
// AIDL parcelable objects don't have constructor
static KeyDescriptor descriptor;
static std::once_flag flag;
std::call_once(flag, [&]() {
descriptor.domain = Domain::SELINUX;
- descriptor.alias = keyAlias;
- descriptor.nspace = keyNspace;
+ descriptor.alias = String16("ondevice-signing");
+ descriptor.nspace = 101; // odsign_key
});
return descriptor;
}
-KeystoreKey::KeystoreKey(std::string signedPubKeyPath, const android::String16& keyAlias,
- int64_t keyNspace, int keyBootLevel)
- : mDescriptor(getKeyDescriptor(keyAlias, keyNspace)),
- mHmacKey(keyAlias, keyNspace, keyBootLevel), mSignedPubKeyPath(std::move(signedPubKeyPath)),
- mKeyBootLevel(keyBootLevel) {}
+KeystoreKey::KeystoreKey() {
+ mDescriptor = getKeyDescriptor();
+}
Result<std::vector<uint8_t>> KeystoreKey::createKey() {
std::vector<KeyParameter> params;
@@ -110,13 +113,13 @@ Result<std::vector<uint8_t>> KeystoreKey::createKey() {
KeyParameter boot_level;
boot_level.tag = Tag::MAX_BOOT_LEVEL;
- boot_level.value = KeyParameterValue::make<KeyParameterValue::integer>(mKeyBootLevel);
+ boot_level.value = KeyParameterValue::make<KeyParameterValue::integer>(kOdsignBootLevel);
params.push_back(boot_level);
KeyMetadata metadata;
auto status = mSecurityLevel->generateKey(mDescriptor, {}, params, 0, {}, &metadata);
if (!status.isOk()) {
- return Error() << "Failed to create new key: " << status;
+ return Error() << "Failed to create new key";
}
// Extract the public key from the certificate, HMAC it and store the signature
@@ -134,7 +137,7 @@ Result<std::vector<uint8_t>> KeystoreKey::createKey() {
return Error() << "Failed to sign public key.";
}
- if (!android::base::WriteStringToFile(*signature, mSignedPubKeyPath)) {
+ if (!android::base::WriteStringToFile(*signature, kPublicKeySignature)) {
return Error() << "Can't write public key signature.";
}
@@ -169,13 +172,11 @@ bool KeystoreKey::initialize() {
auto key = getOrCreateKey();
if (!key.ok()) {
- // Delete the HMAC, just in case signing failed, and we could recover by recreating it.
- mHmacKey.deleteKey();
LOG(ERROR) << key.error().message();
return false;
}
mPublicKey = *key;
- LOG(INFO) << "Initialized Keystore key.";
+ LOG(ERROR) << "Initialized Keystore key.";
return true;
}
@@ -203,7 +204,7 @@ Result<std::vector<uint8_t>> KeystoreKey::verifyExistingKey() {
bool foundBootLevel = false;
for (const auto& auth : keyEntryResponse.metadata.authorizations) {
if (auth.keyParameter.tag == Tag::MAX_BOOT_LEVEL) {
- if (auth.keyParameter.value.get<KeyParameterValue::integer>() == mKeyBootLevel) {
+ if (auth.keyParameter.value.get<KeyParameterValue::integer>() == kOdsignBootLevel) {
foundBootLevel = true;
break;
}
@@ -229,7 +230,7 @@ Result<std::vector<uint8_t>> KeystoreKey::verifyExistingKey() {
std::string publicKeyString = {publicKey->begin(), publicKey->end()};
std::string signature;
- if (!android::base::ReadFileToString(mSignedPubKeyPath, &signature)) {
+ if (!android::base::ReadFileToString(kPublicKeySignature, &signature)) {
return Error() << "Can't find signature for public key.";
}
@@ -253,15 +254,13 @@ Result<std::vector<uint8_t>> KeystoreKey::getOrCreateKey() {
return *existingKey;
}
-Result<SigningKey*> KeystoreKey::getInstance(const std::string& signedPubKeyPath,
- const android::String16& keyAlias, int64_t keyNspace,
- int keyBootLevel) {
- auto keystoreKey = new KeystoreKey(signedPubKeyPath, keyAlias, keyNspace, keyBootLevel);
+Result<SigningKey*> KeystoreKey::getInstance() {
+ static KeystoreKey keystoreKey;
- if (!keystoreKey->initialize()) {
+ if (!keystoreKey.initialize()) {
return Error() << "Failed to initialize keystore key.";
} else {
- return keystoreKey;
+ return &keystoreKey;
}
}
@@ -298,13 +297,19 @@ Result<std::string> KeystoreKey::sign(const std::string& message) const {
auto status = mSecurityLevel->createOperation(mDescriptor, opParameters, false, &opResponse);
if (!status.isOk()) {
- return Error() << "Failed to create keystore signing operation: " << status;
+ return Error() << "Failed to create keystore signing operation: "
+ << status.serviceSpecificErrorCode();
}
auto operation = opResponse.iOperation;
- std::optional<std::vector<uint8_t>> input{std::in_place, message.begin(), message.end()};
+ std::optional<std::vector<uint8_t>> out;
+ status = operation->update({message.begin(), message.end()}, &out);
+ if (!status.isOk()) {
+ return Error() << "Failed to call keystore update operation.";
+ }
+
std::optional<std::vector<uint8_t>> signature;
- status = operation->finish(input, {}, &signature);
+ status = operation->finish({}, {}, &signature);
if (!status.isOk()) {
return Error() << "Failed to call keystore finish operation.";
}
diff --git a/ondevice-signing/KeystoreKey.h b/ondevice-signing/KeystoreKey.h
index 3c9a0ab0..1257cbbe 100644
--- a/ondevice-signing/KeystoreKey.h
+++ b/ondevice-signing/KeystoreKey.h
@@ -20,6 +20,7 @@
#include <android-base/macros.h>
#include <android-base/result.h>
+#include <android-base/unique_fd.h>
#include <utils/StrongPointer.h>
@@ -36,16 +37,13 @@ class KeystoreKey : public SigningKey {
public:
virtual ~KeystoreKey(){};
- static android::base::Result<SigningKey*> getInstance(const std::string& signedPubKeyPath,
- const android::String16& keyAlias,
- int64_t KeyNspace, int keyBootLevel);
+ static android::base::Result<SigningKey*> getInstance();
virtual android::base::Result<std::string> sign(const std::string& message) const;
virtual android::base::Result<std::vector<uint8_t>> getPublicKey() const;
private:
- KeystoreKey(std::string signedPubKeyPath, const android::String16& keyAlias, int64_t keyNspace,
- int keyBootLevel);
+ KeystoreKey();
bool initialize();
android::base::Result<std::vector<uint8_t>> verifyExistingKey();
android::base::Result<std::vector<uint8_t>> createKey();
@@ -56,7 +54,4 @@ class KeystoreKey : public SigningKey {
android::sp<IKeystoreService> mService;
android::sp<IKeystoreSecurityLevel> mSecurityLevel;
std::vector<uint8_t> mPublicKey;
-
- std::string mSignedPubKeyPath;
- int mKeyBootLevel;
};
diff --git a/ondevice-signing/include/SigningKey.h b/ondevice-signing/SigningKey.h
index 89294fc9..89294fc9 100644
--- a/ondevice-signing/include/SigningKey.h
+++ b/ondevice-signing/SigningKey.h
diff --git a/ondevice-signing/StatsReporter.cpp b/ondevice-signing/StatsReporter.cpp
deleted file mode 100644
index 65e645a3..00000000
--- a/ondevice-signing/StatsReporter.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#include "StatsReporter.h"
-#include <android-base/logging.h>
-#include <stdlib.h>
-#include <string>
-#include <sys/stat.h>
-
-// Keep these constant in sync with COMPOS_METRIC_NAME & METRICS_FILE in OdsignStatsLogger.java.
-constexpr const char* kOdsignMetricsFile = "/data/misc/odsign/metrics/odsign-metrics.txt";
-constexpr const char* kComposMetricName = "comp_os_artifacts_check_record";
-
-StatsReporter::~StatsReporter() {
- if (comp_os_artifacts_check_record_ == nullptr) {
- LOG(INFO) << "Metrics report is empty";
-
- // Remove the metrics file if any old version of the file already exists
- if (std::filesystem::remove(kOdsignMetricsFile) != 0 &&
- !((errno = ENOENT) || errno == ENOTDIR)) {
- PLOG(ERROR) << "Could not remove already present file";
- }
- return;
- }
-
- std::ofstream odsign_metrics_file_;
- odsign_metrics_file_.open(kOdsignMetricsFile, std::ios::trunc);
- if (!odsign_metrics_file_) {
- PLOG(ERROR) << "Could not open file: " << kOdsignMetricsFile;
- return;
- }
-
- odsign_metrics_file_ << kComposMetricName << ' ';
- odsign_metrics_file_ << comp_os_artifacts_check_record_->current_artifacts_ok << ' ';
- odsign_metrics_file_ << comp_os_artifacts_check_record_->comp_os_pending_artifacts_exists
- << ' ';
- odsign_metrics_file_ << comp_os_artifacts_check_record_->use_comp_os_generated_artifacts
- << '\n';
- if (chmod(kOdsignMetricsFile, 0644) != 0) {
- PLOG(ERROR) << "Could not set correct file permissions for " << kOdsignMetricsFile;
- return;
- }
- odsign_metrics_file_.close();
- if (!odsign_metrics_file_) {
- PLOG(ERROR) << "Failed to close the file";
- }
-}
-
-StatsReporter::CompOsArtifactsCheckRecord* StatsReporter::GetComposArtifactsCheckRecord() {
- if (comp_os_artifacts_check_record_ == nullptr) {
- comp_os_artifacts_check_record_ = std::make_unique<CompOsArtifactsCheckRecord>();
- }
- return comp_os_artifacts_check_record_.get();
-}
diff --git a/ondevice-signing/StatsReporter.h b/ondevice-signing/StatsReporter.h
deleted file mode 100644
index 2682b963..00000000
--- a/ondevice-signing/StatsReporter.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#pragma once
-
-#include <fstream>
-
-// Class to store CompOsArtifactsCheck related metrics.
-// These are flushed to a file kOdsignMetricsFile and consumed by
-// System Server (in class OdsignStatsLogger) & sent to statsd.
-class StatsReporter {
- public:
- // Keep sync with EarlyBootCompOsArtifactsCheckReported
- // definition in proto_logging/stats/atoms.proto.
- struct CompOsArtifactsCheckRecord {
- bool current_artifacts_ok = false;
- bool comp_os_pending_artifacts_exists = false;
- bool use_comp_os_generated_artifacts = false;
- };
-
- // The report is flushed (from buffer) into a file by the destructor.
- ~StatsReporter();
-
- // Get pointer to comp_os_artifacts_check_record, caller can then modify it.
- // Note: pointer remains valid for the lifetime of this StatsReporter.
- CompOsArtifactsCheckRecord* GetComposArtifactsCheckRecord();
-
- private:
- // Temporary buffer which stores the metrics.
- std::unique_ptr<CompOsArtifactsCheckRecord> comp_os_artifacts_check_record_;
-};
diff --git a/ondevice-signing/TEST_MAPPING b/ondevice-signing/TEST_MAPPING
index 4b2c8c6b..03b9b95c 100644
--- a/ondevice-signing/TEST_MAPPING
+++ b/ondevice-signing/TEST_MAPPING
@@ -1,7 +1,7 @@
{
"presubmit": [
{
- "name": "libsigningutils_test"
+ "name": "odsign_e2e_tests"
}
]
}
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index cd9a1ea6..0bb3979d 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <charconv>
#include <filesystem>
#include <map>
#include <span>
@@ -26,10 +25,8 @@
#include <sys/types.h>
#include <sys/wait.h>
-#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
-#include <asm/byteorder.h>
#include <libfsverity.h>
#include <linux/fsverity.h>
@@ -44,13 +41,23 @@ using android::base::Result;
using android::base::unique_fd;
static const char* kFsVerityInitPath = "/system/bin/fsverity_init";
-static const char* kFsVerityProcPath = "/proc/sys/fs/verity";
-bool SupportsFsVerity() {
- return access(kFsVerityProcPath, F_OK) == 0;
-}
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define cpu_to_le16(v) ((__force __le16)(uint16_t)(v))
+#define le16_to_cpu(v) ((__force uint16_t)(__le16)(v))
+#else
+#define cpu_to_le16(v) ((__force __le16)__builtin_bswap16(v))
+#define le16_to_cpu(v) (__builtin_bswap16((__force uint16_t)(v)))
+#endif
+
+struct fsverity_signed_digest {
+ char magic[8]; /* must be "FSVerity" */
+ __le16 digest_algorithm;
+ __le16 digest_size;
+ __u8 digest[];
+};
-static std::string toHex(std::span<const uint8_t> data) {
+static std::string toHex(std::span<uint8_t> data) {
std::stringstream ss;
for (auto it = data.begin(); it != data.end(); ++it) {
ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
@@ -58,34 +65,22 @@ static std::string toHex(std::span<const uint8_t> data) {
return ss.str();
}
-static std::vector<uint8_t> fromHex(std::string_view hex) {
- if (hex.size() % 2 != 0) {
- return {};
- }
- std::vector<uint8_t> result;
- result.reserve(hex.size() / 2);
- for (size_t i = 0; i < hex.size(); i += 2) {
- uint8_t byte;
- auto conversion_result = std::from_chars(&hex[i], &hex[i + 2], byte, 16);
- if (conversion_result.ptr != &hex[i + 2] || conversion_result.ec != std::errc()) {
- return {};
- }
- result.push_back(byte);
- }
- return result;
-}
-
static int read_callback(void* file, void* buf, size_t count) {
int* fd = (int*)file;
if (TEMP_FAILURE_RETRY(read(*fd, buf, count)) < 0) return errno ? -errno : -EIO;
return 0;
}
-static Result<std::vector<uint8_t>> createDigest(int fd) {
+Result<std::vector<uint8_t>> createDigest(const std::string& path) {
struct stat filestat;
- int ret = fstat(fd, &filestat);
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ return ErrnoError() << "Failed to open " << path;
+ }
+
+ int ret = stat(path.c_str(), &filestat);
if (ret < 0) {
- return ErrnoError() << "Failed to fstat";
+ return ErrnoError() << "Failed to stat " << path;
}
struct libfsverity_merkle_tree_params params = {
.version = 1,
@@ -97,7 +92,7 @@ static Result<std::vector<uint8_t>> createDigest(int fd) {
struct libfsverity_digest* digest;
ret = libfsverity_compute_digest(&fd, &read_callback, &params, &digest);
if (ret < 0) {
- return ErrnoError() << "Failed to compute fs-verity digest";
+ return ErrnoError() << "Failed to compute fs-verity digest for " << path;
}
int expected_digest_size = libfsverity_get_digest_size(FS_VERITY_HASH_ALG_SHA256);
if (digest->digest_size != expected_digest_size) {
@@ -109,14 +104,6 @@ static Result<std::vector<uint8_t>> createDigest(int fd) {
return digestVector;
}
-Result<std::vector<uint8_t>> createDigest(const std::string& path) {
- unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
- if (!fd.ok()) {
- return ErrnoError() << "Unable to open";
- }
- return createDigest(fd.get());
-}
-
namespace {
template <typename T> struct DeleteAsPODArray {
void operator()(T* x) {
@@ -126,19 +113,6 @@ template <typename T> struct DeleteAsPODArray {
}
}
};
-
-static Result<void> measureFsVerity(int fd, const fsverity_digest* digest) {
- if (ioctl(fd, FS_IOC_MEASURE_VERITY, digest) != 0) {
- if (errno == ENODATA) {
- return Error() << "File is not in fs-verity";
- } else {
- return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY";
- }
- }
-
- return {};
-}
-
} // namespace
template <typename T> using trailing_unique_ptr = std::unique_ptr<T, DeleteAsPODArray<T>>;
@@ -152,11 +126,11 @@ static trailing_unique_ptr<T> makeUniqueWithTrailingData(size_t trailing_data_si
static Result<std::vector<uint8_t>> signDigest(const SigningKey& key,
const std::vector<uint8_t>& digest) {
- auto d = makeUniqueWithTrailingData<fsverity_formatted_digest>(digest.size());
+ auto d = makeUniqueWithTrailingData<fsverity_signed_digest>(digest.size());
memcpy(d->magic, "FSVerity", 8);
- d->digest_algorithm = __cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
- d->digest_size = __cpu_to_le16(digest.size());
+ d->digest_algorithm = cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
+ d->digest_size = cpu_to_le16(digest.size());
memcpy(d->digest, digest.data(), digest.size());
auto signed_digest = key.sign(std::string((char*)d.get(), sizeof(*d) + digest.size()));
@@ -167,27 +141,10 @@ static Result<std::vector<uint8_t>> signDigest(const SigningKey& key,
return std::vector<uint8_t>(signed_digest->begin(), signed_digest->end());
}
-static Result<void> enableFsVerity(int fd, std::span<uint8_t> pkcs7) {
- struct fsverity_enable_arg arg = {.version = 1};
-
- arg.sig_ptr = reinterpret_cast<uint64_t>(pkcs7.data());
- arg.sig_size = pkcs7.size();
- arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
- arg.block_size = 4096;
-
- int ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
-
- if (ret != 0) {
- return ErrnoError() << "Failed to call FS_IOC_ENABLE_VERITY";
- }
-
- return {};
-}
-
-Result<std::string> enableFsVerity(int fd, const SigningKey& key) {
- auto digest = createDigest(fd);
+Result<std::string> enableFsVerity(const std::string& path, const SigningKey& key) {
+ auto digest = createDigest(path);
if (!digest.ok()) {
- return Error() << digest.error();
+ return digest.error();
}
auto signed_digest = signDigest(key, digest.value());
@@ -195,102 +152,75 @@ Result<std::string> enableFsVerity(int fd, const SigningKey& key) {
return signed_digest.error();
}
- auto pkcs7_data = createPkcs7(signed_digest.value(), kRootSubject);
- if (!pkcs7_data.ok()) {
- return pkcs7_data.error();
- }
-
- auto enabled = enableFsVerity(fd, pkcs7_data.value());
- if (!enabled.ok()) {
- return Error() << enabled.error();
- }
+ auto pkcs7_data = createPkcs7(signed_digest.value());
- // Return the root hash as a hex string
- return toHex(digest.value());
-}
-
-static Result<std::string> isFileInVerity(int fd) {
- auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
- d->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
-
- const auto& status = measureFsVerity(fd, d.get());
- if (!status.ok()) {
- return status.error();
- }
+ struct fsverity_enable_arg arg = {.version = 1};
- return toHex({&d->digest[0], &d->digest[d->digest_size]});
-}
+ arg.sig_ptr = (uint64_t)pkcs7_data->data();
+ arg.sig_size = pkcs7_data->size();
+ arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+ arg.block_size = 4096;
-static Result<std::string> isFileInVerity(const std::string& path) {
unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
- if (!fd.ok()) {
- return ErrnoError() << "Failed to open " << path;
- }
+ int ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
- auto digest = isFileInVerity(fd.get());
- if (!digest.ok()) {
- return Error() << digest.error() << ": " << path;
+ if (ret != 0) {
+ return ErrnoError() << "Failed to call FS_IOC_ENABLE_VERITY on " << path;
}
- return digest;
+ // Return the root hash as a hex string
+ return toHex(digest.value());
}
Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std::string& path,
const SigningKey& key) {
std::map<std::string, std::string> digests;
-
std::error_code ec;
+
auto it = std::filesystem::recursive_directory_iterator(path, ec);
- for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
+ auto end = std::filesystem::recursive_directory_iterator();
+
+ while (!ec && it != end) {
if (it->is_regular_file()) {
- unique_fd fd(TEMP_FAILURE_RETRY(open(it->path().c_str(), O_RDONLY | O_CLOEXEC)));
- if (!fd.ok()) {
- return ErrnoError() << "Failed to open " << path;
- }
- auto digest = isFileInVerity(fd);
- if (!digest.ok()) {
- LOG(INFO) << "Adding " << it->path() << " to fs-verity...";
- auto result = enableFsVerity(fd, key);
- if (!result.ok()) {
- return result.error();
- }
- digests[it->path()] = *result;
- } else {
- LOG(INFO) << it->path() << " was already in fs-verity.";
- digests[it->path()] = *digest;
+ LOG(INFO) << "Adding " << it->path() << " to fs-verity...";
+ auto result = enableFsVerity(it->path(), key);
+ if (!result.ok()) {
+ return result.error();
}
+ digests[it->path()] = *result;
}
+ ++it;
}
if (ec) {
- return Error() << "Failed to iterate " << path << ": " << ec.message();
+ return Error() << "Failed to iterate " << path << ": " << ec;
}
return digests;
}
-Result<void> enableFsVerity(const std::string& path, const std::string& signature_path) {
+Result<std::string> isFileInVerity(const std::string& path) {
+ unsigned int flags;
+
unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
- if (!fd.ok()) {
- return Error() << "Can't open " << path;
+ if (fd < 0) {
+ return ErrnoError() << "Failed to open " << path;
}
- std::string signature;
- android::base::ReadFileToString(signature_path, &signature);
- std::vector<uint8_t> span = std::vector<uint8_t>(signature.begin(), signature.end());
-
- const auto& enable = enableFsVerity(fd.get(), span);
- if (!enable.ok()) {
- return enable.error();
+ int ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
+ if (ret < 0) {
+ return ErrnoError() << "Failed to FS_IOC_GETFLAGS for " << path;
}
-
- auto digest = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
- digest->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
- const auto& measure = measureFsVerity(fd.get(), digest.get());
- if (!measure.ok()) {
- return measure.error();
+ if (!(flags & FS_VERITY_FL)) {
+ return Error() << "File is not in fs-verity: " << path;
}
- return {};
+ auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
+ d->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
+ ret = ioctl(fd, FS_IOC_MEASURE_VERITY, d.get());
+ if (ret < 0) {
+ return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY for " << path;
+ }
+ return toHex({&d->digest[0], &d->digest[d->digest_size]});
}
Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::string& path) {
@@ -324,90 +254,10 @@ Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::str
return digests;
}
-Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path,
- const std::map<std::string, std::string>& digests,
- const SigningKey& signing_key) {
- std::error_code ec;
- size_t verified_count = 0;
- auto it = std::filesystem::recursive_directory_iterator(directory_path, ec);
- for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
- auto& path = it->path();
- if (it->is_regular_file()) {
- auto entry = digests.find(path);
- if (entry == digests.end()) {
- return Error() << "Unexpected file found: " << path;
- }
- auto& compos_digest = entry->second;
-
- unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
- if (!fd.ok()) {
- return ErrnoError() << "Can't open " << path;
- }
-
- auto verity_digest = isFileInVerity(fd);
- if (verity_digest.ok()) {
- // The file is already in fs-verity. We need to make sure it was signed
- // by CompOS, so we just check that it has the digest we expect.
- if (verity_digest.value() == compos_digest) {
- ++verified_count;
- } else {
- return Error() << "fs-verity digest does not match CompOS digest: " << path;
- }
- } else {
- // Not in fs-verity yet. We know the digest CompOS provided; If
- // it's not the correct digest for the file then enabling
- // fs-verity will fail, so we don't need to check it explicitly
- // ourselves. Otherwise we should be good.
- LOG(INFO) << "Adding " << path << " to fs-verity...";
-
- auto digest_bytes = fromHex(compos_digest);
- if (digest_bytes.empty()) {
- return Error() << "Invalid digest " << compos_digest;
- }
- auto signed_digest = signDigest(signing_key, digest_bytes);
- if (!signed_digest.ok()) {
- return signed_digest.error();
- }
-
- auto pkcs7_data = createPkcs7(signed_digest.value(), kRootSubject);
- if (!pkcs7_data.ok()) {
- return pkcs7_data.error();
- }
-
- auto enabled = enableFsVerity(fd, pkcs7_data.value());
- if (!enabled.ok()) {
- return Error() << enabled.error();
- }
- ++verified_count;
- }
- } else if (it->is_directory()) {
- // These are fine to ignore
- } else if (it->is_symlink()) {
- return Error() << "Rejecting artifacts, symlink at " << path;
- } else {
- return Error() << "Rejecting artifacts, unexpected file type for " << path;
- }
- }
- if (ec) {
- return Error() << "Failed to iterate " << directory_path << ": " << ec.message();
- }
-
- // Make sure all the files we expected have been seen
- if (verified_count != digests.size()) {
- return Error() << "Verified " << verified_count << " files, but expected "
- << digests.size();
- }
-
- return {};
-}
-
-Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyName) {
- const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", keyName};
+Result<void> addCertToFsVerityKeyring(const std::string& path) {
+ const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", "fsv_ods"};
int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
- if (fd == -1) {
- return ErrnoError() << "Failed to open " << path;
- }
pid_t pid = fork();
if (pid == 0) {
dup2(fd, STDIN_FILENO);
@@ -416,7 +266,7 @@ Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyNa
char* argv_child[argc + 1];
memcpy(argv_child, argv, argc * sizeof(char*));
argv_child[argc] = nullptr;
- execvp(argv_child[0], argv_child);
+ execvp(argv_child[0], const_cast<char**>(argv_child));
PLOG(ERROR) << "exec in ForkExecvp";
_exit(EXIT_FAILURE);
} else {
@@ -432,8 +282,10 @@ Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyNa
if (!WIFEXITED(status)) {
return Error() << kFsVerityInitPath << ": abnormal process exit";
}
- if (WEXITSTATUS(status) != 0) {
- return Error() << kFsVerityInitPath << " exited with " << WEXITSTATUS(status);
+ if (WEXITSTATUS(status)) {
+ if (status != 0) {
+ return Error() << kFsVerityInitPath << " exited with " << status;
+ }
}
return {};
diff --git a/ondevice-signing/include/VerityUtils.h b/ondevice-signing/VerityUtils.h
index e6e49c7d..84af3196 100644
--- a/ondevice-signing/include/VerityUtils.h
+++ b/ondevice-signing/VerityUtils.h
@@ -18,29 +18,11 @@
#include <android-base/result.h>
-#include <map>
-#include <string>
-#include <vector>
-
#include "SigningKey.h"
-android::base::Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyName);
+android::base::Result<void> addCertToFsVerityKeyring(const std::string& path);
android::base::Result<std::vector<uint8_t>> createDigest(const std::string& path);
-android::base::Result<std::string> enableFsVerity(int fd, const SigningKey& key);
-bool SupportsFsVerity();
android::base::Result<std::map<std::string, std::string>>
verifyAllFilesInVerity(const std::string& path);
-
-// Note that this function will skip files that are already in fs-verity, and
-// for those files it will return the existing digest.
android::base::Result<std::map<std::string, std::string>>
addFilesToVerityRecursive(const std::string& path, const SigningKey& key);
-
-// Enable verity on the provided file, using the given PKCS7 signature.
-android::base::Result<void> enableFsVerity(const std::string& path,
- const std::string& signature_path);
-
-android::base::Result<void>
-verifyAllFilesUsingCompOs(const std::string& directory_path,
- const std::map<std::string, std::string>& digests,
- const SigningKey& signing_key);
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index c45e3085..425776bd 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -33,7 +33,6 @@
#include "CertUtils.h"
#include "KeystoreKey.h"
-#include "StatsReporter.h"
#include "VerityUtils.h"
#include "odsign_info.pb.h"
@@ -45,57 +44,29 @@ using android::base::SetProperty;
using OdsignInfo = ::odsign::proto::OdsignInfo;
-// Keystore boot level that the odsign key uses
-const int kKeyBootLevel = 30;
-const std::string kPublicKeySignature = "/data/misc/odsign/publickey.signature";
-const android::String16 kKeyAlias{"ondevice-signing"};
-constexpr int kKeyNspace = 101; // odsign_key
-
+const std::string kSigningKeyBlob = "/data/misc/odsign/key.blob";
const std::string kSigningKeyCert = "/data/misc/odsign/key.cert";
const std::string kOdsignInfo = "/data/misc/odsign/odsign.info";
const std::string kOdsignInfoSignature = "/data/misc/odsign/odsign.info.signature";
const std::string kArtArtifactsDir = "/data/misc/apexdata/com.android.art/dalvik-cache";
-constexpr const char* kOdrefreshPath = "/apex/com.android.art/bin/odrefresh";
-constexpr const char* kCompOsVerifyPath = "/apex/com.android.compos/bin/compos_verify";
-
-constexpr bool kForceCompilation = false;
-constexpr bool kUseCompOs = true;
-
-const std::string kCompOsPendingArtifactsDir = "/data/misc/apexdata/com.android.art/compos-pending";
-const std::string kCompOsInfo = kArtArtifactsDir + "/compos.info";
-const std::string kCompOsInfoSignature = kCompOsInfo + ".signature";
-
-constexpr const char* kCompOsPendingInfoPath =
- "/data/misc/apexdata/com.android.art/compos-pending/compos.info";
-constexpr const char* kCompOsPendingInfoSignaturePath =
- "/data/misc/apexdata/com.android.art/compos-pending/compos.info.signature";
+static const char* kOdrefreshPath = "/apex/com.android.art/bin/odrefresh";
-constexpr const char* kOdsignVerificationDoneProp = "odsign.verification.done";
-constexpr const char* kOdsignKeyDoneProp = "odsign.key.done";
+static const char* kFsVerityProcPath = "/proc/sys/fs/verity";
-constexpr const char* kOdsignVerificationStatusProp = "odsign.verification.success";
-constexpr const char* kOdsignVerificationStatusValid = "1";
-constexpr const char* kOdsignVerificationStatusError = "0";
+static const bool kForceCompilation = false;
-constexpr const char* kStopServiceProp = "ctl.stop";
+static const char* kOdsignVerificationDoneProp = "odsign.verification.done";
+static const char* kOdsignKeyDoneProp = "odsign.key.done";
-enum class CompOsInstance { kCurrent, kPending };
+static const char* kOdsignVerificationStatusProp = "odsign.verification.success";
+static const char* kOdsignVerificationStatusValid = "1";
+static const char* kOdsignVerificationStatusError = "0";
-namespace {
-
-bool rename(const std::string& from, const std::string& to) {
- std::error_code ec;
- std::filesystem::rename(from, to, ec);
- if (ec) {
- LOG(ERROR) << "Can't rename " << from << " to " << to << ": " << ec.message();
- return false;
- }
- return true;
-}
+static const char* kStopServiceProp = "ctl.stop";
-int removeDirectory(const std::string& directory) {
+static int removeDirectory(const std::string& directory) {
std::error_code ec;
auto num_removed = std::filesystem::remove_all(directory, ec);
if (ec) {
@@ -109,46 +80,13 @@ int removeDirectory(const std::string& directory) {
}
}
-bool directoryHasContent(const std::string& directory) {
- std::error_code ec;
- return std::filesystem::is_directory(directory, ec) &&
- !std::filesystem::is_empty(directory, ec);
-}
-
-art::odrefresh::ExitCode compileArtifacts(bool force) {
- const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
- const int exit_code =
- logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
- return static_cast<art::odrefresh::ExitCode>(exit_code);
-}
-
-art::odrefresh::ExitCode checkArtifacts() {
- const char* const argv[] = {kOdrefreshPath, "--check"};
- const int exit_code =
- logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
- return static_cast<art::odrefresh::ExitCode>(exit_code);
-}
-
-std::string toHex(const std::vector<uint8_t>& digest) {
- std::stringstream ss;
- for (auto it = digest.begin(); it != digest.end(); ++it) {
- ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
- }
- return ss.str();
-}
-
-bool compOsPresent() {
- // We must have the CompOS APEX
- return access(kCompOsVerifyPath, X_OK) == 0;
-}
-
-Result<void> verifyExistingRootCert(const SigningKey& key) {
+Result<void> verifyExistingCert(const SigningKey& key) {
if (access(kSigningKeyCert.c_str(), F_OK) < 0) {
return ErrnoError() << "Key certificate not found: " << kSigningKeyCert;
}
auto trustedPublicKey = key.getPublicKey();
if (!trustedPublicKey.ok()) {
- return Error() << "Failed to retrieve signing public key: " << trustedPublicKey.error();
+ return Error() << "Failed to retrieve signing public key.";
}
auto publicKeyFromExistingCert = extractPublicKeyFromX509(kSigningKeyCert);
@@ -160,12 +98,11 @@ Result<void> verifyExistingRootCert(const SigningKey& key) {
<< " does not match signing public key.";
}
- // At this point, we know the cert is for our key; it's unimportant whether it's
- // actually self-signed.
+ // At this point, we know the cert matches
return {};
}
-Result<void> createX509RootCert(const SigningKey& key, const std::string& outPath) {
+Result<void> createX509Cert(const SigningKey& key, const std::string& outPath) {
auto publicKey = key.getPublicKey();
if (!publicKey.ok()) {
@@ -173,7 +110,30 @@ Result<void> createX509RootCert(const SigningKey& key, const std::string& outPat
}
auto keySignFunction = [&](const std::string& to_be_signed) { return key.sign(to_be_signed); };
- return createSelfSignedCertificate(*publicKey, keySignFunction, outPath);
+ createSelfSignedCertificate(*publicKey, keySignFunction, outPath);
+ return {};
+}
+
+art::odrefresh::ExitCode checkArtifacts() {
+ const char* const argv[] = {kOdrefreshPath, "--check"};
+ const int exit_code =
+ logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
+ return static_cast<art::odrefresh::ExitCode>(exit_code);
+}
+
+art::odrefresh::ExitCode compileArtifacts(bool force) {
+ const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
+ const int exit_code =
+ logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
+ return static_cast<art::odrefresh::ExitCode>(exit_code);
+}
+
+static std::string toHex(const std::vector<uint8_t>& digest) {
+ std::stringstream ss;
+ for (auto it = digest.begin(); it != digest.end(); ++it) {
+ ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
+ }
+ return ss.str();
}
Result<std::map<std::string, std::string>> computeDigests(const std::string& path) {
@@ -187,8 +147,7 @@ Result<std::map<std::string, std::string>> computeDigests(const std::string& pat
if (it->is_regular_file()) {
auto digest = createDigest(it->path());
if (!digest.ok()) {
- return Error() << "Failed to compute digest for " << it->path() << ": "
- << digest.error();
+ return Error() << "Failed to compute digest for " << it->path();
}
digests[it->path()] = toHex(*digest);
}
@@ -322,8 +281,22 @@ Result<void> persistDigests(const std::map<std::string, std::string>& digests,
return {};
}
-Result<void> verifyArtifactsIntegrity(const std::map<std::string, std::string>& trusted_digests,
- bool supportsFsVerity) {
+static int removeArtifacts() {
+ std::error_code ec;
+ auto num_removed = std::filesystem::remove_all(kArtArtifactsDir, ec);
+ if (ec) {
+ LOG(ERROR) << "Can't remove " << kArtArtifactsDir << ": " << ec.message();
+ return 0;
+ } else {
+ if (num_removed > 0) {
+ LOG(INFO) << "Removed " << num_removed << " entries from " << kArtArtifactsDir;
+ }
+ return num_removed;
+ }
+}
+
+static Result<void> verifyArtifacts(const std::map<std::string, std::string>& trusted_digests,
+ bool supportsFsVerity) {
Result<void> integrityStatus;
if (supportsFsVerity) {
@@ -332,152 +305,16 @@ Result<void> verifyArtifactsIntegrity(const std::map<std::string, std::string>&
integrityStatus = verifyIntegrityNoFsVerity(trusted_digests);
}
if (!integrityStatus.ok()) {
- return integrityStatus.error();
+ return Error() << integrityStatus.error().message();
}
return {};
}
-Result<OdsignInfo> getComposInfo() {
- const char* const argv[] = {kCompOsVerifyPath, "--instance", "current"};
- int result =
- logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
- if (result != 0) {
- return Error() << kCompOsVerifyPath << " returned " << result;
- }
-
- std::string compos_info_str;
- if (!android::base::ReadFileToString(kCompOsInfo, &compos_info_str)) {
- return ErrnoError() << "Failed to read " << kCompOsInfo;
- }
-
- // Delete the files - we don't need them any more, and they'd confuse
- // artifact verification
- if (unlink(kCompOsInfo.c_str()) != 0 || unlink(kCompOsInfoSignature.c_str()) != 0) {
- return ErrnoError() << "Unable to delete CompOS info/signature file";
- }
-
- OdsignInfo compos_info;
- if (!compos_info.ParseFromString(compos_info_str)) {
- return Error() << "Failed to parse " << kCompOsInfo;
- }
-
- LOG(INFO) << "Loaded " << kCompOsInfo;
- return compos_info;
-}
-
-art::odrefresh::ExitCode CheckCompOsPendingArtifacts(const SigningKey& signing_key,
- bool* digests_verified,
- StatsReporter* stats_reporter) {
- StatsReporter::CompOsArtifactsCheckRecord* compos_check_record =
- stats_reporter->GetComposArtifactsCheckRecord();
-
- if (!directoryHasContent(kCompOsPendingArtifactsDir)) {
- // No pending CompOS artifacts, all that matters is the current ones.
- art::odrefresh::ExitCode odrefresh_status = checkArtifacts();
- if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
- compos_check_record->current_artifacts_ok = true;
- }
- return odrefresh_status;
- }
-
- compos_check_record->comp_os_pending_artifacts_exists = true;
-
- // CompOS has generated some artifacts that may, or may not, match the
- // current state. But if there are already valid artifacts present the
- // CompOS ones are redundant.
- art::odrefresh::ExitCode odrefresh_status = checkArtifacts();
- if (odrefresh_status != art::odrefresh::ExitCode::kCompilationRequired) {
- if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
- compos_check_record->current_artifacts_ok = true;
- LOG(INFO) << "Current artifacts are OK, deleting pending artifacts";
- removeDirectory(kCompOsPendingArtifactsDir);
- }
- return odrefresh_status;
- }
-
- // No useful current artifacts, lets see if the CompOS ones are ok
- if (access(kCompOsPendingInfoPath, R_OK) != 0 ||
- access(kCompOsPendingInfoSignaturePath, R_OK) != 0) {
- LOG(INFO) << "Missing CompOS info/signature, deleting pending artifacts";
- removeDirectory(kCompOsPendingArtifactsDir);
- return art::odrefresh::ExitCode::kCompilationRequired;
- }
-
- LOG(INFO) << "Current artifacts are out of date, switching to pending artifacts";
- removeDirectory(kArtArtifactsDir);
- if (!rename(kCompOsPendingArtifactsDir, kArtArtifactsDir)) {
- removeDirectory(kCompOsPendingArtifactsDir);
- return art::odrefresh::ExitCode::kCompilationRequired;
- }
-
- // Make sure the artifacts we have are genuinely produced by the current
- // instance of CompOS.
- auto compos_info = getComposInfo();
- if (!compos_info.ok()) {
- LOG(WARNING) << compos_info.error();
- } else {
- std::map<std::string, std::string> compos_digests(compos_info->file_hashes().begin(),
- compos_info->file_hashes().end());
-
- auto status = verifyAllFilesUsingCompOs(kArtArtifactsDir, compos_digests, signing_key);
- if (!status.ok()) {
- LOG(WARNING) << "Faild to verify CompOS artifacts: " << status.error();
- } else {
- LOG(INFO) << "CompOS artifacts successfully verified.";
- odrefresh_status = checkArtifacts();
- switch (odrefresh_status) {
- case art::odrefresh::ExitCode::kCompilationRequired:
- // We have verified all the files, and we need to make sure
- // we don't check them against odsign.info which will be out
- // of date.
- *digests_verified = true;
- return odrefresh_status;
- case art::odrefresh::ExitCode::kOkay: {
- // We have digests of all the files, so we can just sign them & save them now.
- // We need to make sure we don't check them against odsign.info which will
- // be out of date.
- auto persisted = persistDigests(compos_digests, signing_key);
- if (!persisted.ok()) {
- LOG(ERROR) << persisted.error();
- // Don't try to compile again - if we can't write the digests, things
- // are pretty bad.
- return art::odrefresh::ExitCode::kCleanupFailed;
- }
- compos_check_record->use_comp_os_generated_artifacts = true;
- LOG(INFO) << "Persisted CompOS digests.";
- *digests_verified = true;
- return odrefresh_status;
- }
- default:
- return odrefresh_status;
- }
- }
- }
-
- // We can't use the existing artifacts, so we will need to generate new
- // ones.
- if (removeDirectory(kArtArtifactsDir) == 0) {
- // We have unsigned artifacts that we can't delete, so it's not safe to continue.
- LOG(ERROR) << "Unable to delete invalid CompOS artifacts";
- return art::odrefresh::ExitCode::kCleanupFailed;
- }
-
- return art::odrefresh::ExitCode::kCompilationRequired;
-}
-} // namespace
-
-int main(int /* argc */, char** argv) {
- // stats_reporter is a pointer so that we can explicitly delete it
- // instead of waiting for the program to die & its destrcutor be called
- auto stats_reporter = std::make_unique<StatsReporter>();
- android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
-
+int main(int /* argc */, char** /* argv */) {
auto errorScopeGuard = []() {
- // In case we hit any error, remove the artifacts and tell Zygote not to use
- // anything
- removeDirectory(kArtArtifactsDir);
- removeDirectory(kCompOsPendingArtifactsDir);
+ // In case we hit any error, remove the artifacts and tell Zygote not to use anything
+ removeArtifacts();
// Tell init we don't need to use our key anymore
SetProperty(kOdsignKeyDoneProp, "1");
// Tell init we're done with verification, and that it was an error
@@ -492,54 +329,43 @@ int main(int /* argc */, char** argv) {
LOG(INFO) << "Device doesn't support updatable APEX, exiting.";
return 0;
}
- auto keystoreResult =
- KeystoreKey::getInstance(kPublicKeySignature, kKeyAlias, kKeyNspace, kKeyBootLevel);
+
+ auto keystoreResult = KeystoreKey::getInstance();
if (!keystoreResult.ok()) {
- LOG(ERROR) << "Could not create keystore key: " << keystoreResult.error();
+ LOG(ERROR) << "Could not create keystore key: " << keystoreResult.error().message();
return -1;
}
SigningKey* key = keystoreResult.value();
- bool supportsFsVerity = SupportsFsVerity();
+ bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
if (!supportsFsVerity) {
LOG(INFO) << "Device doesn't support fsverity. Falling back to full verification.";
}
- bool useCompOs = kUseCompOs && supportsFsVerity && compOsPresent();
-
if (supportsFsVerity) {
- auto existing_cert = verifyExistingRootCert(*key);
+ auto existing_cert = verifyExistingCert(*key);
if (!existing_cert.ok()) {
- LOG(WARNING) << existing_cert.error();
+ LOG(WARNING) << existing_cert.error().message();
// Try to create a new cert
- auto new_cert = createX509RootCert(*key, kSigningKeyCert);
+ auto new_cert = createX509Cert(*key, kSigningKeyCert);
if (!new_cert.ok()) {
- LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error();
+ LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error().message();
// TODO apparently the key become invalid - delete the blob / cert
return -1;
}
} else {
LOG(INFO) << "Found and verified existing public key certificate: " << kSigningKeyCert;
}
- auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert, "fsv_ods");
+ auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
if (!cert_add_result.ok()) {
LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
- << cert_add_result.error();
+ << cert_add_result.error().message();
return -1;
}
}
- bool digests_verified = false;
- art::odrefresh::ExitCode odrefresh_status =
- useCompOs ? CheckCompOsPendingArtifacts(*key, &digests_verified, stats_reporter.get())
- : checkArtifacts();
-
- // Explicitly reset the pointer - We rely on stats_reporter's
- // destructor for actually writing the buffered metrics. This will otherwise not be called
- // if the program doesn't exit normally (for ex, killed by init, which actually happens
- // because odsign (after it finishes) sets kStopServiceProp instructing init to kill it).
- stats_reporter.reset();
+ art::odrefresh::ExitCode odrefresh_status = checkArtifacts();
// The artifacts dir doesn't necessarily need to exist; if the existing
// artifacts on the system partition are valid, those can be used.
@@ -547,9 +373,8 @@ int main(int /* argc */, char** argv) {
// If we receive any error other than ENOENT, be suspicious
bool artifactsPresent = (err == 0) || (err < 0 && errno != ENOENT);
- if (artifactsPresent && !digests_verified &&
- (odrefresh_status == art::odrefresh::ExitCode::kOkay ||
- odrefresh_status == art::odrefresh::ExitCode::kCompilationRequired)) {
+ if (artifactsPresent && (odrefresh_status == art::odrefresh::ExitCode::kOkay ||
+ odrefresh_status == art::odrefresh::ExitCode::kCompilationRequired)) {
// If we haven't verified the digests yet, we need to validate them. We
// need to do this both in case the existing artifacts are okay, but
// also if odrefresh said that a recompile is required. In the latter
@@ -570,7 +395,7 @@ int main(int /* argc */, char** argv) {
SetProperty(kOdsignKeyDoneProp, "1");
}
- auto verificationResult = verifyArtifactsIntegrity(trusted_digests, supportsFsVerity);
+ auto verificationResult = verifyArtifacts(trusted_digests, supportsFsVerity);
if (!verificationResult.ok()) {
int num_removed = removeDirectory(kArtArtifactsDir);
if (num_removed == 0) {
@@ -605,12 +430,12 @@ int main(int /* argc */, char** argv) {
digests = computeDigests(kArtArtifactsDir);
}
if (!digests.ok()) {
- LOG(ERROR) << digests.error();
+ LOG(ERROR) << digests.error().message();
return -1;
}
auto persistStatus = persistDigests(*digests, *key);
if (!persistStatus.ok()) {
- LOG(ERROR) << persistStatus.error();
+ LOG(ERROR) << persistStatus.error().message();
return -1;
}
} else if (odrefresh_status == art::odrefresh::ExitCode::kCleanupFailed) {
diff --git a/ondevice-signing/proto/Android.bp b/ondevice-signing/proto/Android.bp
index 356e6619..fd48f314 100644
--- a/ondevice-signing/proto/Android.bp
+++ b/ondevice-signing/proto/Android.bp
@@ -23,22 +23,7 @@ cc_library_static {
host_supported: true,
proto: {
export_proto_headers: true,
- type: "lite",
+ type: "full",
},
srcs: ["odsign_info.proto"],
- apex_available: [
- "//apex_available:platform",
- "com.android.compos",
- ],
-}
-
-rust_protobuf {
- name: "libodsign_proto_rust",
- crate_name: "odsign_proto",
- protos: ["odsign_info.proto"],
- source_stem: "odsign_proto",
- host_supported: true,
- apex_available: [
- "com.android.compos",
- ],
}
diff --git a/ondevice-signing/tests/Android.bp b/ondevice-signing/tests/Android.bp
deleted file mode 100644
index 40272200..00000000
--- a/ondevice-signing/tests/Android.bp
+++ /dev/null
@@ -1,44 +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 {
- // 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"],
-}
-
-cc_test {
- name: "libsigningutils_test",
- srcs: ["SigningUtilsTest.cpp"],
- test_suites: ["device-tests"],
- compile_multilib: "both",
- defaults: [
- "odsign_flags_defaults",
- ],
- static_libs: [
- "libsigningutils",
- ],
- shared_libs: [
- "libbase",
- "libcrypto",
- ],
- data: [
- "test_file",
- "test_file.sig",
- "SigningUtils.cert.der",
- ],
-}
diff --git a/ondevice-signing/tests/SigningUtils.cert.der b/ondevice-signing/tests/SigningUtils.cert.der
deleted file mode 100644
index 0703d596..00000000
--- a/ondevice-signing/tests/SigningUtils.cert.der
+++ /dev/null
Binary files differ
diff --git a/ondevice-signing/tests/SigningUtils.pem b/ondevice-signing/tests/SigningUtils.pem
deleted file mode 100644
index 01dfa5ed..00000000
--- a/ondevice-signing/tests/SigningUtils.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKAIBAAKCAgEAwyOUvcTpPuPxw2I9E28uhXT9pbJAgNE0dc1emOWRfapZ5iow
-6PC7am47DfHyyZ/dwtw0vvi3QWCLIpsyFQbspO5XfmJH7mHBTvG8SkV40rFjptf8
-iwMl5i1ZUc5LChxuRh6DIS1fT2tFiXQ8iX3FUNNqjj/tOOtp45JEsgFK7WDW8YU3
-UseUn2BpkccUHMbe78EkEb4F6/AJWISk2INMOMPzRPUwdE3eUPVrufkp6jVJs69I
-c6skRvAAqpiOx4rYpVzs3V5PuzhmCr7jt8hTQ0DA6q501UT06og6gIKPdqLjugKK
-jt88XNXhhofckSbIH7edwa20LhvE4vtM/x7E0u+sCJozr0uyNgRXWs6imsVrC7Hb
-cclecBUFqETVzP6RG49cCXVkuOkSNBm3HLt4291+O821gn3yygTE9bpf9uuDHSb1
-l13MOMbVeT3ipaRGMwD1bp7kBW3cEZ2zrQ4WSwwtADeWI6Z0k5SpcHfLxp+6dhPN
-JtqBmgUTHuycmvoAPZK6XMnV0wCo2gUYS2q3Vc9yqsJRG0q2CKhOtKnuEmSqcjcC
-52OnP06M5pLfvms+qoOLVUHMhidMtYs3yRPZ1ZPzScQhjg64QDsRiG+PC0lL/NFz
-XPFcgzSsoIJ8UNZNjw7l5hh9NehJam2pqMizIcECl3gwrqGtq7Qo/AIXc4sCAwEA
-AQKCAgBqkCqw+zBYvMgQ57vsugGQtdOyQcaB0j0wu6cWHf+2vWl8jLvK6XOfanTr
-Z54rRxcmS3SueUox9JPmoRPXccGXS+URyn/3iQC0qMQnVwrlHCQMP9TU4TI4Ibmu
-N9a4vc/mkNERNCLhTvZZWtWYS8uOGPYOmpBkTgK0WPMUtioBuamHmTUeCol6A3+D
-MVElaeDi0vlsivXW421nHoCbEBB2y2M03CTKzp9CXNOoao3eLZ2C94y8RdB4wKXM
-g6UtCQDIRRfAx7kIx4LKCXZ3rXjyuBDh18VLle2diilQdnv70HZF5Q9feD8Rf2c6
-PUVRKvmMgIww8Tf9GgMJ5SwmAdp/VblgcKsdjr8tnT7V9ljkYCL4K3H4FT5LKXjI
-FiGyqBie8jvLYCLx6DlVTIi+Q/kvxaFT9twazVlz9jfgufQ4ICBKlNp4A6kpwrzb
-9QnMrHI+gTOrCCEeklg90M+xk5UERueLPnXYbvAG8cv1FeVpi9ldSQds5VHN1ZJ4
-hWWeWfGgIDiNeZuKL141NLS/sX9rCGEsQyVLTKkSDIgh5ncepVcXhB40GZFfggml
-A3HfNRN5lHLwH5+JKWlVx7PfUGPOTgx62i7HF+qcV3bQJfqnAMLPA7hh1bo8xIPp
-hiAbaZkwCGlNCwadzOq6U99Dx7eorwfAgGDKWnAr/rMaK9n+OQKCAQEA8UItzBLq
-yyJ+Xj7n/M3x/+v2E6dcDYH64oeMpVEEdH6cSSNcdUm0vgX4p25W0uY5kgj1RsXV
-gOk+9W6cM84p+2+DIGI8fydnuIv8q6TFiouDCY+T3FVdj4MasNZuAeHR7c6Coc5N
-Eckv3F1sfoGlH4z2AzppY0T3VX5TkKkh719X7A/cpIirZBRLfddDA3Hr6pm/vgSo
-mX1X5BhjrujasRoZhU2RsXnAflXp18aP67zvlRlzGrpMGeueRjp2NXF3pItiMAjN
-EOSoY3elEi6Um+WomFGLsY5SAuId+TJy8SqsGKWNHN+UPx4tGYQmEPA1e76Nu5Ex
-xDxpHIWyZ4Hz/wKCAQEAzw/9bPFgihzSnrBbJiFZVGPhFxw39aeuB5/3ZMPjknfK
-fyWonbhrJF14/86JSd16Xime6J4a3OsXKzTo36sLBDsYfYXof7bwfKk/GUryMFOG
-0aZqiZbiRQ8uCfzd+MtnNxO0WxiyZvj8i7hMjK50yBhRs/5YAHSoLxFOfVVSLNAi
-nhIDqtzeLA2W05PGusgz/0w8FHI6J64jZY4EQLgr43K6DXoFLQjtsl8JZfMO9fEc
-0j5vRytXtYlTSlQEtKeQ8cvpcqbY0gmrEasZ4v1jEzemXzvFkx+ck+Ayl/vHx60A
-AZCom66BudFArAnuVdrMAWUH+78Xf2l5en7kz40QdQKCAQBWxkran+M7dQimtVGT
-qC9msWQs5YFCioHGgKKhw2Yq0G8+Dy3uMbiEsHkjH5iy+oOydu5hqj6Ew2AVvtcH
-+xs2iIFNYIgJ5A52XkNfKUCz+EIFalLwaPPh7nHnMPkYTDTJqAFsWVt3DjnctO2V
-AuR1WKoTtyq4vdGIOour+GlwQ4bILVxbAZ1Dvdj5RjegQZVtKCfDHMHXkzHNpMgV
-3ULreEu9mozQnM4ToqsdJRoW3DoAEstHzcIZgJnJALYLuughktCaHlBDxzqZrCr/
-QynIeO4O+yWXk20EBHhrbS3SeFq18rWysOgNW7k0+EcIyJ00CPHJiQuxXVkhHSVx
-/VfZAoIBAGA1isg642No/wgS41c1OZ93hRfK2cl/ruIGFtowFqZwmJs5cT5PeSD9
-eYJKggnbKcdkyVxGUi8B4NMHk4iRnd3KY5e3R49H/je+H/5tj1ibBtKU432oqNvz
-sK2dW7oFMKEru6p0MDieSiHVcWQQj1yFyDi83kDf82FjRjgAE92Um/EcZ63VUDnh
-2onWaQlSiq59ypCpfpH/XJ0MPrefm2zkWsR2RL9nHaK6e9Bt/i6SaJTbw7Kq1ecY
-tqWbolAaZ8OhvoeyNJ5rNZxRBwcsOwOr4NbxG90/W+5txrRNnccOgCk6AM3NaKNh
-Mg590sr7jby8J9h2MsHVzUb4fPJfFh0CggEBANS+aqEzWflHMOR4knhM7QHHaTfS
-4wwR3zL3/tSaV/cxNGehtjyEg85/aKklt/ude3/sm/Z0m6jQEMWgAt5iwuBovPA+
-1/rGkWTHL+dQr0i81C3b/Ymx7izL7BobaYV0o00EoKotC0NP5qR0fBKSkTfFqAYG
-SxnHtw/vduxu2H6TyIrdtvNNqc1PbHdzDI/FwzcWZFNyHBzSWwZxB5w+21uRUayv
-Iz3zcytrZZbAuOjCnhxNL/6XgcttqWSVFB4Ul1xiXrXDx2Xq+FfM40UF7oKGd+Kt
-B0wMqoZJj+0CdFfRZxHA6/n8v1Al+8lYo8smp+R9fR6qZKcugEFgdVkIl7E=
------END RSA PRIVATE KEY-----
diff --git a/ondevice-signing/tests/SigningUtilsTest.cpp b/ondevice-signing/tests/SigningUtilsTest.cpp
deleted file mode 100644
index 10f7629e..00000000
--- a/ondevice-signing/tests/SigningUtilsTest.cpp
+++ /dev/null
@@ -1,48 +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.
- */
-
-#include <android-base/file.h>
-#include <gtest/gtest.h>
-
-#include "CertUtils.h"
-#include "VerityUtils.h"
-
-// These files were created using the following commands:
-// openssl genrsa -out SigningUtils.pem 4096
-// openssl req -new -x509 -key SigningUtils.pem -out SigningUtils.cert.pem
-// openssl x509 -in SigningUtils.cert.pem -out SigningUtils.cert.der -outform DER
-// head -c 4096 </dev/urandom >test_file
-// openssl dgst -sign SigningUtils.pem -keyform PEM -sha256 -out test_file.sig -binary test_file
-const std::string kTestCert = "SigningUtils.cert.der";
-const std::string kTestFile = "test_file";
-const std::string kTestFileSignature = "test_file.sig";
-
-TEST(SigningUtilsTest, CheckVerifySignature) {
- std::string signature;
- std::string sigFile = android::base::GetExecutableDirectory() + "/" + kTestFileSignature;
- ASSERT_TRUE(android::base::ReadFileToString(sigFile, &signature));
-
- std::string data;
- std::string testFile = android::base::GetExecutableDirectory() + "/" + kTestFile;
- ASSERT_TRUE(android::base::ReadFileToString(testFile, &data));
-
- std::string testCert = android::base::GetExecutableDirectory() + "/" + kTestCert;
- auto trustedKey = extractPublicKeyFromX509(testCert.c_str());
- ASSERT_TRUE(trustedKey.ok());
-
- auto result = verifySignature(data, signature, *trustedKey);
- ASSERT_TRUE(result.ok());
-}
diff --git a/ondevice-signing/tests/test_file b/ondevice-signing/tests/test_file
deleted file mode 100644
index 8a121bea..00000000
--- a/ondevice-signing/tests/test_file
+++ /dev/null
Binary files differ
diff --git a/ondevice-signing/tests/test_file.sig b/ondevice-signing/tests/test_file.sig
deleted file mode 100644
index ffd95dcd..00000000
--- a/ondevice-signing/tests/test_file.sig
+++ /dev/null
Binary files differ
diff --git a/provisioner/Android.bp b/provisioner/Android.bp
index 665a9e71..afbc4055 100644
--- a/provisioner/Android.bp
+++ b/provisioner/Android.bp
@@ -47,10 +47,8 @@ cc_binary {
name: "rkp_factory_extraction_tool",
vendor: true,
srcs: ["rkp_factory_extraction_tool.cpp"],
- defaults: [
- "keymint_use_latest_hal_aidl_ndk_shared",
- ],
shared_libs: [
+ "android.hardware.security.keymint-V1-ndk_platform",
"libbinder",
"libbinder_ndk",
"libcrypto",
diff --git a/provisioner/rkp_factory_extraction_tool.cpp b/provisioner/rkp_factory_extraction_tool.cpp
index 0f455310..c439b990 100644
--- a/provisioner/rkp_factory_extraction_tool.cpp
+++ b/provisioner/rkp_factory_extraction_tool.cpp
@@ -22,7 +22,6 @@
#include <cppbor.h>
#include <gflags/gflags.h>
#include <keymaster/cppcose/cppcose.h>
-#include <openssl/base64.h>
#include <remote_prov/remote_prov_utils.h>
#include <sys/random.h>
@@ -30,7 +29,6 @@ using aidl::android::hardware::security::keymint::DeviceInfo;
using aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent;
using aidl::android::hardware::security::keymint::MacedPublicKey;
using aidl::android::hardware::security::keymint::ProtectedData;
-using aidl::android::hardware::security::keymint::RpcHardwareInfo;
using aidl::android::hardware::security::keymint::remote_prov::generateEekChain;
using aidl::android::hardware::security::keymint::remote_prov::getProdEekChain;
using aidl::android::hardware::security::keymint::remote_prov::jsonEncodeCsrWithBuild;
@@ -51,26 +49,6 @@ constexpr std::string_view kBuildPlusCsr = "build+csr"; // Text-encoded (JSON)
constexpr size_t kChallengeSize = 16;
-std::string toBase64(const std::vector<uint8_t>& buffer) {
- size_t base64Length;
- int rc = EVP_EncodedLength(&base64Length, buffer.size());
- if (!rc) {
- std::cerr << "Error getting base64 length. Size overflow?" << std::endl;
- exit(-1);
- }
-
- std::string base64(base64Length, ' ');
- rc = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(base64.data()), buffer.data(), buffer.size());
- ++rc; // Account for NUL, which BoringSSL does not for some reason.
- if (rc != base64Length) {
- std::cerr << "Error writing base64. Expected " << base64Length
- << " bytes to be written, but " << rc << " bytes were actually written."
- << std::endl;
- exit(-1);
- }
- return base64;
-}
-
std::vector<uint8_t> generateChallenge() {
std::vector<uint8_t> challenge(kChallengeSize);
@@ -78,13 +56,9 @@ std::vector<uint8_t> generateChallenge() {
uint8_t* writePtr = challenge.data();
while (bytesRemaining > 0) {
int bytesRead = getrandom(writePtr, bytesRemaining, /*flags=*/0);
- if (bytesRead < 0) {
- if (errno == EINTR) {
- continue;
- } else {
- std::cerr << errno << ": " << strerror(errno) << std::endl;
- exit(-1);
- }
+ if (bytesRead < 0 && errno != EINTR) {
+ std::cerr << errno << ": " << strerror(errno) << std::endl;
+ exit(-1);
}
bytesRemaining -= bytesRead;
writePtr += bytesRead;
@@ -114,30 +88,27 @@ Array composeCertificateRequest(const ProtectedData& protectedData,
return certificateRequest;
}
-std::vector<uint8_t> getEekChain(uint32_t curve) {
+std::vector<uint8_t> getEekChain() {
if (FLAGS_test_mode) {
const std::vector<uint8_t> kFakeEekId = {'f', 'a', 'k', 'e', 0};
- auto eekOrErr = generateEekChain(curve, 3 /* chainlength */, kFakeEekId);
+ auto eekOrErr = generateEekChain(3 /* chainlength */, kFakeEekId);
if (!eekOrErr) {
std::cerr << "Failed to generate test EEK somehow: " << eekOrErr.message() << std::endl;
exit(-1);
}
- auto [eek, pubkey, privkey] = eekOrErr.moveValue();
- std::cout << "EEK raw keypair:" << std::endl;
- std::cout << " pub: " << toBase64(pubkey) << std::endl;
- std::cout << " priv: " << toBase64(privkey) << std::endl;
+ auto [eek, ignored_pubkey, ignored_privkey] = eekOrErr.moveValue();
return eek;
}
- return getProdEekChain(curve);
+ return getProdEekChain();
}
-void writeOutput(const std::string instance_name, const Array& csr) {
+void writeOutput(const Array& csr) {
if (FLAGS_output_format == kBinaryCsrOutput) {
auto bytes = csr.encode();
std::copy(bytes.begin(), bytes.end(), std::ostream_iterator<char>(std::cout));
} else if (FLAGS_output_format == kBuildPlusCsr) {
- auto [json, error] = jsonEncodeCsrWithBuild(instance_name, csr);
+ auto [json, error] = jsonEncodeCsrWithBuild(csr);
if (!error.empty()) {
std::cerr << "Error JSON encoding the output: " << error;
exit(1);
@@ -163,23 +134,16 @@ void getCsrForInstance(const char* name, void* /*context*/) {
auto rkp_service = IRemotelyProvisionedComponent::fromBinder(rkp_binder);
if (!rkp_service) {
std::cerr << "Unable to get binder object for '" << fullName << "', skipping.";
- exit(-1);
+ return;
}
std::vector<uint8_t> keysToSignMac;
std::vector<MacedPublicKey> emptyKeys;
DeviceInfo verifiedDeviceInfo;
ProtectedData protectedData;
- RpcHardwareInfo hwInfo;
- ::ndk::ScopedAStatus status = rkp_service->getHardwareInfo(&hwInfo);
- if (!status.isOk()) {
- std::cerr << "Failed to get hardware info for '" << fullName
- << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
- exit(-1);
- }
- status = rkp_service->generateCertificateRequest(
- FLAGS_test_mode, emptyKeys, getEekChain(hwInfo.supportedEekCurve), challenge,
- &verifiedDeviceInfo, &protectedData, &keysToSignMac);
+ ::ndk::ScopedAStatus status = rkp_service->generateCertificateRequest(
+ FLAGS_test_mode, emptyKeys, getEekChain(), challenge, &verifiedDeviceInfo, &protectedData,
+ &keysToSignMac);
if (!status.isOk()) {
std::cerr << "Bundle extraction failed for '" << fullName
<< "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
@@ -187,7 +151,7 @@ void getCsrForInstance(const char* name, void* /*context*/) {
}
auto request =
composeCertificateRequest(protectedData, verifiedDeviceInfo, challenge, keysToSignMac);
- writeOutput(std::string(name), request);
+ writeOutput(request);
}
} // namespace